支付(SuperSDK.pay namespace)
SDK 提供应用内支付桥接能力 · H5 通过 SuperSDK.pay.* 调用。
🔴 F038 破坏式变更(继 F036 后第二次):
pay.purchase返回从 F036 的PurchaseResult凭证对象改为 PayResult3State 三态对象。统一为「provider.createOrder → provider.pay → 三态回调」契约;凭证不再经 H5 业务 JS(由 SDK NativePayBridge直投后端回执端点)。现网 H5 必须配套升级(无兼容期 · 无 alias)。详见下方 F038 迁移指南。
🔴 SDK 是哑管道(v0.3 设计原则):provider = 两个方法
{ createOrder(payParams)→json, pay(json)→三态 }。createOrder 产出的json对 SDK 完全不透明 —— SDK 不解析其中任何字段(productId / accountId / profileId / orderToken 都不碰),只按provider名路由、把json原样透传给pay。字段语义由 provider 实现方(createOrder 产出 ↔ pay 消费)自洽保障。
业务方前提(必读): 本能力假定业务方已具备服务端主导 IAP 架构:
- 创单 / 凭证验签(verify) / acknowledge / consume / 查单:全部由业务后端实现(调 Google Play Developer API + App Store Server API)· SDK 不提供
- SDK 仅负责 ① 按 provider 名路由 + 把 createOrder 产出的不透明 json 透传给 pay ② 内置
google_play/ 宿主 Apple provider 的原生扣款 ③ 由 NativePayBridge把凭证直投后端回执端点(凭证不出 Native)④ 回调白名单三态 ⑤ 探测能力 ⑥ 客户端 restore(可选) - 详细架构见下方 IAP 推荐架构章节(v0.3 已重写)
⚠️ Apple Guideline 3.1.1 合规提醒:订阅 / 非消耗型商品 App 必须提供 “Restore Purchases” 按钮。可走 SDK pay.restorePurchases(provider 不实现时返回 NOT_SUPPORTED · 业务方 fallback 到业务后端 /api/pay/my-purchases)。漏做审核会 reject。
pay.querySubscriptionOffers(options)
Section titled “pay.querySubscriptionOffers(options)”查询 Google Play 当前账号此刻可购买的订阅 offers。数据只来自客户端 queryProductDetailsAsync(ProductType.SUBS) 返回的 SubscriptionOfferDetails;SDK 不查询 Play Console 完整目录、不读 Settings、不合并业务文案,也不负责 UI 排序。
const offers = await SuperSDK.pay.querySubscriptionOffers({ provider: 'google_play', productIds: ['matrixvplan01']});
// 普通 base plan 的 offerId 明确为 null// offerToken 永远不会暴露给 H5console.log(offers[0]);// {// productId: 'matrixvplan01',// basePlanId: 'weekly01',// offerId: null,// offerTags: [],// title: 'Go VPN',// description: '...',// pricingPhases: [{// formattedPrice: 'US$2.69',// currencyCode: 'USD',// priceAmountMicros: 2690000,// billingPeriod: 'P1W',// billingCycleCount: 0,// recurrenceMode: 1// }]// }| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
options.provider | string | 否 | 单 provider 可省;Google 内置名为 google_play |
options.productIds | string[] | 是 | 非空订阅 product ID 列表 |
返回 Promise<SubscriptionOffer[]>。同一 base plan 可以返回多条当前 eligible offer;Play 未向当前账号返回的促销不会被 SDK 拼接出来。Android 支持;iOS 明确返回 NOT_SUPPORTED。
查询错误会 reject:PRODUCT_NOT_FOUND(请求商品均未返回)、BILLING_UNAVAILABLE(Billing 服务不可用)、QUERY_FAILED(其他查询失败)、SDK_TOO_OLD(Native 尚无该 JSB)或 NOT_IN_APP。
pay.purchase(payConfig)
Section titled “pay.purchase(payConfig)”唯一支付入口。内部聚合 provider.createOrder → provider.pay 两段(公共面仍仅此一个入口 · 不新增 createAndPay)。
执行编排(v0.3 哑管道):
校验 payConfig(SDK 仅校验 payConfig/payParams 是对象 · 不碰业务字段) → json = provider.createOrder(payParams) [provider 实现 · 缺省=identity 透传 payParams] → provider.pay(json) [IAP=SDK 内置原生自读 json · 三方=业务 JS 自写] → 回调三态 PayResult3State { status: finished | cancelled | error }// IAP(provider 注册 createOrder-only override · pay 走 SDK 内置原生 · 见 registerProvider)const result = await SuperSDK.pay.purchase({ provider: 'google_play', // 🔴 内置 Google provider 名(非 'google')· 以 getProviders() 返回为准 · 单 Provider 可省 payParams: { // createOrder 入参 · 由你的 createOrder 消费 · SDK 不解析 productId: 'sku_premium', userId: currentUser.id }});
// result 是三态对象 PayResult3State(不是凭证!)switch (result.status) { case 'finished': // 平台已受理 ≠ 支付成功 · 去业务查单定真相 · 不在客户端发货 pollBusinessOrder(result.orderToken); break; case 'cancelled': // 用户取消 break; case 'error': // 失败 · result.phase('create'|'pay') + result.code console.error(result.phase, result.code, result.message); break;}三方支付 inline 模式(无需 registerProvider · 单次性 · 两方法都内联):
const result = await SuperSDK.pay.purchase({ payParams: { productId: pkg.id, userId: uid }, createOrder: async (payParams) => { // 可选 · 产出对 SDK 不透明的 json(你的 pay 自己消费) const res = await myApi.createOrder(payParams); return { prepayId: res.prepay_id, orderToken: res.order_token }; }, pay: async (json) => { // 必填(inline) · 自行 checkout + 轮询 · 返回三态 // json 是你 createOrder 产出的对象(orderToken 是你自己产的 · 当场就持有 · 无需 SDK 回传) const { payUrl } = await myApi.checkout(json.prepayId); window.open(payUrl, '_blank'); const status = await pollChannel(json.orderToken); // 业务自实现轮询 if (status === 'completed') return { status: 'finished', orderToken: json.orderToken }; if (status === 'cancelled') return { status: 'cancelled' }; return { status: 'error', phase: 'pay', code: status, orderToken: json.orderToken }; }});payConfig 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| provider | string | 否 | 支付方式名(单 Provider 自动选 · 多 Provider 必填)· 以 getProviders() 返回为准 |
| payParams | object | 是 | createOrder 入参 · 任意对象(SDK 仅校验是对象 · 字段由你的 createOrder 消费 · SDK 不解析) |
| createOrder | function | 否 | inline 模式 · (payParams) => Promise<json> · 缺省时 json=payParams(identity 透传) |
| pay | function | 否 | inline 模式 · (json) => Promise<PayResult3State> · 有此则走 inline JS provider;无此则走内置原生 IAP |
🔴 删除 v0.2 的
onOrderCreated/checkout/queryStatus/pollInterval/maxPolls/onProgress:v0.3 哑管道下,这些都是 provider 自己实现的事(三方在自己的pay里 checkout+轮询;orderToken 是业务 createOrder 自己产的 · 当场持有 · 无需 SDK 回调还给业务)。
输入契约(无法开始 → reject):
仅「无法开始」reject(
INVALID_PARAMS/NOT_IN_APP/SDK_TOO_OLD/PURCHASE_IN_PROGRESS/PROVIDER_REQUIRED/NO_PROVIDER);已开始流程的终态(create/pay 失败、取消、结束)一律 resolve 三态。
| 输入 | 行为 |
|---|---|
pay.purchase(undefined/null/'x') | reject INVALID_PARAMS · err.detail: “payConfig must be object” |
pay.purchase({}) 或 payParams 非对象 | reject INVALID_PARAMS · err.detail: “payParams must be object” |
| Native 路径但非端环境 | reject NOT_IN_APP |
Native 路径但老 SDK 无 appbridge.nativePay | reject SDK_TOO_OLD(明示升级 · 不静默 fallback 老 purchase) |
| 多 Provider 不传 provider | reject PROVIDER_REQUIRED |
| 无任何 provider | reject NO_PROVIDER |
| 一笔 inline 进行中再调 | reject PURCHASE_IN_PROGRESS |
🔴 v0.3 不再校验 productId:SDK 不碰业务字段。
productId缺失/类型错由 provider 自己处理(内置google_play的 pay 读不到 productId → 返回phase:'pay'error)。
返回值:Promise<PayResult3State> — 统一三态对象(IAP 与三方路径返回结构一致 · 由 Native PayBridge 白名单构造):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | enum | 是 | finished / cancelled / error |
| phase | enum | error 时 | create(createOrder 阶段失败 · 在 JSSDK 内捕获 · 未调 pay) / pay(pay 阶段平台计费错误) |
| code | string | error 时 | 平台错误码映射(如 BILLING_UNAVAILABLE / CREATE_FAILED) |
| message | string | 否 | 调试信息(非敏感) |
| orderToken | string | 否 | provider 从其 pay 内部结果回带的关联键(唯一业务键 · 供 H5 查单) |
🔴 闭合白名单(凭证不出 Native):发给 H5 的对象只含上表字段,由 Native
PayBridge显式构造 —— 不把 provider 内部结果原样透传。purchaseToken/signature/originalJson等凭证绝不到达 H5(由 PayBridge 投回执端点)。🔴
finished≠ 支付成功:语义为「客户端流程正常结束 · 去查单」。GooglePENDING(现金/家长 Ask-to-Buy/慢速支付)与 iOS 等价待定态映射为finished;GoogleITEM_ALREADY_OWNED也映射为finished(去查单 · 后端对账补发)而非 error。H5 一律以业务查单结果判定成功/待定/失败 · 不在客户端发货/解锁(见下方业务查单四态) 。
pay.getProviders()
Section titled “pay.getProviders()”探测宿主支持的支付方式列表(合并 JS 注册的 + Native 注册的)。
const providers = await SuperSDK.pay.getProviders();// 例如:['google_play', 'alipay'] ← 实际值以本方法返回为准🔴 provider 名以
pay.getProviders()返回为准 · 不要硬编码。内置 Google provider 注册名是'google_play'(GooglePayProvider.getProviderName()硬编码 · 不是'google')。iOS 无内置支付 provider · Apple 的 provider 名由宿主ApplePayProvider实现时自定义。推荐先调getProviders()拿返回值再传给purchase/registerProvider,避免硬编码错名导致PROVIDER_NOT_FOUND。下文示例为说明清晰用'google_play'字面量,生产代码应用getProviders()的返回值。
pay.registerProvider(name, provider)
Section titled “pay.registerProvider(name, provider)”注册支付 Provider。v0.3 起 provider = 两个方法 { createOrder?, pay? }(至少有一个)· 由 pay 是否存在决定路由:
形态 A · 三方支付(支付宝 / 微信 / Stripe 等非 IAP):有 pay(可附 createOrder)。pay 自行 checkout + 轮询 · 返回三态。
SuperSDK.pay.registerProvider('alipay', { createOrder: async (payParams) => { // 可选 · 产出对 SDK 不透明的 json(你的 pay 消费) const res = await myApi.createOrder(payParams); return { prepayId: res.prepay_id, orderToken: res.order_token }; }, pay: async (json) => { // 必填(三方) · 自行拉起渠道 + 轮询 · 返回 PayResult3State const { payUrl } = await myApi.checkout(json.prepayId); window.open(payUrl, '_blank'); const status = await pollChannel(json.orderToken); // 业务自实现 if (status === 'completed') return { status: 'finished', orderToken: json.orderToken }; if (status === 'cancelled') return { status: 'cancelled' }; return { status: 'error', phase: 'pay', code: status, orderToken: json.orderToken }; }});形态 B · IAP createOrder override(内置 Google = 'google_play' / 宿主 Apple provider 名见 getProviders()):仅 createOrder(无 pay)· pay 走 SDK 内置原生扣款(appbridge.nativePay)。用于让 IAP 的 createOrder 产出你约定的 json(供内置原生 pay 自读)。
🔴 注册名必须与内置/宿主 provider 名完全一致(内置 Google 是
'google_play',不是'google')· 否则purchase时PROVIDER_NOT_FOUND。建议用(await SuperSDK.pay.getProviders())的返回值作 name,不要硬编码。
const [googleName] = (await SuperSDK.pay.getProviders()) .filter(n => n === 'google_play'); // 取真实内置名 · 不硬编码
SuperSDK.pay.registerProvider(googleName /* 'google_play' */, { createOrder: async (payParams) => { // 业务自行 fetch 创单端点建单 · 返回内置 google_play provider 的 pay 输入 json(见下「google provider 输入键」) const res = await myApi.createOrder(payParams); // 🔴 后端创单端点签发 UUID orderToken return { productId: payParams.productId, // 必填 productType: payParams.productType ?? 'inapp', // 'inapp'(默认)或'subs' basePlanId: payParams.basePlanId, // productType='subs'时必填 offerId: payParams.offerId, // 订阅优惠可选;省略时购买 base plan accountId: payParams.accountId, // 可选 → obfuscatedAccountId(用户标识) profileId: res.order_token // 可选 → obfuscatedProfileId(订单标识)· 🔴 必须 UUID }; } // 无 pay → SDK 内置 google_play 原生 pay 消费上面的 json(SDK 不解析 · 由内置 provider.pay 自读)});🔴 orderToken 必须 UUID(
profileId/ iOSappAccountToken同值):若纯前端生成,用crypto.randomUUID();若后端签发,后端创单端点须返回 UUID 形态。非 UUID → 内置google_play/ iOS host provider 返回{ phase:'pay', code:'INVALID_ORDER_TOKEN' }。// 前端生成 orderToken 示例(无后端创单时)createOrder: async (payParams) => ({productId: payParams.productId,profileId: crypto.randomUUID() // 🔴 UUID · 双端(Android obfuscatedProfileId / iOS appAccountToken)通吃})
provider 字段:
| 字段 | 形态 | 说明 |
|---|---|---|
| createOrder | A 可选 / B 必填 | (payParams) => Promise<json> · 产出对 SDK 不透明的 json · 缺省=identity(json=payParams) |
| pay | A 必填 / B 无 | (json) => Promise<PayResult3State> · 有它=三方 JS pay;无它=内置原生 IAP pay |
🔴 provider 契约(v0.3 哑管道):
pay.purchase内部json = createOrder(payParams)→pay(json)。SDK 不解析 json 任何字段(productId/accountId/profileId/orderToken 都不碰)· 由 provider 实现方(createOrder 产出 ↔ pay 消费)自洽保障。google provider 输入键(文档化 · 非 SDK 传输层):内置
google_play的 pay 自读 json:productId(必填)、productType(可选,'inapp'默认值或'subs')、basePlanId(subs必填)、offerId(subs可选)、accountId(可选 →obfuscatedAccountId)、profileId(可选 →obfuscatedProfileId)。订阅只从 Google 返回的用户可用SubscriptionOfferDetails中精确匹配basePlanId + offerId;不传offerId时匹配该 base plan 自身的 offer,不会静默回退到其他优惠。无匹配返回OFFER_UNAVAILABLE。这是 google provider 的输入契约(谁 override 其 createOrder 就照此产)· 不是 SDK 传输层契约。宿主 Apple provider 自定义其 pay 输入键。未 override createOrder 的纯 IAP:provider 既未注册 createOrder、payConfig 也无 createOrder → SDK 用 identity(json=payParams)· 即
payParams直接作内置 pay 的输入(payParams 须自带 google provider 输入键)。
pay.restorePurchases(options)
Section titled “pay.restorePurchases(options)”客户端恢复购买 · 走 Native PayService.restorePurchases · provider 可选实现。
🔵 F038 不改本契约:
restorePurchases仍返回 F036 的PurchaseResult[](客户端 StoreKit/BillingClient 凭证恢复)· 与purchase的「三态 + 凭证回执直投」属不同路径(restore 是客户端凭证恢复 · 不走回执直投)。统一化留后续 Feature(已知不一致)。Google 订阅约束:内置
google_playprovider 的 restore 仅查询ProductType.SUBS,不返回一次性商品。
try { // provider 名以 getProviders() 返回为准:内置 Google 是 'google_play';iOS Apple 名由宿主注册 const purchases = await SuperSDK.pay.restorePurchases({ provider: 'apple' /* 宿主注册名 · 以 getProviders() 为准 */ }); // purchases: [{ orderId, purchaseToken, productId, purchaseState }]} catch (e) { if (e.message === 'NOT_SUPPORTED') { // provider 未实现 restore · fallback 业务后端 const res = await fetch('/api/pay/my-purchases?userId=' + userId); const purchases = await res.json(); } else if (e.message === 'PROVIDER_NOT_FOUND') { // 错误的 provider 名 }}| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| options.provider | string | 否 | 单 Provider 时可省 · 多 Provider 必填 |
返回值:Promise<Object[]> — 购买记录数组,每个元素结构同 pay.purchase 的 Native IAP 返回对象(含 orderId / productId / purchaseToken / purchaseTime / purchaseState / isAcknowledged / signature / originalJson / provider / raw)。无可恢复购买时返回空数组 []。
F038
pay.purchase的失败分两类:reject 的 code(无法开始 ·e.message)与 error 三态的 code(已开始的支付阶段失败 ·result.code+result.phase)。
reject code(无法开始 · pay.purchase 抛出):
| code | 含义 | 建议处理 |
|---|---|---|
| INVALID_PARAMS | 入参错误(payConfig/payParams 非对象)· err.detail 含具体字段 | 检查传入参数 |
| NOT_IN_APP | Native 路径但非端环境 | 检查 isInApp() |
| SDK_TOO_OLD | 老 SDK 无 appbridge.nativePay(F038 统一支付需 SDK 升级) | 升级宿主 SDK |
| PROVIDER_REQUIRED | 多 Provider 时未指定 | 传入 provider 参数 |
| NO_PROVIDER | 无任何 provider 可用 | 提示宿主未实现支付 |
| PURCHASE_IN_PROGRESS | 有 inline 购买正在进行中 | 等待当前购买完成 |
error 三态 code(result.status==='error' · 带 phase):
| code | phase | 含义 | 建议处理 |
|---|---|---|---|
| CREATE_FAILED | create | createOrder reject/throw(在 JSSDK 内捕获 · 未调 pay) | 提示重试(未拉起 · 可安全重发) |
| INVALID_JSON | create | createOrder 产出的 json 无法 JSON 序列化(IAP 路径) | 检查 createOrder 产出对象 |
| INVALID_PARAMS | pay | 内置 google_play 缺少 productId / 订阅缺少 basePlanId / productType 非 inapp/subs | 修正 Google provider 输入键 |
| INVALID_ORDER_TOKEN | pay | provider 校验输入契约:orderToken(profileId / appAccountToken)非 UUID 格式(内置 google_play 与 iOS host provider 均会返回此 code) | createOrder 产 orderToken 时用 crypto.randomUUID()(后端创单端点亦同) |
| PRODUCT_NOT_FOUND | pay | 购买前重查后未找到所选订阅商品 | 刷新 eligible offer 列表 |
| OFFER_UNAVAILABLE | pay | 内置 google_play 返回的用户可用订阅 offer 中没有匹配的 basePlanId + offerId | 刷新商品配置,确认用户资格及 Play Console base plan/offer |
| BILLING_UNAVAILABLE | pay | 订阅查询或购买时 Google Billing 服务不可用 | 稍后重试 |
| QUERY_FAILED | pay | 订阅购买前查询失败(非 Billing unavailable) | 稍后重试并保留原选择 |
| PURCHASE_FAILED | pay | 订阅 Billing UI 拉起或购买回调失败 | 展示错误并允许重试 |
| FAILED / EXPIRED / CANCELLED… | pay | 三方 provider 的 pay 自定义返回的 code(业务自定) | 按业务语义处理 |
| BILLING_UNAVAILABLE / SERVICE_UNAVAILABLE / DEVELOPER_ERROR / ITEM_UNAVAILABLE / NO_ACTIVITY / ERROR … | pay | 内置 IAP provider.pay 平台计费错误(映射自 BillingResponseCode / StoreKit Error) | 展示错误信息 |
🔴 注意:
cancelled不在 error 列(单独是status==='cancelled');PENDING/ITEM_ALREADY_OWNED也不是 error(归finished· 去查单 · 见下方架构章节)。phase:'pay'的具体 code 由 provider.pay 返回(IAP=SDK 内置映射;三方=业务自定),SDK 不收敛枚举(哑管道)。
业务查单四态(finished 后)
Section titled “业务查单四态(finished 后)”finished ≠ 支付成功。SDK 不提供查单 API(与支付通道解耦)。H5 用 orderToken 轮询业务自有查单接口判定真实结果。业务查单接口约定返回四态:
| status | 含义 | H5 处理 |
|---|---|---|
success | 后端已验签 + 发货完成 | 解锁 / 提示成功 |
processing | 受理中(SDK 回执未达 / RTDN 未达 / 重试中) | 继续轮询(自定间隔/上限) |
failed | 后端判定失败(验签不过 / 退款 / 平台拒绝) | 提示失败 |
unknown | 暂不确定(超轮询上限仍未定) | 留后台兜底(RTDN/ASSN 终会落地)· 不在客户端发货 |
async function pollBusinessOrder(orderToken, maxPolls = 6, interval = 2000) { for (let i = 0; i < maxPolls; i++) { const { status } = await fetch('/api/pay/order-status?orderToken=' + orderToken).then(r => r.json()); if (status === 'success' || status === 'failed') return status; await new Promise(r => setTimeout(r, interval)); } return 'unknown'; // 超上限仍未定 · 依赖后端 RTDN/ASSN 兜底}凭证已由 SDK Native(PayBridge)直投后端回执端点 · 后端独立验签发货。即使前端被杀,RTDN/ASSN 兜底也能让订单最终落地 success(见凭证回执直投契约 PayBridge 守门)。
完整支付流程示例(v0.3)
Section titled “完整支付流程示例(v0.3)”let PAY_PROVIDER; // 真实 provider 名 · 来自 getProviders() · 不硬编码
async function init() { // 1. 探测能力(provider 名以此返回为准 · 内置 Google 为 'google_play') const providers = await SuperSDK.pay.getProviders(); if (providers.length === 0) return; PAY_PROVIDER = providers[0]; // 或按业务挑选 · 如 providers.find(n => n === 'google_play')
// 2. 给 IAP provider 注册 createOrder override(可选 · 让 createOrder 产出 google provider 输入键) SuperSDK.pay.registerProvider(PAY_PROVIDER, { createOrder: async (payParams) => { const res = await fetch('/api/pay/create-order', { // 业务创单(后端签发 UUID orderToken · 你当场持有) method: 'POST', body: JSON.stringify(payParams) }).then(r => r.json()); // ⬇️ google provider 输入键;订阅还需 productType='subs' + basePlanId,offerId 可选 return { productId: payParams.productId, productType: payParams.productType ?? 'inapp', basePlanId: payParams.basePlanId, offerId: payParams.offerId, accountId: res.accountId, profileId: res.orderToken // 🔴 UUID }; } });}
async function buyProduct(productId, userId) { // 3. 发起购买(SDK 内部 createOrder→pay · 返回三态) const result = await SuperSDK.pay.purchase({ provider: PAY_PROVIDER, // 🔴 用 getProviders() 返回值 · 勿硬编码 'google'(内置实为 'google_play') payParams: { productId, userId } // 任意对象 · 由你的 createOrder 消费 · SDK 不解析 });
// 4. 按三态分支 switch (result.status) { case 'finished': // ≠ 成功 · 去查单(orderToken 由 provider 内部回带) const orderStatus = await pollBusinessOrder(result.orderToken); if (orderStatus === 'success') unlockEntitlement(); else if (orderStatus === 'failed') showFailed(); else showPending(); // processing / unknown break; case 'cancelled': // 用户取消 · 静默 break; case 'error': // 失败 · phase + code if (result.phase === 'create') showRetry(); // createOrder 失败 · 未拉起 · 可安全重发 else console.error('支付失败:', result.code, result.message); break; } // 🔴 凭证不经 H5:purchaseToken/signature 等由 SDK Native(PayBridge)直投后端回执端点 · H5 不再 POST /verify}
async function restoreUserPurchases(userId) { try { // 5. 客户端 restore(Apple Guideline 3.1.1 必需 · F038 契约不变 · 返回 PurchaseResult[]) return await SuperSDK.pay.restorePurchases({ provider: PAY_PROVIDER }); } catch (e) { if (e.message === 'NOT_SUPPORTED') { const res = await fetch('/api/pay/my-purchases?userId=' + userId); return await res.json(); } throw e; }}F038 迁移指南(从 F036 PurchaseResult 迁到三态)
Section titled “F038 迁移指南(从 F036 PurchaseResult 迁到三态)”🔴 破坏式变更(继 F036 后第二次):
pay.purchase返回从PurchaseResult凭证对象改为 PayResult3State 三态对象;凭证不再经 H5;JSB 由位置参数改为通用nativePay(provider, jsonString, callbackId)。现网 H5 必须配套升级(无 alias · 无兼容期)。restorePurchases不变(仍 F036PurchaseResult[])。
| F036 写法 | F038 后行为 | 迁移路径 |
|---|---|---|
const r = await pay.purchase(...)r.purchaseToken / r.orderId / r.signature | r 不再含凭证(三态对象) | 删除「读凭证 + POST /verify」逻辑;改为读 r.status(finished/cancelled/error) |
if (r.purchaseState===0) ...(凭证判成功) | r.purchaseState 不存在 | 改为 r.status==='finished' → 用 orderToken 查单定真相 |
try/catch 捕 CANCELLED | 取消不再 reject | 改为 r.status==='cancelled' 分支 |
| try/catch 捕支付失败 | 支付阶段失败不再 reject(归三态 error) | 改为 r.status==='error' + r.phase/r.code;reject 仅剩「无法开始」(INVALID_PARAMS/NOT_IN_APP/SDK_TOO_OLD/PURCHASE_IN_PROGRESS/PROVIDER_REQUIRED/NO_PROVIDER) |
H5 POST /api/pay/verify(凭证) | 凭证不经 H5 | 删除;凭证由 SDK Native(PayBridge)直投宿主配置的回执端点 · H5 改为轮询业务查单接口 |
| (无)需要订单关联 | — | 注册 createOrder override · 产出 google provider 输入键(orderToken→profileId);orderToken 是你 createOrder 自己产的 · 当场持有 · 无需 SDK 回调 |
🔴 不留兼容包袱:F038 直接改三态返回 · 不提供 PurchaseResult 旧返回 alias · 不留兼容期。CHANGELOG 标 BREAKING CHANGE。