1. Usage Examples
Note: This section is non-normative.
1.1. Getting a service instance
window.getDigitalGoodsService()
, which
might only be available in certain contexts (eg. HTTPS, app, browser, OS). If
available, the method can be called with a service provider URL. The method
returns a promise that is rejected if the given service provider is not
available.
if ( window. getDigitalGoodsService=== undefined ) { // Digital Goods API is not supported in this context. return ; } try { const digitalGoodsService= await window. getDigitalGoodsService( "https://example.com/billing" ); // Use the service here. ... } catch ( error) { // Our preferred service provider is not available. // Use a normal web-based payment flow. console. error( "Failed to get service:" , error. message); return ; }
1.2. Querying item details
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); }
The getDetails()
method returns server-side details
about a given set of items, intended to be displayed to the user in a menu, so
that they can see the available purchase options and prices without having to
go through a purchase flow.
The returned ItemDetails
sequence can be in any order and might not
include an item if it doesn’t exist on the server (i.e. there is not a 1:1
correspondence between the input list and output).
The item ID is a string representing the primary key of the items, configured in the store server. There is no function to get a list of item IDs; those have to be hard-coded in the client code or fetched from the developer’s own server.
The item’s price is a PaymentCurrencyAmount
containing the current price
of the item in the user’s current region and currency. It is designed to be
formatted for the user’s current locale using Intl.NumberFormat, as shown above.
For more information on the fields in the ItemDetails
object, refer to the
[ItemDetails dictionary] section below.
1.3. Purchase using 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}}]);
The purchase flow itself uses the Payment Request API. We
don’t show the
full payment request code here, but note that the item ID for any items the
user chooses to purchase can be sent in the data
field of a methodData
entry
for the given payment method, in a manner specific to the store.
1.4. Checking existing purchases
purchases= await digitalGoodsService. listPurchases(); for ( pof purchases) { VerifyOnBackendAndGrantEntitlement( p. itemId, p. purchaseToken); }
The listPurchases()
method allows a client to get a
list of items that are
currently owned or purchased by the user. This might be necessary to check for
entitlements (e.g. whether a subscription, promotional code, or permanent
upgrade is active) or to recover from network interruptions during a purchase
(e.g. item is purchased but not yet confirmed with a backend). The method
returns item IDs and purchase tokens, which would typically be verified using
a direct developer-to-provider API before granting entitlements.
1.5. Checking past purchases
const purchaseHistory= await digitalGoodsService. listPurchaseHistory(); for ( pof purchaseHistory) { DisplayPreviousPurchase( p. itemId); }
The listPurchaseHistory()
method allows a client
to list the latest purchases for each item type ever purchased by the
user. Can include expired or consumed purchases. Some stores might not keep
such history, in which case it would return the same data as the listPurchases()
method.
1.6. Consuming a purchase
digitalGoodsService. consume( purchaseToken);
Purchases that are designed to be purchased multiple times usually need to be
marked as "consumed" before they can be purchased again by the user. An
example of a consumable purchase is an in-game powerup that makes the player
stronger for a short period of time. This can be done with the consume()
method.
It is preferable to use a direct developer-to-provider API to consume purchases, if one is available, in order to more verifiably ensure that a purchase was used up.
1.7. Use with subdomain iframes
< iframe src= "https://sub.origin.example" allow= "payment" > < /iframe>
To indicate that a subdomain iframe is allowed to invoke the Digital Goods
API, the allow
attribute along with the "payment"
keyword can be specified on the iframe element. Cross-origin iframes cannot
invoke the Digital Goods API. The Permissions Policy specification provides further
details and examples.
2. API definition
2.1. Extensions to the Window interface
partial interface Window { [SecureContext ]Promise <DigitalGoodsService >getDigitalGoodsService (DOMString ); };
serviceProvider
The Window
object MAY expose a getDigitalGoodsService()
method.
User agents that do not support Digital Goods SHOULD NOT expose getDigitalGoodsService()
on the Window
interface.
Note: The above statement is designed to permit feature detection. If getDigitalGoodsService()
is present, there is a reasonable
expectation that it will work with at least one service provider.
2.1.1. getDigitalGoodsService() method
Note: The getDigitalGoodsService()
method is called to determine
whether the given serviceProvider
is supported
in the current context. The method returns a Promise that will be resolved with
a DigitalGoodsService
object if the serviceProvider is supported, or
rejected with an exception if the serviceProvider is unsupported or any error
occurs. The serviceProvider
is usually a url-based payment method identifier.
getDigitalGoodsService(serviceProvider)
method is called, run the following steps:
-
Let document be the current settings object's relevant global object's associated
Document
. -
If document is not fully active, then return a promise rejected with an
"InvalidStateError"
DOMException
. -
If document’s origin is not same origin with the top-level origin return a promise rejected with a
"NotAllowedError"
DOMException
. -
If document is not allowed to use the "payment" permission return a promise rejected with a
"NotAllowedError"
DOMException
. -
If serviceProvider is undefined or null or the empty string return a promise rejected with a
TypeError
. -
Let result be the result of performing the can make digital goods service algorithm given serviceProvider and document.
-
If result is false return a promise rejected with an
OperationError
. -
Return a promise resolved with a new
DigitalGoodsService
.
2.1.2. Can make digital goods service algorithm
-
The user agent MAY return true or return false based on the serviceProvider or document or external factors.
Note: This allows for user agents to support different service providers in different contexts.
2.2. DigitalGoodsService interface
[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() method
getDetails(itemIds)
method is called, run the following steps:
-
If itemIds is empty, then return a promise rejected with a
TypeError
. -
Let result be the result of requesting information about the given itemIds from the digital goods service.
Note: This allows for different digital goods service providers to be supported by provider-specific behavior in the user agent.
-
If result is an error, then return a promise rejected with an
OperationError
. -
For each itemDetails in result:
-
itemDetails.itemId SHOULD NOT be the empty string.
-
itemIds SHOULD contain itemDetails.itemId.
-
itemDetails.title SHOULD NOT be the empty string.
-
itemDetails.price MUST be a canonical PaymentCurrencyAmount.
-
If present, itemDetails.subscriptionPeriod MUST be be a iso-8601 duration.
-
If present, itemDetails.freeTrialPeriod MUST be be a iso-8601 duration.
-
If present, itemDetails.introductoryPrice MUST be a canonical PaymentCurrencyAmount.
-
If present, itemDetails.introductoryPricePeriod MUST be be a iso-8601 duration.
-
-
Return a promise resolved with result.
Note: There is no requirement that the ordering of items in result matches the ordering of items in itemIds. This is to allow for missing or invalid items to be skipped in the output list.
2.2.2. listPurchases() method
listPurchases()
method is
called, run the following steps:
-
Let result be the result of requesting information about the user’s purchases from the digital goods service.
Note: This allows for different digital goods service providers to be supported by provider-specific behavior in the user agent.
-
If result is an error, then return a promise rejected with an
OperationError
. -
For each itemDetails in result:
-
itemDetails.itemId SHOULD NOT be the empty string.
-
itemDetails.purchaseToken SHOULD NOT be the empty string.
-
-
Return a promise resolved with result.
2.2.3. listPurchaseHistory() method
listPurchaseHistory()
method is
called, run the following steps:
-
Let result be the result of requesting information about the latest purchases for each item type ever purchased by the user.
-
If result is an error, then return a promise rejected with an
OperationError
. -
For each itemDetails in result:
-
itemDetails.itemId SHOULD NOT be the empty string.
-
itemDetails.purchaseToken SHOULD NOT be the empty string.
-
-
Return a promise resolved with result.
2.2.4. consume() method
Note: Consume in this context means to use up a purchase. The user is expected to no longer be entitled to the purchase after it is consumed.
consume(purchaseToken)
method is called, run the following steps:
-
If purchaseToken is the empty string, then return a promise rejected with a
TypeError
. -
Let result be the result of requesting the digital goods service to record purchaseToken as consumed.
Note: This allows for different digital goods service providers to be supported by provider-specific behavior in the user agent.
-
If result is an error, then return a promise rejected with an
OperationError
. -
Return a promise resolved with
undefined
.
2.3. ItemDetails dictionary
This section is non-normative.
ItemDetails
dictionary represents information about a digital item from
a serviceProvider
.
-
itemId
identifies a particular digital item in the current app’s inventory. It is expected to be unique within the app but might not be unique across all apps. -
title
is the name of the item to display to the user. It is expected to have been localized for the user by theserviceProvider
. -
price
is the price of the item and is intended to be able to be formatted as shown in the § 1.2 Querying item details example above for display to the user. It is expected to have been localized for the user by theserviceProvider
. -
description
is the full description of the item to display to the user. It is expected to have been localized for the user by theserviceProvider
. -
iconURLs
is a list of icons that provide a visual description of the item. -
subscriptionPeriod
is the time period, specified as an ISO 8601 duration, in which the item grants some entitlement to the user. After this period the entitlement is expected to be renewed or lost (this is not controlled through the Digital Goods API). This field is only expected to be set for subscriptions and not for one-off purchases. -
freeTrialPeriod
is the time period, specified as an ISO 8601 duration, in which the item grants some entitlement to the user without costing anything. After this period the entitlement is expected to be paid or lost (this is not controlled through the Digital Goods API). This field is only expected to be set for subscriptions and not for one-off purchases. -
introductoryPrice
is the initial price of the item and is intended to be able to be formatted as shown in the § 1.2 Querying item details example above for display to the user. It is expected to have been localized for the user by theserviceProvider
. -
introductoryPricePeriod
is the time period, specified as an ISO 8601 duration, in which the item costs theintroductoryPrice
. After this period the item is epected to cost theprice
. -
introductoryPriceCycles
is the number of subscription cycles during which theintroductoryPrice
is effective.
2.4. PurchaseDetails dictionary
This section is non-normative.
PurchaseDetails
dictionary represents information about a digital item
from a serviceProvider
which the user has purchased at some point.
-
itemId
identifies a particular digital item in the current app’s inventory. It is expected to be unique within the app but might not be unique across all apps. It is expected to be equivalent to anitemId
as used in thegetDetails()
method. -
purchaseToken
is an abitrary token representing a purchase as generated by theserviceProvider
. It is intended to be able to be used to verify the purchase by contacting the service provider directly (not part of the Digital Goods API).
3. Permissions Policy integration
This specification defines a policy-controlled feature identified
by the string "payment". Its default
allowlist is 'self
'.
Note: A document’s permissions policy determines
whether any content in that document is allowed to get DigitalGoodsService
instances. If disabled in any document, no content
in the document will be allowed to use the getDigitalGoodsService()
method (trying to call the method will throw).
4. Additional Definitions
The "payment" permission is a [permissions-policy] feature defined in the payment-request spec.A canonical PaymentCurrencyAmount
is a PaymentCurrencyAmount
amount
that can be run through the steps to check and canonicalize amount without throwing any errors or being altered.
iso-8601 is a standard for date and time representations.