Payment API
Payment API
TudadaSDK.Instance.payment.* 는 게임 내 상품 구매를 위한 결제 API입니다. 게임은 결제 수단·레일을
알 필요 없이 productId 로만 구매를 요청합니다. 결제 수단 선택, 가격 산출, 영수증 검증, 지급
처리는 모두 투다다 플랫폼이 담당합니다.
네 메서드 모두 콜백 기반입니다 — onSuccess(결과별 타입)와 onFail(Action<PaymentFailResult>)을
받습니다. 실패 분기는 항상 PaymentFailResult.code 로 합니다.
- 결제 미지원 환경 —
GetProducts의onFail이PAYMENT_UNAVAILABLE로 호출됩니다. - 지원 환경, 매칭 상품 없음 —
products가 빈 배열입니다(정상 응답). 빈 배열이면 상점 UI를 노출하지 마세요. - 통신 실패 —
NETWORK_ERROR로 실패합니다.
Purchase 의 결과는 화면 표시·UX 용 축소 정보입니다. 최종 지급 확정은 게임 서버에서 확인하세요.
트랜잭션에는 금액·영수증이 포함되지 않으며, 권위 있는 지급 정보는 게임 서버·투다다 서버가 보유합니다.
참고: 결제는 WebGL 빌드에서만 동작합니다. Unity 에디터 시뮬레이션에서는
onFail이PAYMENT_UNAVAILABLE로 호출됩니다(에디터 시뮬레이션 참조).
payment.GetProducts(onSuccess, onFail)
구매 가능한 상품 목록을 조회합니다. 판매 가능한 상품이 없으면 products 가 빈 배열입니다.
파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
onSuccess | Action<GetProductsResult> | - | 성공 콜백 |
onFail | Action<PaymentFailResult> | - | 실패 콜백 |
성공 응답 (GetProductsResult):
| 필드 | 타입 | 설명 |
|---|---|---|
products | PaymentProduct[] | 판매 가능한 상품 목록 (없으면 빈 배열) |
errMsg | string | 결과 메시지 |
TudadaSDK.Instance.payment.GetProducts(
onSuccess: (res) => {
if (res.products.Length == 0) { HideStore(); return; } // 빈 배열 → 상점 미노출
foreach (var p in res.products)
Debug.Log($"{p.productId} / {p.name} / {p.price} {p.currency}");
RenderStore(res.products);
},
onFail: (err) => Debug.LogError("상품 조회 실패: " + err.code)
);
payment.GetProduct(productId, onSuccess, onFail)
단일 상품을 조회합니다. 미존재/판매 불가 시 onFail 이 PRODUCT_NOT_FOUND 로 호출됩니다.
파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
productId | string | ✅ | 조회할 상품 ID |
onSuccess | Action<GetProductResult> | - | 성공 콜백 |
onFail | Action<PaymentFailResult> | - | 실패 콜백 |
성공 응답 (GetProductResult):
| 필드 | 타입 | 설명 |
|---|---|---|
product | PaymentProduct | 상품 정보 |
errMsg | string | 결과 메시지 |
TudadaSDK.Instance.payment.GetProduct("coin_100",
onSuccess: (res) => Debug.Log($"{res.product.name} / {res.product.price}"),
onFail: (err) => {
if (err.code == PaymentErrorCode.ProductNotFound) ShowToast("판매 중인 상품이 아닙니다.");
}
);
payment.Purchase(productId, passthroughPayload, onSuccess, onFail)
상품을 구매합니다. 성공 시 트랜잭션이 GRANTED(지급 완료) 또는 PENDING(접수·확정 대기)
상태로 전달되며, 그 외에는 onFail 이 호출됩니다.
파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
productId | string | ✅ | 구매할 상품 ID |
passthroughPayload | string | - | 불투명 echo 문자열(≤1000 UTF-8 바이트). 구매 검증 시 원문 그대로 반환됩니다. 비밀·개인정보 금지 |
onSuccess | Action<PurchaseResult> | - | 성공 콜백 |
onFail | Action<PaymentFailResult> | - | 실패 콜백 |
성공 응답 (PurchaseResult):
| 필드 | 타입 | 설명 |
|---|---|---|
transaction | PaymentTransaction | 트랜잭션 (GRANTED / PENDING) |
errMsg | string | 결과 메시지 |
TudadaSDK.Instance.payment.Purchase("coin_100", "order-1234",
onSuccess: (res) => {
var txn = res.transaction;
if (txn.status == PaymentTxnStatus.Granted) {
// 지급 완료 — UX 반영 (최종 확정은 게임 서버에서)
} else if (txn.status == PaymentTxnStatus.Pending) {
// 접수됨 — 확정 대기. GetTransaction 으로 이후 상태 확인 가능
}
},
onFail: (err) => {
if (err.code == PaymentErrorCode.UserCancelled) return; // 사용자가 결제창을 닫음
Debug.LogError("결제 실패: " + err.code);
}
);
payment.GetTransaction(txnKey, onSuccess, onFail)
트랜잭션 상태·결과를 조회합니다. Purchase 가 PENDING 으로 끝난 경우의 후속 확인 등에 사용합니다.
미존재/미소유 시 onFail 이 TRANSACTION_NOT_FOUND 로 호출됩니다.
파라미터:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
txnKey | string | ✅ | 조회할 트랜잭션 키 |
onSuccess | Action<GetTransactionResult> | - | 성공 콜백 |
onFail | Action<PaymentFailResult> | - | 실패 콜백 |
성공 응답 (GetTransactionResult):
| 필드 | 타입 | 설명 |
|---|---|---|
transaction | PaymentTransaction | 트랜잭션 (5개 상태 모두 가능) |
errMsg | string | 결과 메시지 |
TudadaSDK.Instance.payment.GetTransaction(prevTxnKey,
onSuccess: (res) => Debug.Log("상태: " + res.transaction.status),
onFail: (err) => Debug.LogError("조회 실패: " + err.code)
);
데이터 타입
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 | 트랜잭션 상태 (아래 표, PaymentTxnStatus 상수) |
productId | string | 거래 상품 |
failReason | string | 진단·로깅용 상세 사유(사용자 노출 금지, 값 변경 가능). 없으면 빈 문자열 |
passthroughPayload | string | 구매 시 넘긴 값 그대로. 생략·빈 문자열이면 빈 문자열 |
requestedAt | long | 요청 시각 (Unix epoch ms) |
grantedAt | long | 최초 지급 시각 (epoch ms). 미지급 상태면 0 (hasGrantedAt 로 존재 확인) |
트랜잭션 상태(status):
| 값 | 설명 |
|---|---|
PENDING | 접수됨, 지급 미확정 |
GRANTED | 지급 완료 |
FAILED | 실패 |
PARTIALLY_REFUNDED | 부분 환불됨(과거 지급 이력 존재) |
REFUNDED | 환불됨(과거 지급 이력 존재) |
Purchase는GRANTED/PENDING만 전달하고,GetTransaction은 5개 상태를 모두 반환할 수 있습니다.
에러 처리
실패는 onFail 의 PaymentFailResult 로 전달되며, 분기는 항상 code 로 합니다.
| 필드 | 타입 | 설명 |
|---|---|---|
code | string | 에러 코드 (아래 표) — 분기 기준. PaymentErrorCode 상수 |
errMsg | string | 메시지(사용자 노출 금지) |
txnKey | string | 거래가 기록된 실패에만 존재. 없으면 빈 문자열 |
failReason | string | 진단·로깅용 상세 사유. 없으면 빈 문자열 |
에러 코드(code, PaymentErrorCode 상수):
| 코드 | 설명 |
|---|---|
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 | 통신 실패 |
TudadaSDK.Instance.payment.Purchase("coin_100", null,
onSuccess: (res) => { /* ... */ },
onFail: (err) => {
switch (err.code) {
case PaymentErrorCode.UserCancelled: break; // 조용히 무시
case PaymentErrorCode.PaymentInProgress: ShowToast("처리 중입니다."); break;
case PaymentErrorCode.SessionExpired: RequestRelogin(); break;
default: ShowToast("결제에 실패했습니다.");
}
}
);