PAYATHON 2026

PWA App 如何在 Google Play 中选择订阅计划

支付小周

结论

sku 应填写订阅商品的 product ID,不能填写 base plan ID。PWA 通过 https://play.google.com/billing 发起订阅购买时,前端接口通常没有单独传入 base plan 或 offer 的参数。Google Play 会选择该订阅商品中配置为兼容旧版结算流程的 base plan。

各部分的职责如下:

  • 前端传递订阅商品的 product ID。
  • 在 Play Console 中配置并激活 base plan,并确认它兼容旧版结算流程。
  • 后端验证 purchase token,并显式 acknowledge 这笔购买。
  • 如果需要让用户选择多个计划,而当前 PWA Billing 接口又不能传入 base plan,可以将不同计划拆成不同的订阅商品,分别使用不同的 product ID。

为什么不能把 plan ID 放进 sku

Google Play 的新订阅模型大致分为三层:

Subscription product
└── Base plan
    └── Offer

各层含义如下:

  • product ID 标识订阅商品,例如 my_subscription_id。
  • base plan ID 标识该商品下的计费方案,例如月付、年付或预付费方案。
  • offer 标识试用期、首期优惠等促销条件。

当前代码中的 sku 对应最外层的 product ID,不是 base plan ID:

data: {
  sku: 'my_subscription_id',
}

base plan ID 并不是独立的商品 SKU。把它填入 sku 后,接口返回“商品不存在”或类似错误,属于预期结果。

在 Play Console 中配置计划

进入对应应用的订阅管理页面,依次检查:

  1. 创建或打开订阅商品,例如 my_subscription_id。
  2. 在该订阅商品下创建 base plan。
  3. 配置计费周期、价格、续订方式和销售地区。
  4. 激活该 base plan。
  5. 确认该方案已设为兼容旧版结算流程的 base plan。

即使订阅商品下只有一个 active base plan,也要确认当前 PWA 使用的结算接口能够识别它,而不只是确认它已经激活。部分版本的 Play Console 会把这项设置称为“向后兼容”或类似名称,具体文字可能随控制台版本变化。

前端仍然只传 product ID:

const paymentMethodData = [
  {
    supportedMethods: 'https://play.google.com/billing',
    data: {
      sku: 'my_subscription_id',
    },
  },
];

const request = new PaymentRequest(paymentMethodData);
const paymentResponse = await request.show();

const purchaseToken = paymentResponse.details.purchaseToken;

如果只有一个符合条件的 base plan,Google Play 会自动选择它,用户和后端都不需要再次选择。

后端需要完成的处理

后端不负责选择 base plan,主要处理以下工作:

  1. 接收 purchase token。
  2. 通过 Google Play Developer API 查询购买状态。
  3. 核对应用包名、订阅商品、付款状态和到期时间。
  4. 向用户发放订阅权益。
  5. acknowledge 购买。
  6. 保存购买记录,并处理续订、取消、退款和撤销。

“验证购买”和“acknowledge 购买”是两个不同的操作。查询 token 并确认付款有效,不一定代表购买已经完成 acknowledge。

以下是示意代码,具体方法名取决于使用的 Google API 客户端版本:

const purchase = await androidPublisher.purchases.subscriptionsv2.get({
  packageName: 'com.example.app',
  token: purchaseToken,
});

if (purchase.data.subscriptionState === 'SUBSCRIPTION_STATE_ACTIVE') {
  // 核对订阅商品、到期时间等信息,然后发放权益。
}

// 如购买尚未被 acknowledged,需要调用对应的 acknowledge 接口。

旧式 acknowledge 接口通常还需要 product ID:

await androidPublisher.purchases.subscriptions.acknowledge({
  packageName: 'com.example.app',
  subscriptionId: 'my_subscription_id',
  token: purchaseToken,
  requestBody: {},
});

应以当前使用的 Google Play Developer API 和客户端库文档为准,不能只根据示例中的字段判断购买是否已经确认。

后端处理成功后,前端还应结束 Payment Request:

try {
  const paymentResponse = await request.show();
  const purchaseToken = paymentResponse.details.purchaseToken;

  const response = await fetch('/api/google-play/subscription', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ purchaseToken }),
  });

  if (!response.ok) {
    await paymentResponse.complete('fail');
    throw new Error('Subscription verification failed');
  }

  await paymentResponse.complete('success');
} catch (error) {
  console.error(error);
}

paymentResponse.complete('success') 只负责结束前端支付流程,不能替代后端验证购买状态,也不应假定它必然可以替代 Google Play Developer API 的 acknowledge 操作。

多个计划如何让用户选择

如果一个订阅商品下配置了多个 base plan,而 PWA 的 Payment Request 接口仍然只能接受 sku,下面的调用就无法精确指定其中某个计划:

data: {
  sku: 'my_subscription_id',
}

可以采用以下两种方式。

方式一:只销售兼容方案

在 Play Console 中选择一个兼容当前接口的 base plan。用户购买该 product ID 时,Google Play 会使用这个方案。

这种方式适合只提供一种计费周期或价格的订阅。

方式二:为不同计划创建不同商品

如果需要让用户选择月付或年付,可以创建两个订阅商品:

premium_monthly
premium_yearly

用户先在页面上选择计划,前端再将对应的 product ID 传给 sku:

async function subscribe(productId) {
  const request = new PaymentRequest([
    {
      supportedMethods: 'https://play.google.com/billing',
      data: {
        sku: productId,
      },
    },
  ]);

  const paymentResponse = await request.show();
  const purchaseToken = paymentResponse.details.purchaseToken;

  // 将 productId 和 purchaseToken 一起交给后端验证。
}

调用示例:

await subscribe('premium_monthly');
// 或
await subscribe('premium_yearly');

后端不能直接信任前端传来的 productId,仍需根据 Google Play 返回的购买信息,核对用户实际购买的商品和订阅状态。

关于“Confirm Plan”

“Confirm Plan”并不表示开发者需要把 base plan ID 填入 sku。出现该状态时,应优先检查:

  • base plan 是否已经激活。
  • 是否配置了兼容当前结算接口的 base plan。
  • 后端是否已经 acknowledge 这笔购买。
  • purchase token 是否对应正确的包名和订阅商品。
  • Play Console 中是否有尚未生效的价格变更,或需要用户同意的订阅变更。
  • 测试账号是否已经完成测试购买,还是仍处于待处理状态。

如果购买没有及时完成 acknowledge,Google Play 可能撤销购买并退款,因此需要重点检查这一项。

PWA 前端传递的是订阅商品的 product ID,base plan 则由 Play Console 中的兼容配置决定。只有所使用的 Web Billing 接口明确提供了 base plan 或 offer 的选择参数,前端才能在一次购买请求中直接指定它们。对于当前这种只支持 sku 的调用方式,不能把 base plan ID 当作 SKU 使用。

备注:内容仅供参考。