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.6.
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 responsible 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
[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
-
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.