┗ APIs를 이용한 Token 발급 및 전송 시나리오

Print

0. 사전 준비

* Access Token 발급

OKTA-URL : OKTA 인증 서버 접속 URL

OKTA-USER-ID : OKTA 서버에 등록되어 있는 로그인 ID

OKTA-USER-PW : OKTA 서버에 등록되어 있는 로그인 비밀번호

OKTA-CLIENT-ID : OKTA 서버에서 Token service에 발급된 CLIENT ID

OKTA-CLIENT-SECRET : OKTA 서버에서 Token service에 발급된 CLIENT SECRET

Request

curl --request POST \
  --url https://{{OKTA-URL}}/oauth2/default/v1/token \
  --header 'accept: application/json' \
  --header 'cache-control: no-cache' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'grant_type=password&username={{OKTA-USER-ID}}&password={{OKTA-USER-PW}}&client_id={{OKTA-CLIENT-ID}}&client_secret={{OKTA-CLIENT-SECRET}}&scope=openid'

Response

{
    "token_type": "Bearer",
    "expires_in": 3600,
    "access_token": "{{ACCESS-TOKEN}}",
    "scope": "openid",
    "id_token": "{{ID-TOKEN}}"
}

Response body에서 access_token 값인 {{ACCESS-TOKEN}}을 아래 시나리오에서 사용합니다.

1. Token 관리자 선정

Token을 발급하기 위한 관리자를 선정합니다. 기존 등록되어 있는 token 관리자이거나 새로운 token 관리자를 등록하여 진행할 수 있습니다.

1.1. Token 관리자 목록 조회

Request

curl -X 'GET' \
  'http://localhost:3000/user/info' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json'
  -H 'Authorization: Bearer {{ACCESS-TOKEN}}'

Response

{
    "total": 18,
    "skip": 0,
    "take": 2,
    "contents": [
        {
            "userId": "admin1",
            "userName": "Admin Kim",
            "organization": "Team",
            "status": "ACTIVATED",
            "createdAt": "2021-09-06T09:46:26.000Z"
        },
        {
            "userId": "admin6",
            "userName": "Super Lee",
            "organization": "Team",
            "status": "ACTIVATED",
            "createdAt": "2021-09-06T09:28:57.000Z"
        }
    ]
}

단순히 userId가 Token 관리자로 등록되어 있는지 확인하는 API로 없는 경우 '1.2. Token 관리자 생성'을 수행합니다.

1.2. Token 관리자 생성

[1.1. Token 관리자 목록 조회] 결과가 존재하지 않을 경우

Request

curl -X 'POST' \
  'http://localhost:3000/user/info' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer {{ACCESS-TOKEN}}'
  -d '{
  "userId": "gildong.hong",
  "userName": "홍길동",
  "organization": "홍보팀",
  "userRoles": [ "TOKEN_ADMIN" ]
}'

Request body 중 userRoles의 ‘TOKEN_ADMIN’은 token 관리자 권한을 부여하는 것이기 때문에 반드시 포함되어야 한니다.

Response

{
    "userId": "gildong.hong",
    "userName": "홍길동",
    "organization": "홍보팀",
    "status": "ACTIVATED",
    "createdAt": "2021-09-07T05:04:50.000Z",
    "createdBy": null,
    "userEoas": [
        {
            "ownedAddress": "0xfa0D8d66c557c28E6FF0d26799Be306dEA6F97d1",
            "userId": "gildong.hong",
            "tempId": "gildong.hong",
            "alias": "사용자 등록에 의한 자동 생성",
            "createdAt": "2021-09-07T05:04:50.000Z"
        }
    ],
    "userRoles": [
        {
            "userId": "gildong.hong",
            "role": "TOKEN_ADMIN"
        }
    ]
}
  • 점검 포인트

    • userId가 입력한 userId와 동일한가?

    • userEoas에 포함되어 있는 ownedAddress가 ‘0x’로 시작하는 42자리 hexa string인가?

    • userRolesTOKEN_ADMIN이 포함되어 있는가?

