Payment API
Payment API
TudadaSDK.payment.* 是用于购买游戏内商品的支付 API。游戏无需了解支付方式与渠道,仅通过 productId 即可发起购买。支付方式选择、价格计算、收据验证、发放处理均由Tudada平台负责。
每个动作均分为回调版本(getProducts 等 — success / fail / complete 回调,无返回值)与
*Async 版本(getProductsAsync 等 — 返回 Promise)。回调的成功值与对应 *Async 的 resolve 值相同,
*Async 的必填参数以位置参数传入。
- 不支持支付的环境 —
getProductsAsync()以PAYMENT_UNAVAILABLEreject(回调版本getProducts()调用fail)。 - 支持但无匹配商品 — 返回空数组(正常响应)。若为空数组,请不要展示商店 UI。
- 通信失败 — 以
NETWORK_ERRORreject。
purchase() 的结果是用于界面展示与 UX 的精简信息。**最终发放确认请在游戏服务器进行。**交易中不包含金额与收据,权威的发放信息由游戏服务器与Tudada服务器持有。
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 | - | 不透明回传字符串(≤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 毫秒) |
grantedAt | number | null | 首次发放时刻(epoch 毫秒)。未发放状态时为 null |
交易状态(status):
| 值 | 说明 |
|---|---|
PENDING | 已受理,发放未确认 |
GRANTED | 发放完成 |
FAILED | 失败 |
PARTIALLY_REFUNDED | 已部分退款(存在过往发放记录) |
REFUNDED | 已退款(存在过往发放记录) |
purchase只会以GRANTED/PENDINGresolve,而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('支付失败。');
}
}