能力 · API
Digital Goods API
发布于 更新于
Digital Goods API 让 Web 应用从商店后端读取自己的商品目录和用户的购买记录:window.getDigitalGoodsService(provider) 返回一个 DigitalGoodsService,其 getDetails()、listPurchases()、listPurchaseHistory() 与 consume() 直接对接该提供方。购买本身通过 Payment Request API 完成,付款方式用的是同一个提供方 URL。
Android 与 ChromeOS 上的 Chrome 101 是唯一实现,并且只在 PWA 运行于发布到 Google Play 的 Trusted Web Activity 中、以 https://play.google.com/billing 为提供方时可用(Chrome for Developers 指南)。MDN 的 browser-compat-data 没有 DigitalGoodsService 条目;Firefox 与 Safari 不暴露 getDigitalGoodsService。规范是 WICG 草案,不支持的环境下 Window 上根本没有这个方法,所以一个 in 检查就是完整的特性检测。
window.getDigitalGoodsService(serviceProvider)
digitalGoodsService.getDetails(itemIds)digitalGoodsService.listPurchases()digitalGoodsService.listPurchaseHistory()digitalGoodsService.consume(purchaseToken)getDigitalGoodsService() 返回 Promise<DigitalGoodsService>。getDetails() 返回 Promise<sequence<ItemDetails>>,listPurchases() 与 listPurchaseHistory() 返回 Promise<sequence<PurchaseDetails>>,consume() 返回 Promise<undefined>。一切都是 [SecureContext] 且仅限 Window;没有方法需要用户手势,但文档必须与顶层页面同源,并被允许使用 payment 这个 Permissions Policy 特性(同站 iframe 需要 allow="payment")。
三个方法接受参数;两个列表方法不接受。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
serviceProvider |
DOMString |
是 | 基于 URL 的支付方式标识符;Google Play 为 "https://play.google.com/billing"。undefined、null 或 "" 会拒绝。 |
itemIds |
sequence<DOMString> |
是 | 在商店控制台配置的商品或订阅 ID。不能为空;结果可能省略未知 ID,顺序也不保证。 |
purchaseToken |
DOMString |
是 | listPurchases() 或购买时 PaymentResponse.details 返回的令牌。不能为空。 |
getDetails() 兑现的 ItemDetails 字典包含以下成员:
| 成员 | 类型 | 必填 | 说明 |
|---|---|---|---|
itemId |
DOMString |
是 | 被请求的 ID。 |
title |
DOMString |
是 | 用于展示的本地化名称。 |
price |
PaymentCurrencyAmount |
是 | 用户所在地区的 { currency, value };用 Intl.NumberFormat 格式化。 |
type |
"product" 或 "subscription" |
否 | 商品种类。 |
description |
DOMString |
否 | 本地化的详细描述。 |
iconURLs |
sequence<DOMString> |
否 | 商品图片。 |
subscriptionPeriod |
DOMString |
否 | ISO 8601 时长,仅订阅。 |
freeTrialPeriod |
DOMString |
否 | 免费试用的 ISO 8601 时长。 |
introductoryPrice |
PaymentCurrencyAmount |
否 | 优惠期内的价格。 |
introductoryPricePeriod |
DOMString |
否 | 优惠期的 ISO 8601 时长。 |
introductoryPriceCycles |
unsigned long long |
否 | 优惠价适用的计费周期数。 |
listPurchases() 与 listPurchaseHistory() 兑现的 PurchaseDetails 字典有两个必填成员 itemId 与 purchaseToken;listPurchaseHistory() 可能包含已消耗或已过期的购买,没有历史记录的商店返回与 listPurchases() 相同的列表。
每个方法都以拒绝而非抛出报错。Chromium 的 OperationError 消息就是 Play Billing 的响应码字面量,这是区分「商品已拥有」与「商店应用缺失」的唯一途径。
| 异常 | 条件 |
|---|---|
InvalidStateError |
在非 fully active 的文档中调用 getDigitalGoodsService()。 |
NotAllowedError |
调用 getDigitalGoodsService() 的文档与顶层源不同源,或 payment Permissions Policy 不允许。 |
TypeError |
getDigitalGoodsService() 的提供方为 undefined、null 或空串;getDetails([]);consume("")。 |
OperationError |
getDigitalGoodsService() 的提供方在当前环境不受支持(不在 TWA 内,或打包时未启用 Play Billing);四个服务方法在商店报错时。Chromium 的消息字符串:unsupported payment method、unsupported context、error、itemAlreadyOwned、itemNotOwned、itemUnavailable、clientAppUnavailable、clientAppError。 |
针对同一提供方的 PaymentRequest.show() 在用户退出 Play 购买面板时以 AbortError 拒绝;那是 Payment Request API 的异常,不属于本 API。
每个示例都从同一个辅助函数出发,它在 Play 支撑的 Trusted Web Activity 之外兑现 null,应用的其余部分据此显示网页收银台(Stripe、PayPal 或普通表单)。
获取 Play 服务,不可用时回退到网页收银台
Section titled “获取 Play 服务,不可用时回退到网页收银台”in 检查只说明浏览器实现了该 API;只有 Promise 兑现才说明这个安装包能连上 Play Billing。两种失败按同一方式处理。
const PLAY_BILLING = 'https://play.google.com/billing';
async function getPlayService() { if (!('getDigitalGoodsService' in window)) return null; try { return await window.getDigitalGoodsService(PLAY_BILLING); } catch (err) { console.info(`Play Billing 不可用(${err.name}: ${err.message})`); return null; }}
const service = await getPlayService();document.querySelector('#web-checkout').hidden = Boolean(service);document.querySelector('#play-checkout').hidden = !service;记录 err.message 能把 Play 响应码(unsupported context、clientAppUnavailable)留在遥测里,而不改变用户看到的内容。
用 getDetails() 按用户区域渲染价格
Section titled “用 getDetails() 按用户区域渲染价格”price 以 PaymentCurrencyAmount 形式到达;用 Intl.NumberFormat 格式化,而不是自己拼接 value 与 currency。缺失的 ID 直接不在结果里,所以遍历响应而不是请求。
async function renderCatalogue(service, ids) { if (!service) return renderWebPrices(ids); // 从你自己的服务器获取 const items = await service.getDetails(ids); const money = new Intl.NumberFormat(navigator.language, { style: 'currency', currency: items[0]?.price.currency ?? 'USD' }); for (const item of items) { const row = document.createElement('li'); row.textContent = `${item.title}: ${money.format(item.price.value)}`; if (item.type === 'subscription' && item.freeTrialPeriod) { row.textContent += `(试用 ${item.freeTrialPeriod})`; } document.querySelector('#catalogue').append(row); }}P7D 之类的试用期是 ISO 8601 时长字符串,需要自行格式化;Chrome 101 的 Intl 没有可用于它的时长格式化器。
用 PaymentRequest 购买,再用 listPurchases() 恢复权益
Section titled “用 PaymentRequest 购买,再用 listPurchases() 恢复权益”购买流程是 Payment Request:supportedMethods 设为提供方 URL,SKU 放进 data;Play 忽略你仍必须提供的 total。在服务器上确认 purchaseToken:任何三天内未确认的购买,Google Play 都会退款并撤销(Chrome for Developers 指南)。之后的启动里,listPurchases() 返回用户已拥有的内容,包括在其他设备上的购买。
async function buy(service, sku) { if (!service) return startWebCheckout(sku); const request = new PaymentRequest( [{ supportedMethods: PLAY_BILLING, data: { sku } }], { total: { label: 'Total', amount: { currency: 'USD', value: '0' } } }, ); try { const response = await request.show(); const { purchaseToken } = response.details; const verified = await postJson('/play/acknowledge', { sku, purchaseToken }); await response.complete(verified ? 'success' : 'fail'); if (verified) await service.consume(purchaseToken); // 仅限消耗型商品 } catch (err) { if (err.name !== 'AbortError') throw err; // 用户退出了 Play 购买面板 }}
async function restoreEntitlements(service) { if (!service) return []; const owned = await service.listPurchases(); return (await postJson('/play/verify', owned)).filter((p) => p.valid).map((p) => p.itemId);}consume() 只用于设计为可重复购买的商品:规范写明购买被消耗后用户预期不再享有该权益,所以不要对订阅或永久升级调用它。
- Payment Request API,流程中负责购买的那一半
- Trusted Web Activity(TWA):PWA 进入 Play 商店,该 API 所要求的容器
- 为 PWA 创造收入
- Digital Goods API: getDigitalGoodsService() method(wicg.github.io)
- Receive Payments via Google Play Billing with the Digital Goods API and the Payment Request API(developer.chrome.com)
- Chrome Platform Status: Digital Goods API(chromestatus.com)
规范
| 规范 | 状态 |
|---|---|
| Digital Goods API: getDigitalGoodsService() method | WICG 草案 |