2. 토큰 생성

2.1. 기 발급된 Token 목록 조회

Request

curl -X 'GET' \
  'http://localhost:3000/token/admin' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {{ACCESS-TOKEN}}'

Response

{
    "total": 1,
    "skip": 0,
    "take": 10,
    "contents": [
        {
            "tokenSeq": 1,
            "tokenName": "테스트토큰1",
            "tokenSymbol": "TEST1",
            "tokenInitialSupply": 0,
            "tokenInitialHolderId": "",
            "tokenTotalSupply": 0,
            "tokenDescription": "테스트토큰1 설명",
            "tokenStatus": "ACTIVATED",
            "tokenContractSeq": 1,
            "tokenContractAddress": "0x16Df26f69e762FE5FA651d67EfdA76Ef79B98228",
            "createdAt": "2021-09-04T11:17:24.000Z",
            "createdBy": null,
            "updatedAt": "2021-09-04T11:17:24.000Z",
            "updatedBy": null
        }
    ]
}
  • 점검 포인트

    • 생성한 token이 존재하는지 확인합니다.

    • Token의 상태(tokenStatus)가 ACTIVATED인지 확인한다. Token을 이더리움 메인넷에 발급하는데 많은 시간이 소요되기 때문에 초기 PENDING 상태에서 발급이 완료되면 ACTIVATED 상태로 변경됩니다.

    • PENDING 상태에서 token에 대한 서비스 요청을 하는 경우 정상적으로 동작되지 않는다.

  • 중요 사항

    • Token 정보 중 tokenSeq는 token service API를 이용하는 key 정보이기 때문에 연계되어 사용되어야 합니다.

2.2. 신규 Token 발급

Request

curl -X 'POST' \
  'http://localhost:3000/token/tx/init' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "tokenAdminId": "gildong.hong",
  "tokenName": "첫번째토큰",
  "tokenSymbol": "FSTTK",
  "tokenInitialSupply": 1000
}'

tokenSymbol은 UNIQUE한 값이 사용되어야 하며, tokenInitialSupply 초기 발행량으로 초기 0으로 설정한 후 추가 발행 API를 이용하여 추가 발행할 수 있습니다.

Response

{
  "tokenName": "첫번째토큰",
  "tokenSymbol": "FSTTK",
  "tokenInitialSupply": 1000,
  "tokenInitialHolderId": "gildong.hong",
  "comment": null,
  "opCode": "INIT",
  "opBy": "gildong.hong",
  "tokenDescription": null,
  "tokenSeq": null,
  "tokenContractSeq": null,
  "tokenContractAddress": null,
  "opResult": null,
  "opDetail": null,
  "historySeq": "57",
  "opState": "PENDING",
  "opAt": "2021-09-06T02:59:49.504Z",
  "updatedAt": "2021-09-06T02:59:49.504Z"
}
  • 점검 사항

    • 요청시 입력된 값대로 생성되었는지 확인

  • 중요 사항

    • opStatePENDING인 상태에서는 token 서비스 이용이 불가합니다. Token 목록 조회 또는 상세 조회를 통해 tokenSymbol에 해당하는 token의 상태를 확인 후 서비스를 이용합니다. (2.1. 기 발급된 Token 목록 조회 참조)

3. 토큰 발행

3.1. Token에 가입되어 있는 사용자 목록 조회

특정 token에 가입되어 있는 token 사용자 목록을 조회하는 API로 단순히 token 사용자로 등록되어 있는(해당 token에 가입되어 있지 않은) 사용자는 조회되지 않습니다.

Request

curl -X 'GET' \
  'http://localhost:3000/token/admin/1/member' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {{ACCESS-TOKEN}}'

요청 URL의 /token/admin/1/member 1은 path variable로 tokenSeq 이며 ‘2.1. 기 발급된 Token 목록 조회’등의 API를 이용하여 대상 token의 tokenSeq값을 확인하여 요청합니다.

Response

