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("支付失败。");
}
}
);