1. 使用示例
注意:本节为非规范性内容。
1.1. 获取服务实例
window.getDigitalGoodsService(),
该方法
可能仅在特定上下文下可用(如 HTTPS、应用、浏览器、操作系统等)。如果可用,可以传入服务提供商 URL 调用此方法。该方法
返回一个 promise,如果指定的服务提供商不可用则被拒绝。
if ( window. getDigitalGoodsService=== undefined ) { // 当前上下文不支持数字商品 API。 return ; } try { const digitalGoodsService= await window. getDigitalGoodsService( "https://example.com/billing" ); // 在此使用该服务。 ... } catch ( error) { // 我们的首选服务提供商不可用。 // 使用普通的网页支付流程。 console. error( "Failed to get service:" , error. message); return ; }
1.2. 查询商品详情
const details= await digitalGoodsService. getDetails([ 'shiny_sword' , 'gem' , 'monthly_subscription' ]); for ( itemof details) { const priceStr= new Intl. NumberFormat( locale, { style: 'currency' , currency: item. price. currency} ). format( item. price. value); AddShopMenuItem( item. itemId, item. title, priceStr, item. description); }
getDetails()
方法返回服务端关于给定商品集合的详情信息,目的是在菜单中展示给用户,让他们可以在无需实际进入购买流程前,先看到所有可购买项及其价格。
返回的 ItemDetails
序列可以是任意顺序,并且如果某商品在服务端不存在(即输入和输出不一定一一对应),也可能不会包含该商品。
商品 ID 是一个字符串,表示商品在商店服务器的主键。没有函数能获取商品 ID 列表,这些 ID 需硬编码在客户端代码中,或从开发者自己的服务器拉取。
商品定价为 PaymentCurrencyAmount
,包含当前用户地区与币种的价格设计,应使用
Intl.NumberFormat 进行本地化格式化,如上示例。
关于 ItemDetails
对象各字段的详细介绍,请参阅下方 [ItemDetails 字典] 部分。
1.3. 使用 Payment Request API 进行购买
const details= await digitalGoodsService. getDetails([ 'monthly_subscription' ]); const item= details[ 0 ]; new PaymentRequest( [{ supportedMethods: 'https://example.com/billing' , data: { itemId: item. itemId}}]);
购买流程本身使用 Payment Request API。
这里只展示关键部分,未包含整个支付请求,但注意:用户选择购买的商品 ID 可以随特定支付方式,通过 methodData 条目的 data
字段传递给服务商。
1.4. 检查现有购买
purchases= await digitalGoodsService. listPurchases(); for ( pof purchases) { VerifyOnBackendAndGrantEntitlement( p. itemId, p. purchaseToken); }
listPurchases()
方法使客户端可以获取用户当前拥有或已购买的商品列表。常用于检查权益(如订阅/促销码/永久升级等是否激活)或在网络中断时恢复购买(如商品已购但尚未确认给后端)。返回的是商品 ID
和购买令牌,通常需通过开发者-服务商 API 校验后再授予权益。
1.5. 查询历史购买
const purchaseHistory= await digitalGoodsService. listPurchaseHistory(); for ( pof purchaseHistory) { DisplayPreviousPurchase( p. itemId); }
listPurchaseHistory()
方法可获取用户所有购买过的每种商品的最新记录,包含已过期或已消耗的商品。有些商店可能不保留历史,返回内容可能与 listPurchases()
相同。
1.6. 消耗购买记录
digitalGoodsService. consume( purchaseToken);
设计为可多次购买的商品,通常在再次购买前需先标记为“已消耗”。比如:游戏内道具(如临时增益)。可以使用 consume()
方法实现。
如条件允许,更建议使用开发者-服务商 API 直接消耗商品,以确保消耗的可验证性。
1.7. 与子域 iframe 搭配使用
< iframe src= "https://sub.origin.example" allow= "payment" > < /iframe>
如需允许子域 iframe 调用数字商品 API,可在 iframe 元素上指定 allow 属性,并包含 "payment" 关键字。跨域 iframe
不可调用数字商品 API。更多细节见 Permissions Policy
规范。
2. API 定义
2.1. 对 Window 接口的扩展
partial interface Window { [SecureContext ]Promise <DigitalGoodsService >getDigitalGoodsService (DOMString ); };serviceProvider
Window
对象可以暴露 getDigitalGoodsService()
方法。
不支持数字商品功能的用户代理不应在 Window
接口上暴露 getDigitalGoodsService()
方法。
注意: 上述说明是为了便于特性检测。如果 getDigitalGoodsService()
存在,可合理认为至少有一个服务提供商可用。
2.1.1. getDigitalGoodsService() 方法
注意: getDigitalGoodsService()
方法用于检测指定的 serviceProvider
是否支持
当前上下文。方法返回一个 Promise,如果支持则解析为 DigitalGoodsService
对象,否则如不支持或发生错误则拒绝。serviceProvider
通常为
基于 URL 的支付方式标识符。
getDigitalGoodsService(serviceProvider)
方法时,执行以下步骤:
-
令 document 为 当前设置对象的 相关全局对象的 关联
Document。 -
如果 document 不是 完全激活状态,则返回一个被拒绝的 promise,异常类型为
"InvalidStateError"DOMException。 -
如果 document 的 源与顶级源不同源,则返回一个被拒绝的 promise,异常类型为
"NotAllowedError"DOMException。 -
如果 document 无 允许使用 “payment” 权限,则返回一个被拒绝的 promise,异常类型为
"NotAllowedError"DOMException。 -
如果 serviceProvider 是 undefined 或 null 或空字符串,则返回一个被拒绝的 promise,异常类型为
TypeError。 -
令 result 为对 serviceProvider 和 document 执行 可创建数字商品服务算法 的结果。
-
如果 result 为 false,返回一个被拒绝的 promise,异常类型为
OperationError。 -
返回一个被解析的 promise,其值为新建的
DigitalGoodsService。
2.1.2. 可创建数字商品服务算法
-
用户代理 可以基于 serviceProvider、document 或外部因素返回 true 或返回 false。
注意: 允许用户代理针对不同服务商和不同上下文支持不同能力。
2.2. DigitalGoodsService 接口
[Exposed =Window ,SecureContext ]interface {DigitalGoodsService Promise <sequence <ItemDetails >>getDetails (sequence <DOMString >);itemIds Promise <sequence <PurchaseDetails >>listPurchases ();Promise <sequence <PurchaseDetails >>listPurchaseHistory ();Promise <undefined >consume (DOMString ); };purchaseToken dictionary {ItemDetails required DOMString ;itemId required DOMString ;title required PaymentCurrencyAmount ;price ItemType ;type DOMString ;description sequence <DOMString >;iconURLs DOMString ;subscriptionPeriod DOMString ;freeTrialPeriod PaymentCurrencyAmount ;introductoryPrice DOMString ; [introductoryPricePeriod EnforceRange ]unsigned long long ; };introductoryPriceCycles enum {ItemType ,"product" , };"subscription" dictionary {PurchaseDetails required DOMString ;itemId required DOMString ; };purchaseToken
2.2.1. getDetails() 方法
getDetails(itemIds)
方法时,执行下列步骤:
-
如果 itemIds 为空,则返回一个被拒绝的 promise,异常类型为
TypeError。 -
令 result 为从数字商品服务请求给定 itemIds 信息的结果。
注意: 允许用户代理针对不同数字商品服务提供商做不同的行为以支持多家服务商。
-
如果 result 为错误,则返回一个被拒绝的 promise,异常类型为
OperationError。 -
对于 result 中的每个 itemDetails:
-
itemDetails.itemId 不应为空字符串。
-
itemIds 应包含 itemDetails.itemId。
-
itemDetails.title 不应为空字符串。
-
itemDetails.price 必须为 规范 PaymentCurrencyAmount。
-
如有,itemDetails.subscriptionPeriod 必须为 iso-8601 持续时间。
-
如有,itemDetails.freeTrialPeriod 必须为 iso-8601 持续时间。
-
如有,itemDetails.introductoryPrice 必须为 规范 PaymentCurrencyAmount。
-
如有,itemDetails.introductoryPricePeriod 必须为 iso-8601 持续时间。
-
-
返回一个被解析的 promise,其值为 result。
注意: 并不要求 result 项的顺序与 itemIds 顺序一致。这样可允许缺失或无效项被跳过。
2.2.2. listPurchases() 方法
listPurchases() 方法时,执行下列步骤:
-
令 result 为从数字商品服务请求关于用户已购商品信息的结果。
注意: 允许用户代理针对不同数字商品服务提供商做不同行为以支持多家服务商。
-
如果 result 为错误,则返回一个被拒绝的 promise,异常类型为
OperationError。 -
对于 result 中的每个 itemDetails:
-
itemDetails.itemId 不应为空字符串。
-
itemDetails.purchaseToken 不应为空字符串。
-
-
返回一个被解析的 promise,其值为 result。
2.2.3. listPurchaseHistory() 方法
listPurchaseHistory()
方法时,执行下列步骤:
-
令 result 为请求用户购买过的每种商品类型最新购买信息的结果。
-
如果 result 为错误,则返回一个被拒绝的 promise,异常类型为
OperationError。 -
对于 result 中的每个 itemDetails:
-
itemDetails.itemId 不应为空字符串。
-
itemDetails.purchaseToken 不应为空字符串。
-
-
返回一个被解析的 promise,其值为 result。
2.2.4. consume() 方法
注意: 此处的“消耗”指用掉一次购买。消耗后用户将不再获得该权益。
consume(purchaseToken)
方法时,执行下列步骤:
-
若 purchaseToken 为空字符串,则返回一个被拒绝的 promise,异常类型为
TypeError。 -
令 result 为请求数字商品服务将 purchaseToken 标记为已消耗的结果。
注意: 允许用户代理根据实际服务商进行差异化实现。
-
如果 result 为错误,则返回一个被拒绝的 promise,异常类型为
OperationError。 -
返回一个被解析的 promise,其值为
undefined。
2.3. ItemDetails 字典
本节为非规范性内容。
ItemDetails
字典用于描述来自
serviceProvider
的数字商品详情。
-
itemId标识当前应用清单中的唯一数字商品。应用内期望唯一,但不同 app 可重复。 -
title为用于展示给用户的商品名称,期望已被serviceProvider本地化。 -
price为商品价格,可用于展示,见上文 § 1.2 查询商品详情 示例。期望已由serviceProvider本地化。 -
description商品完整描述,期望已由serviceProvider本地化。 -
iconURLs商品相关的图标列表,用于视觉描述。 -
subscriptionPeriod订阅有效期(ISO 8601 时长), 此期间用户享有相应权益,期满后需续费或失去权益(与 API 本身无关,仅作标记)。仅针对订阅,非一次性商品。 -
freeTrialPeriod免费试用期(ISO 8601 时长), 期间用户免费享有权益,期满后需付费或权益失效(与 API 本身无关,仅作标记)。仅针对订阅,非一次性商品。 -
introductoryPrice商品初始价格,格式可参考前述 § 1.2 查询商品详情 示例,期望由serviceProvider本地化。 -
introductoryPricePeriod初始价格的有效期(ISO 8601 时长)。 在此期间商品按introductoryPrice售卖, 期满后恢复为price。 -
introductoryPriceCycles指定introductoryPrice有效的订阅周期数。
2.4. PurchaseDetails 字典
本节为非规范性内容。
PurchaseDetails
字典表示用户曾购买过的来自
serviceProvider
的商品。
-
itemId标识当前应用清单中的商品,期望唯一但 app 间可重复,并等同于itemId,与getDetails()返回值一致。 -
purchaseToken为服务商生成的用于标识购买行为的任意令牌, 可用于通过与服务商的服务端通信验证购买(不属于 API 职责)。
3. 权限策略集成
本规范定义了一个由字符串 "payment" 标识的 受策略控制的特性。
其 默认允许列表为 'self'。
注意: 文档
的 权限策略 决定本文件中任何内容是否允许获取 DigitalGoodsService
实例。若某文档中被禁用,则该文档内所有内容都不允许
调用 getDigitalGoodsService()
方法(尝试调用会抛出异常)。
4. 补充定义
"payment" 权限是 [permissions-policy] 特性, 参见 payment-request 规范 定义。
规范化的
PaymentCurrencyAmount
是指可以经过
PaymentCurrencyAmount
的 amount 字段在
校验并规范化金额
步骤处理后,不会抛出任何错误且不会被更改的值。
iso-8601 是一种日期时间格式标准。