[
  {
    "tokenSeq": 1,
    "tokenUserId": "gildong.hong",
    "isAdmin": true,
    "status": "ACTIVATED",
    "createdAt": "2021-09-04T11:17:24.000Z",
    "createdBy": "gildong.hong",
    "updatedAt": null,
    "updatedBy": null,
    "tokenUser": {
      "tokenUserId": "gildong.hong",
      "tokenUserName": "홍길동",
      "tokenUserOrganization": "홍보팀",
      "tokenUserOwnedAddress": "0xfa0D8d66c557c28E6FF0d26799Be306dEA6F97d1",
      "tokenUserStatus": "ACTIVE",
      "createdAt": "2021-09-04T11:17:24.000Z",
      "createdBy": "gildong.hong",
      "updatedAt": null,
      "updatedBy": null
    }
  },
  {
    "tokenSeq": 1,
    "tokenUserId": "muge.ah",
    "isAdmin": false,
    "status": "ACTIVATED",
    "createdAt": "2021-09-06T23:06:15.000Z",
    "createdBy": "muge.ah",
    "updatedAt": null,
    "updatedBy": null,
    "tokenUser": {
      "tokenUserId": "muge.ah",
      "tokenUserName": "아무개",
      "tokenUserOrganization": "홍보팀",
      "tokenUserOwnedAddress": "0x785ac7B77eFe4C561174Fd4Ea3F4372f1630083e",
      "tokenUserStatus": "ACTIVE",
      "createdAt": "2021-09-06T21:58:26.000Z",
      "createdBy": null,
      "updatedAt": "2021-09-07T08:06:12.000Z",
      "updatedBy": null
    }
  }
]

3.2. 신규 Token 사용자 등록

이미 등록되어 있는 token 사용자일 경우 특정 token에 가입 절차만 수행하면 됩니다.

Request

curl -X 'PUT' \
  'http://localhost:3000/token/user/info' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "tokenUserId": "taewoong.seo",
  "tokenUserName": "서태웅",
  "tokenUserOrganization": "홍보팀",
  "tokenUserOwnedAddress": "0xDB8396d6F9fd03AAe058F90BDFeE4AeB972034c7"
}'

Token 사용자는 token 관리자와 달리 본인의 ownedAddress는 이미 생성되어 있어야 하며, Private Key 정보 또한 본인이 관리하고 있어야 합니다.

Response

{
  "tokenUserId": "taewoong.seo",
  "tokenUserName": "서태웅",
  "tokenUserOrganization": "홍보팀",
  "tokenUserOwnedAddress": "0xDB8396d6F9fd03AAe058F90BDFeE4AeB972034c7",
  "createdBy": null,
  "updatedAt": null,
  "updatedBy": null,
  "tokenUserStatus": "ACTIVE",
  "createdAt": "2021-09-06T21:58:26.000Z"
}

3.3. 신규 Token 사용자 Token 가입

Request

curl -X 'PUT' \
  'http://localhost:3000/token/admin/1/member/taewoong.seo?isAdmin=false' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "tokenUserId": "taewoong.seo",
  "tokenUserName": "서태웅",
  "tokenUserOrganization": "홍보팀",
  "tokenUserOwnedAddress": "0xDB8396d6F9fd03AAe058F90BDFeE4AeB972034c7"
}'

요청 URL /token/admin/1/member/taewoong.seo?isAdmin=false의 1은 path variable로 tokenSeq 이며 taewoong.seo은 tokenUserId이다. (API 설명 참조)

Response

