Payment API
Payment API
TudadaSDK.payment.* 는 게임 내 상품 구매를 위한 결제 API입니다. 게임은 결제 수단·레일을
알 필요 없이 productId 로만 구매를 요청합니다. 결제 수단 선택, 가격 산출, 영수증 검증,
지급 처리는 모두 투다다 플랫폼이 담당합니다.
각 동작은 콜백 버전(getProducts 등 — success / fail / complete 콜백, 반환 없음)과
*Async 버전(getProductsAsync 등 — Promise 반환)으로 분리되어 있습니다. 콜백의 성공값은
대응 *Async 의 resolve 값과 동일하며, *Async 의 필수 인자는 위치 인자로 전달합니다.
- 결제 미지원 환경 —
getProductsAsync()가PAYMENT_UNAVAILABLE로 reject(콜백 버전getProducts()는fail호출)됩니다. - 지원 환경, 매칭 상품 없음 — 빈 배열을 반환합니다(정상 응답). 빈 배열이면 상점 UI를 노출하지 마세요.
- 통신 실패 —
NETWORK_ERROR로 reject됩니다.
purchase() 의 결과는 화면 표시·UX 용 축소 정보입니다. 최종 지급 확정은 게임 서버에서 확인하세요.
트랜잭션에는 금액·영수증이 포함되지 않으며, 권위 있는 지급 정보는 게임 서버·투다다 서버가 보유합니다.
payment.getProducts(options?)
구매 가능한 상품 목록을 조회합니다(콜백 버전, 반환 없음).
옵션:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
success | function | - | 성공 콜백 — PaymentProduct[] |
fail | function | - | 실패 콜백 — PaymentFailResult |
complete | function | - | 완료 콜백 |
TudadaSDK.payment.getProducts({
success: (products) => {
if (products.length === 0) hideStore(); // 빈 배열 → 상점 미노출
else renderStore(products);
},
fail: (err) => console.warn('상품 조회 실패:', err.code),
});
payment.getProductsAsync()
getProducts() 의 Promise 버전입니다. Promise<PaymentProduct[]> 를 반환하며, 판매 가능한 상품이 없으면 빈 배열입니다.
const products = await TudadaSDK.payment.getProductsAsync();
if (products.length === 0) {
hideStore(); // 빈 배열 → 상점 미노출
} else {
renderStore(products);
}
payment.getProduct(options)
단일 상품을 조회합니다(콜백 버전). 미존재/판매 불가 시 fail 콜백에 PRODUCT_NOT_FOUND 가 전달됩니다.
옵션:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
productId | string | ✅ | 조회할 상품 ID |
success | function | - | 성공 콜백 — PaymentProduct |
fail | function | - | 실패 콜백 |
complete | function | - | 완료 콜백 |
TudadaSDK.payment.getProduct({
productId: 'coin_100',
success: (product) => console.log(product.name, product.price),
fail: (err) => {
if (err.code === 'PRODUCT_NOT_FOUND') showToast('판매 중인 상품이 아닙니다.');
},
});
payment.getProductAsync(productId)
getProduct() 의 Promise 버전입니다. productId 를 위치 인자로 받아 Promise<PaymentProduct> 를 반환하며, 미존재/판매 불가 시 PRODUCT_NOT_FOUND 로 reject됩니다.
try {
const product = await TudadaSDK.payment.getProductAsync('coin_100');
console.log(product.name, product.price);
} catch (err) {
if (err.code === 'PRODUCT_NOT_FOUND') showToast('판매 중인 상품이 아닙니다.');
}
payment.purchase(options)
상품을 구매합니다(콜백 버전). 성공 시 트랜잭션이 GRANTED(지급 완료) 또는 PENDING(접수·확정 대기)
상태로 전달되며, 그 외에는 fail 콜백이 호출됩니다.
옵션:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
productId | string | ✅ | 구매할 상품 ID |
passthroughPayload | string | - | 불투명 echo 문자열(≤1000 UTF-8 바이트). 구매 검증 시 원문 그대로 반환됩니다. 비밀·개인정보 금지 |
success | function | - | 성공 콜백 — PaymentTransaction |
fail | function | - | 실패 콜백 |
complete | function | - | 완료 콜백 |
TudadaSDK.payment.purchase({
productId: 'coin_100',
passthroughPayload: 'order-1234', // 선택 — 검증 시 원문 반환
success: (txn) => {
if (txn.status === 'GRANTED') {
// 지급 완료 — UX 반영 (최종 확정은 게임 서버에서)
} else if (txn.status === 'PENDING') {
// 접수됨 — 확정 대기. getTransaction 으로 이후 상태 확인 가능
}
},
fail: (err) => {
if (err.code === 'USER_CANCELLED') return; // 사용자가 결제창을 닫음
console.error('결제 실패:', err.code);
},
});
payment.purchaseAsync(productId, passthroughPayload?)
purchase() 의 Promise 버전입니다. productId(필수)와 passthroughPayload(선택)를 위치 인자로
받아 Promise<PaymentTransaction>(GRANTED / PENDING)를 반환합니다.
try {
const txn = await TudadaSDK.payment.purchaseAsync('coin_100', 'order-1234');
if (txn.status === 'GRANTED') {
// 지급 완료 — UX 반영 (최종 확정은 게임 서버에서)
} else if (txn.status === 'PENDING') {
// 접수됨 — 확정 대기. getTransactionAsync 으로 이후 상태 확인 가능
}
} catch (err) {
if (err.code === 'USER_CANCELLED') return; // 사용자가 결제창을 닫음
console.error('결제 실패:', err.code);
}
payment.getTransaction(options)
트랜잭션 상태·결과를 조회합니다(콜백 버전). purchase 가 PENDING 으로 끝난 경우의 후속 확인 등에
사용합니다. 미존재/미소유 시 fail 콜백에 TRANSACTION_NOT_FOUND 가 전달됩니다.
옵션:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
txnKey | string | ✅ | 조회할 트랜잭션 키 |
success | function | - | 성공 콜백 — PaymentTransaction |
fail | function | - | 실패 콜백 |
complete | function | - | 완료 콜백 |
TudadaSDK.payment.getTransaction({
txnKey: prevTxn.txnKey,
success: (txn) => console.log(txn.status),
});
payment.getTransactionAsync(txnKey)
getTransaction() 의 Promise 버전입니다. txnKey 를 위치 인자로 받아 Promise<PaymentTransaction>
(5개 상태 모두 가능)를 반환하며, 미존재/미소유 시 TRANSACTION_NOT_FOUND 로 reject됩니다.
const txn = await TudadaSDK.payment.getTransactionAsync(prevTxn.txnKey);
console.log(txn.status); // 'PENDING' | 'GRANTED' | 'FAILED' | 'PARTIALLY_REFUNDED' | 'REFUNDED'
데이터 타입
PaymentProduct
| 필드 | 타입 | 설명 |
|---|---|---|
productId | string | 마켓 무관 단일 상품 ID (구매 시 지정) |
name | string | 등록 상품명 |
price | string | (할인 적용된) 현재 가격 — 표시-완성 문자열(예 "4,400", "4.99"). 산술 금지, 그대로 표시 |
currency | string | 통화 식별 문자열(예 "KRW"). 미인지 값은 문자열 그대로 표시 |
originalPrice | string? | 할인 전 가격(표시-완성 문자열). 취소선 등 표시용 |
discountRate | string? | 할인율 표시 문자열(예 "20%"). 마케팅 표기 — 가격으로 역산 금지 |
가격은 모두 표시용 완성 문자열입니다. 천단위 구분자·소수 자릿수가 적용된 상태이므로 그대로 출력하고, 직접 계산에 사용하지 마세요.
PaymentTransaction
검증·UX 용 축소 뷰입니다(금액·영수증 없음).
| 필드 | 타입 | 설명 |
|---|---|---|
txnKey | string | 고유 키 — 재조회에 사용 |
status | string | 트랜잭션 상태 (아래 표) |
productId | string | 거래 상품 |
failReason | string | null | 진단·로깅용 상세 사유(사용자 노출 금지, 값 변경 가능). 없으면 null |
passthroughPayload | string | null | 구매 시 넘긴 값 그대로. 생략·빈 문자열이면 null |
requestedAt | number | 요청 시각 (Unix epoch ms) |
grantedAt | number | null | 최초 지급 시각 (epoch ms). 미지급 상태면 null |
트랜잭션 상태(status):
| 값 | 설명 |
|---|---|
PENDING | 접수됨, 지급 미확정 |
GRANTED | 지급 완료 |
FAILED | 실패 |
PARTIALLY_REFUNDED | 부분 환불됨(과거 지급 이력 존재) |
REFUNDED | 환불됨(과거 지급 이력 존재) |
purchase는GRANTED/PENDING만 resolve하고,getTransaction은 5개 상태를 모두 반환할 수 있습니다.
에러 처리
실패는 콜백 fail(콜백 버전) 또는 Promise reject(*Async 버전)로 전달되며, 분기는 항상 code 로 합니다.
| 필드 | 타입 | 설명 |
|---|---|---|
code | string | 에러 코드 (아래 표) — 분기 기준 |
errMsg | string | 메시지(사용자 노출 금지) |
txnKey | string? | 거래가 기록된 실패에만 존재 |
failReason | string? | 진단·로깅용 상세 사유 |
에러 코드(code):
| 코드 | 설명 |
|---|---|
PAYMENT_UNAVAILABLE | 결제 불가 환경 |
USER_CANCELLED | 사용자가 결제창을 닫음 |
PAYMENT_FAILED | 결제 실패(카드 거절·잔액 부족 등) |
PRODUCT_NOT_FOUND | 등록되지 않은 productId |
INVALID_PARAM | 잘못된 인자(passthroughPayload 초과 등) |
PURCHASE_REJECTED | 검증/지급 거절 |
PAYMENT_IN_PROGRESS | 진행 중 구매가 있어 신규 호출 거부(동시 1건) |
TRANSACTION_NOT_FOUND | 트랜잭션 미존재 또는 미소유 |
SESSION_EXPIRED | 세션 만료 — 재로그인 필요 |
NETWORK_ERROR | 통신 실패 |
try {
await TudadaSDK.payment.purchaseAsync('coin_100');
} catch (err) {
switch (err.code) {
case 'USER_CANCELLED': break; // 조용히 무시
case 'PAYMENT_IN_PROGRESS': showToast('처리 중입니다.'); break;
case 'SESSION_EXPIRED': requestRelogin(); break;
default: showToast('결제에 실패했습니다.');
}
}