End point APIs
APIs
프로젝트 설정에 필요한 End point API 에 대한 설명 입니다.
접속 링크
Webview(모바일), 시스템 브라우저(데스크탑) 환경에서 RAMP F/E 로드하기 위한 URL에 대한 설명 입니다.
프로젝트 생성시 projectID 를 포함한 램프 접근 URL이 생성 됩니다.
https://ramp.crosstoken.io/catalog?projectId={{projectID}}&sessionId={{sessionId}}&accessToken={{accessToken}}&platform=web&network=testnet
파라미터 설명
파라미터명 | 설명 | 필수여부 |
|---|---|---|
projectId | 넥써쓰에서 발급하는 Project ID | 필수 |
sessionId | 유저의 게임 캐릭터 식별 ID | 필수 |
accessToken | 인게임 유저 인증 토큰 RAMP B/E 에서 개발사 서버로 게임 재화 조회 요청시 사용됩니다. | 필수 |
network | Mainnet, Testnet 구분 | 필수 |
lang | 언어 선택 (zh, en, zh-Hant) | 선택 |
platform | web | 선택 |
유저의 게임 재화 조회 API
토큰 발행을 위해 유저가 보유한 게임 재화를 조회하는 API 구현이 필요 합니다. 조회된 게임 재화는 RAMP F/E 페이지에 표현됩니다.
RAMP B/E에서 게임 재화 조회 API를 호출할 때는, 게임 내에서 토큰 발행 또는 소각을 위해 로드되는 RAMP F/E URL의 쿼리 파라미터에 포함된 accessToken 및 sessionId 값을 Request Header에 함께 포함하여 전송합니다.
따라서 개발사 서버에서는 해당 accessToken을 검증하고, 검증 결과에 따른 적절한 응답(Response) 을 반환해야 합니다.
CROSS-RAMP Console 프로젝트에 설정한 Get Assets End point 로 요청을 하게 됩니다.
Request / Response
RAMP B/E 에서 개발사가 구현한 End
Request 샘플
GET /api/assets
Host: https://your-server.com
Content-Type: application/json
X-Dapp-Authorization: Bearer {{accessToken}}
X-Dapp-SessionID: {{sessionId}}Request Header 필드 설명
필드명 | 설명 |
|---|---|
X-Dapp-Authorization | 개발사 서버에서 생성한 accessToken 입니다.
RAMP F/E 로드시 Query 에 추가한 |
X-Dapp-SessionID | 개발사에서 관리하는 유저의 캐릭터 식별 ID 입니다.
RAMP F/E 로드시 Query 에 추가한 |
Response 샘플
성공 응답 (200 OK)
{
"success": true,
"errorCode": null,
"data": {
"v1": {
"player_id": "player_id_allen",
"name": "name_allen",
"wallet_address": "0x62c5a30a90d3c3032dfb0fe4ea05e9c454c56707",
"server": "test",
"assets": [
{
"id": "item_gold",
"balance": "1000.123"
}
]
}
}
}Response 필드 설명
필드명 | 타입 | 설명 |
|---|---|---|
success | boolean | 요청 성공 여부 |
errorCode | string | 오류 코드 (성공시 null) |
data | object | 응답 데이터 (실패시 null) |
data.v1 | object | API 버전 1 데이터 |
data.v1.player_id | string | 유저 캐릭터 고유 ID |
data.v1.name | string | 유저 캐릭터 이름 |
data.v1.wallet_address | string | 유저 지갑 주소 |
data.v1.server | string | 유저 캐릭터 접속 서버 정보 |
data.v1.assets | array | 유저의 게임 재화 보유 목록 |
data.v1.assets[].id | string |
|
data.v1.assets[].balance | string |
|
유저의 서명 검증 조회 API
토큰 발행 과정에서, 유저의 서명(Signature) 값과 개발사에서 전송한 트랜잭션 요청 데이터가 일치하는지 검증하는 API 구현이 필요합니다.
데이터 무결성 검증을 위해 CROSS-RAMP Console 에서 생성된 HMAC-SHA256 으로 서명한 데이터를 Request Header 에 포함하여 요청을 합니다.
따라서 개발사 서버에서는 CROSS-RAMP Console 의 HMAC 값을 이용하여 무결성 검증을 해야 합니다.
유저의 토큰 발행 / 소각 요청이 개발사 정책에 위배 되는지 검증 후(인증, 게임 재화 수량 등), 응답이 필요 합니다.
CROSS-RAMP Console 프로젝트에 설정한 Validate Order End point 로 요청을 하게 됩니다.
Request / Response
Request 샘플
POST /api/validate
Host: https://your-server.com
Content-Type: application/json
x-dapp-authorization: Bearer {{accessToken}}
x-dapp-sessionid: {{sessionId}}
x-hmac-signature: {{hmac_signature}}
{
"user_sig": "0x58ea88cc20a571d2bc4f4a7ab687158e1924887c005a8a2ccce9a7c8f669adbb222932f9e760b923b6f359870169d58a171d47516ee71167313d5068dbd84c631c",
"user_address": "0x6de346a7333d97fe0d39a49178ac65918c257b28",
"project_id": "3a4----------------------2d7",
"digest": "0x6d196d0881bb8e322c194fbf53518089b240055134044491a78b14920098e395",
"uuid": "86b555dd-e622-43fe-a799-c5c4536dd8c6",
"intent": {
"method": "mint",
"type": "assemble",
"from": [
{
"type": "asset",
"id": "item_gold",
"amount": 100
}
],
"to": [
{
"type": "ERC20",
"id": "0x14f6f0057274c3519d6258EB66F5d01D79821D81",
"amount": 1
}
],
"target_candidate": {}
}
}POST /api/validate
Host: https://your-server.com
Content-Type: application/json
x-dapp-authorization: Bearer {{accessToken}}
x-dapp-sessionid: {{sessionId}}
x-hmac-signature: {{hmac_signature}}
{
"user_sig": "0xb1378a978b5e77d750c44d4b9bdf4d883d2e2bad8e09c8928e8d83176359cc9376a959c3576c82f2214c9fece66c73417669bf667734ec8f94880885c5d1b82a1c",
"user_address": "0x6de346a7333d97fe0d39a49178ac65918c257b28",
"project_id": "3a4f5838f7cdfe31873a43ca021a92d7",
"digest": "0x7bd721630a8c7e6b1c1050934fc3bf69cadaef0253c46f92f1b03242c5f2e733",
"uuid": "d7360515-8547-427e-acb5-6556c8376fd4",
"intent": {
"method": "burn-permit",
"type": "disassemble",
"from": [
{
"type": "ERC20",
"id": "0x14f6f0057274c3519d6258EB66F5d01D79821D81",
"amount": 1
}
],
"to": [
{
"type": "asset",
"id": "item_gold",
"amount": 50
}
],
"target_candidate": {}
}
}Request Header 필드 설명
필드명 | 설명 |
|---|---|
X-Dapp-Authorization | 개발사 서버에서 생성한 accessToken 입니다.
RAMP F/E 로드시 Query 에 추가한 |
X-Dapp-SessionID | 개발사에서 관리하는 유저의 캐릭터 식별 ID 입니다.
RAMP F/E 로드시 Query 에 추가한 |
Request Body 필드 설명
필드명 | 타입 | 설명 |
|---|---|---|
user_sig | string | 유저의 토큰 발행 / 소각을 위해서 CROSSx 를 통해 서명한 데이터. |
user_address | string | 유저의 CROSSx 주소 |
project_id | string | RAMP 콘솔에서 생성한 프로젝트 ID |
digest | string | 트랜잭션 데이터의 해시 다이제스트 개발사에서 생성한 Validator 키를 이용해서 서명할 원문 입니다. |
uuid | string | 요청 고유 식별자 |
intent | object | 토큰 발행 / 소각 정보 |
intent.method | string | 실행 방법 (mint, burn) |
intent.type | string | 트랜잭션 타입 (assemble, disassemble)
|
intent.from | array | 소스 자산 정보 목록 |
intent.from[].type | string | 자산 타입 (asset큰, ERC20)
|
intent.from[].id | string | CROSS-RAMP Console 에 등록한 게임 재화 ID. |
intent.from[].amount | number | 토큰 발행에 사용된 게임 재화 수량 |
intent.to | array | 대상 자산 정보 목록 |
intent.to[].type | string | 자산 타입 (ERC20, NFT) |
intent.to[].id | string | 토큰 컨트랙트 주소 |
intent.to[].amount | number | 발행할 토큰 수량 |
intent.target_candidate | object | 대상 후보 정보 (추가 옵션) |
Response 샘플
성공 응답 (200 OK)
{
"success": true,
"errorCode": null,
"data": {
"userSig": "0x58ea88cc20a571d2bc4f4a7ab687158e1924887c005a8a2ccce9a7c8f669adbb222932f9e760b923b6f359870169d58a171d47516ee71167313d5068dbd84c631c",
"validatorSig": "0xfd7c12023378170c615bdd63be3e7aa195ff98b42fe84dad34348017fc1050db157e077dd5053328040b476479edebfe5d773bb8602de6bc088951de7b597fd31b"
}
}Response 필드 설명
필드명 | 타입 | 설명 |
|---|---|---|
success | boolean | 요청 성공 여부 |
errorCode | string | 오류 코드 (성공시 null) |
data | object | 응답 데이터 (실패시 null) |
data.userSig | string | 토큰 발행 / 소각을 위해 유저가 CROSSx 를 통해서 서명한 데이터 입니다. |
data.validatorSig | string | 개발사에서 생성한 Validator 키를 이용해서 ECDSA 서명한 데이터 입니다. |
유저의 토큰 발행 / 소각 결과 API
유저의 토큰 발행 / 소각 요청에 대한 블록체인상 트랜잭션 결과를 게임 서버로 Webhook 으로 전달 합니다.
CROSS-RAMP Console 에 등록된 End point 로 트랜잭션 결과를 전달하며, 개발사 서버에서는 결과 수신 후 Http 상태 값을 200 으로 응답 해야 합니다.
RAMP B/E 에서 응답을 받지 못하거나 HTTP 500 코드를 수신하는 경우 RAMP B/E 에서 재전송을 하게됩니다.
정상적인 처리를 위해서 HTTP 200 코드를 응답해야 합니다.
재시도 정보
Webhook 전달 첫번째 시도 후 12시간 동안 최대 20회까지 재전송을 시도 합니다.
- 5분 간격 2회 시도
- 15분 간격으로 7번 시도
- 60분 간격으로 10번 시도
Webhook request / response
receipt.status 가 0x1 이 아닌 경우에는 블록체인 네트워크상 트랜잭션 요청 실패 이므로 게임 재화를 복구해야 합니다.
Request 샘플
POST /api/result
Host: https://your-server.com
Content-Type: application/json
x-dapp-authorization: Bearer {{accessToken}}
x-dapp-sessionid: {{sessionId}}
x-hmac-signature: {{hmac_signature}}
{
"session_id": "{{dappSessionId}}",
"uuid": "b6d3976b-36b1-48d6-974b-da1471aa94de",
"tx_hash": "0x17def972330f874dcfbc8099ada5da777c4273e3c0aa6faebb103e09615f5784",
"receipt": {
"type": "0x2",
"root": "0x",
"status": "0x1",
"cumulativeGasUsed": "0x1f531",
"logsBloom": "0x00000000080040000000200a00000000000000000000000000000000000000000000010000000000002000100000000000000000000000000000000000000000000000100820000400000008000000000000000000000000000000000000110000040000020000000000000000000800000040000000000000000010000000000000000008000000000000080000000000000000000000000000000000100000000000000000000000000400000002000000000000000000000000000000000000000002000000000000000000020800000002000000000000000000000020400000000000000000000200000000000000000000000000000800000000000000",
"logs": [
{
"address": "0x14f6f0057274c3519d6258eb66f5d01d79821d81",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000006de346a7333d97fe0d39a49178ac65918c257b28"
],
"data": "0x0000000000000000000000000000000000000000000000003e73362871420000",
"blockNumber": "0xaa3d8f",
"transactionHash": "0x17def972330f874dcfbc8099ada5da777c4273e3c0aa6faebb103e09615f5784",
"transactionIndex": "0x0",
"blockHash": "0x5b62d0387e2f37ea7c76cde2b08b1a36648aca277c76152fdf0da59e6ee63733",
"blockTimestamp": 0,
"logIndex": "0x0",
"removed": false
},
{
"address": "0x7189d67b9ded72e9f1dcfce1c23ea3af418f4a57",
"topics": [
"0x9612604afba70e4cf03261d7d86ca03d08911d9887aa41621dea929e34cfd7b1",
"0x3361346635383338663763646665333138373361343363613032316139326437",
"0x171ac396aacce04a8c32ecd6322f384796a2a5e15224316a78c9358a8cea809b",
"0x0000000000000000000000006de346a7333d97fe0d39a49178ac65918c257b28"
],
"data": "0x00000000000000000000000014f6f0057274c3519d6258eb66f5d01d79821d810000000000000000000000000000000000000000000000004563918244f40000",
"blockNumber": "0xaa3d8f",
"transactionHash": "0x17def972330f874dcfbc8099ada5da777c4273e3c0aa6faebb103e09615f5784",
"transactionIndex": "0x0",
"blockHash": "0x5b62d0387e2f37ea7c76cde2b08b1a36648aca277c76152fdf0da59e6ee63733",
"blockTimestamp": 0,
"logIndex": "0x2",
"removed": false
}
],
"transactionHash": "0x17def972330f874dcfbc8099ada5da777c4273e3c0aa6faebb103e09615f5784",
"contractAddress": "0x0000000000000000000000000000000000000000",
"gasUsed": "0x1f531",
"effectiveGasPrice": "0xee6b2800",
"blockHash": "0x5b62d0387e2f37ea7c76cde2b08b1a36648aca277c76152fdf0da59e6ee63733",
"blockNumber": "0xaa3d8f",
"transactionIndex": "0x0"
},
"intent": {
"method": "mint",
"type": "assemble",
"from": [
{
"type": "asset",
"id": "item_gold_n",
"amount": 500
}
],
"to": [
{
"type": "ERC20",
"id": "0x14f6f0057274c3519d6258EB66F5d01D79821D81",
"amount": 5
}
],
"target_candidate": {}
}
}response 샘플
POST /api/result
Host: https://your-server.com
Content-Type: application/json
{
"success": true,
"errorCode": null,
"data": null
}HMAC 서명 및 Validator 서명 샘플
HMAC 서명 샘플
const crypto = require('crypto');
function generateHmacSignature(payload, secretKey) {
// JSON 데이터를 문자열로 변환
const dataString = JSON.stringify(payload);
// HMAC-SHA256으로 서명 생성
const hmac = crypto.createHmac('sha256', secretKey);
hmac.update(dataString);
const signature = hmac.digest('hex');
return signature;
}
// 서명 검증 함수
function verifyHmacSignature(payload, receivedSignature, secretKey) {
const expectedSignature = generateHmacSignature(payload, secretKey);
// 타이밍 공격 방지를 위한 안전한 비교
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
// 사용 예시
const secretKey = 'your_secret_key_from_console';
const requestData = {
sessionId: 'player_12345',
assetId: 'gold_coin',
amount: '100.50'
};
// 서명 생성
const signature = generateHmacSignature(requestData, secretKey);
console.log('Generated signature:', signature);
// 서명 검증
const isValid = verifyHmacSignature(requestData, signature, secretKey);
console.log('Signature valid:', isValid);Validator 서명 샘플
import { ethers } from "ethers";
/**
* generateValidatorSignature
*
* Signs the given digest using the validator's private key.
* The digest must be a 32-byte hash (e.g. "0xabc123...").
*
* @param {string} userSig - (optional) user's signature
* @param {string} digest - 32-byte digest to sign
* @returns {Promise<{ success: boolean, signature?: string, error?: string }>}
*/
export async function generateValidatorSignature(userSig, digest) {
try {
// Load validator private key (from env or default)
const privateKey =
process.env.VALIDATOR_PRIVATE_KEY;
// Create an Ethers.js Wallet instance
const wallet = new ethers.Wallet(privateKey);
// Convert digest (hex) to byte array
const digestBytes = ethers.getBytes(digest);
// Sign the digest (must be 32 bytes)
const rawSignature = await wallet.signingKey.sign(digestBytes);
// Serialize signature (r + s + v)
const signature = ethers.Signature.from(rawSignature).serialized;
// Return success response
return {
success: true,
signature,
};
} catch (error) {
console.error("Error generating validator signature:", error);
// Return failure response
return {
success: false,
error: error.message,
};
}
}Updated 8 days ago