{
  "tokenSeq": 1,
  "tokenUserId": "taewoong.seo",
  "isAdmin": false,
  "status": "ACTIVATED",
  "createdAt": "2021-09-07T02:32:52.000Z",
  "createdBy": "taewoong.seo",
  "updatedAt": null,
  "updatedBy": null,
  "token": {
    "tokenSeq": 1,
    "tokenName": "첫번째토큰",
    "tokenSymbol": "FSTTK",
    "tokenInitialSupply": 1000,
    "tokenInitialHolderId": "gildong.hong",
    "tokenTotalSupply": 1000,
    "tokenDescription": null,
    "tokenStatus": "ACTIVATED",
    "tokenContractSeq": 1,
    "tokenContractAddress": "0x16Df26f69e762FE5FA651d67EfdA76Ef79B98228",
    "createdAt": "2021-09-04T11:17:24.000Z",
    "createdBy": null,
    "updatedAt": "2021-09-07T06:42:45.648Z",
    "updatedBy": ""
  },
  "tokenUser": {
    "tokenUserId": "taewoong.seo",
    "tokenUserName": "서태웅",
    "tokenUserOrganization": "홍보팀",
    "tokenUserOwnedAddress": "0xDB8396d6F9fd03AAe058F90BDFeE4AeB972034c7",
    "tokenUserStatus": "ACTIVE",
    "createdAt": "2021-09-06T21:58:26.000Z",
    "createdBy": null,
    "updatedAt": "2021-09-07T11:32:52.000Z",
    "updatedBy": null
  }
}

3.4. Token 사용자에게 토큰 발행 (token 관리자 → token 사용자)

Request

curl -X 'POST' \
  'http://localhost:3000/token/tx/transfer' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer {{ACCESS-TOKEN}}' \
  -d '{
  "tokenSeq": 1,
  "amount": 10,
  "senderId": "gildong.hong",
  "receiverId": "taewoong.seo",
  "comment": "초기 발행"
}'

Response

{
  "tokenSeq": 1,
  "senderId": "gildong.hong",
  "senderAddress": "0xfa0D8d66c557c28E6FF0d26799Be306dEA6F97d1",
  "senderIsAdmin": true,
  "recipientId": "taewoong.seo",
  "recipientAddress": "0xDB8396d6F9fd03AAe058F90BDFeE4AeB972034c7",
  "recipientIsAdmin": false,
  "amount": 1,
  "transferCode": "TRANSFER",
  "transferState": "PENDING",
  "transferResult": null,
  "historySeq": "7",
  "opAt": "2021-09-06T03:28:10.351Z",
  "updatedAt": "2021-09-06T03:28:10.351Z"
}

3.5. Token 사용자간 토큰 전송 (token 사용자 → token 사용자 또는 token 관리자)

3.5.1. transaction object 생성 요청

Request

curl -X 'POST' \
  'http://localhost:3000/token/tx/raw/object/transfer' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer {{ACCESS-TOKEN}}' \
  -d '{
  "tokenSeq": 1,
  "senderId": "taewoong.seo",
  "receiverId": "gildong.hong",
  "amount": 10
}'

Response

{
  "from": "0x015786A259035a7ff0BDD8A77AAC25f6C3A5FF73",
  "to": "0xAC50aeaAb151b78F310c1CA40bFeC1F09a7EC248",
  "data": "0xa9059cbb000000000000000000000000ee188c31b163ad11a7c180fb4f0342d2e2575a530000000000000000000000000000000000000000000000000000000000000001",
  "gas": 6721975,
  "gasPrice": 0,
  "nonce": 46
}

3.5.2. signed transaction 수행(전송) 요청

Request

curl -X 'POST' \
  'http://localhost:3000/token/tx/signed' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer {{ACCESS-TOKEN}}' \
  -d '{
  "serializedSignedTx": "0xf8a50380836691b794ac50aeaab151b78f310c1ca40bfec1f09a7ec24880b844a9059cbb000000000000000000000000015786a259035a7ff0bdd8a77aac25f6c3a5ff7300000000000000000000000000000000000000000000000000000000000000011ca064453e7bae68f3132b59da52f168100ab437d2679e081155be324980bb50e6a7a042feabef392d5f16930a93bf6a660f1eb5dfed951beb3e8c3592f0d3e3ec8cc9"
}'

'3.5.1. transaction object 생성 요청' 응답 전문을 개인의 private key로 sign하여 요청합니다.

Response

{
  "blockHash": "0x3a2208e5a439a93a5a8b5d9f81ee454c8551ef0a62ec018d7fcab33c062c8ffb",
  "blockNumber": 14773476,
  "contractAddress": null,
  "cumulativeGasUsed": 23581,
  "from": "0x46a84c5c22c49ee8c0e0231bc41a96ef8dd80cba",
  "gasUsed": 23581,
  "logs": [
    {
      "address": "0xAC50aeaAb151b78F310c1CA40bFeC1F09a7EC248",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x00000000000000000000000046a84c5c22c49ee8c0e0231bc41a96ef8dd80cba",
        "0x000000000000000000000000015786a259035a7ff0bdd8a77aac25f6c3a5ff73"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000000000001",
      "blockNumber": 14773476,
      "transactionHash": "0x0fa0d3dea37e8b27e9f93be3cea3473974802579f8d080ef9926ad7e39438f00",
      "transactionIndex": 0,
      "blockHash": "0x3a2208e5a439a93a5a8b5d9f81ee454c8551ef0a62ec018d7fcab33c062c8ffb",
      "logIndex": 0,
      "removed": false,
      "id": "log_02ba7fb3"
    }
  ],
  "logsBloom": "0x00000000000000000000000000400000000000000000000000000000000000000000000000400000010000000000000000000004000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000010010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000",
  "status": true,
  "to": "0xac50aeaab151b78f310c1ca40bfec1f09a7ec248",
  "transactionHash": "0x0fa0d3dea37e8b27e9f93be3cea3473974802579f8d080ef9926ad7e39438f00",
  "transactionIndex": 0
}

3.5.3. signed transaction 수행(전송) 결과 조회

Request

curl -X 'GET' \
  'http://localhost:3000/token/tx/signed/receipt/0x0fa0d3dea37e8b27e9f93be3cea3473974802579f8d080ef9926ad7e39438f00' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {{ACCESS-TOKEN}}'

요청 URL /token/tx/signed/receipt/0x0fa0d3dea37e8b27e9f93be3cea3473974802579f8d080ef9926ad7e39438f000x0fa0d3dea37e8b27e9f93be3cea3473974802579f8d080ef9926ad7e39438f00은 ‘3.5.2. signed transaction 수행(전송) 요청’의 결과 중 transactionHash 값입니다.

Response

{
  "blockHash": "0x3a2208e5a439a93a5a8b5d9f81ee454c8551ef0a62ec018d7fcab33c062c8ffb",
  "blockNumber": 14773476,
  "contractAddress": null,
  "cumulativeGasUsed": 23581,
  "from": "0x46a84c5c22c49ee8c0e0231bc41a96ef8dd80cba",
  "gasUsed": 23581,
  "logs": [
    {
      "address": "0xAC50aeaAb151b78F310c1CA40bFeC1F09a7EC248",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x00000000000000000000000046a84c5c22c49ee8c0e0231bc41a96ef8dd80cba",
        "0x000000000000000000000000015786a259035a7ff0bdd8a77aac25f6c3a5ff73"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000000000001",
      "blockNumber": 14773476,
      "transactionHash": "0x0fa0d3dea37e8b27e9f93be3cea3473974802579f8d080ef9926ad7e39438f00",
      "transactionIndex": 0,
      "blockHash": "0x3a2208e5a439a93a5a8b5d9f81ee454c8551ef0a62ec018d7fcab33c062c8ffb",
      "logIndex": 0,
      "removed": false,
      "id": "log_02ba7fb3"
    }
  ],
  "logsBloom": "0x00000000000000000000000000400000000000000000000000000000000000000000000000400000010000000000000000000004000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000010010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000",
  "status": true,
  "to": "0xac50aeaab151b78f310c1ca40bfec1f09a7ec248",
  "transactionHash": "0x0fa0d3dea37e8b27e9f93be3cea3473974802579f8d080ef9926ad7e39438f00",
  "transactionIndex": 0
}
  • 점검 사항

    • status가 true인가?를 확인합니다.

이 답변이 유용합니까? 아니오

Send feedback
도움을 드리지 못해 죄송합니다. 피드백을 주시면 이 문서의 품질을 높이겠습니다.