This specification standardizes an API to allow merchants (i.e. web sites selling physical or digital goods) to utilize one or more payment methods with minimal integration. User agents (e.g., browsers) facilitate the payment flow between merchant and user.
The working group maintains a list of all bug reports that the group has not yet addressed. Pull requests with proposed specification text for outstanding issues are strongly encouraged.
The working group will demonstrate implementation experience by producing an implementation report. The report will show two or more independent implementations passing each mandatory test in the test suite (i.e., each test corresponds to a MUST requirement of the specification).
There has been no change in dependencies on other workings groups during the development of this specification.
Substantive changes to the Payment Request API since the 9 July 2018 version are as follows. The complete list of changes, including all editorial changes, is viewable in the commit history.
languageCode
and regionCode
from PaymentAddress.
currencySystem
.
This specification describes an API that allows user agents (e.g., browsers) to act as an intermediary between three parties in a transaction:
The details of how to fulfill a payment request for a given payment method is an implementation detail of a payment handler. Concretely, each payment handler defines:
BasicCardResponse
dictionary of [[?payment-method-basic-card]].
Steps that describe how to handle the user changing payment method or monetary instrument (e.g., from a debit card to a credit card) that results in a dictionary or object or null.
A payment handler that defines steps for when a user changes payment method MUST redact the address line, organization, phone number, and recipient from any PaymentAddress included in the PaymentMethodChangeEvent's methodDetails attribute.
This API also enables web sites to take advantage of more secure payment schemes (e.g., tokenization and system-level authentication) that are not possible with standard JavaScript libraries. This has the potential to reduce liability for the merchant and helps protect sensitive user information.
The following are out of scope for this specification:
In order to use the API, the developer needs to provide and keep track of a number of key pieces of information. These bits of information are passed to the PaymentRequest constructor as arguments, and subsequently used to update the payment request being displayed to the user. Namely, these bits of information are:
Once a PaymentRequest is constructed, it's presented to the end user via the show() method. The show() returns a promise that, once the user confirms request for payment, results in a PaymentResponse.
When constructing a new PaymentRequest, a merchant uses the first argument (methodData) to list the different ways a user can pay for things (e.g., credit cards, Apple Pay, Google Pay, etc.). More specifically, the methodData sequence contains PaymentMethodData dictionaries containing the payment method identifiers for the payment methods that the merchant accepts and any associated payment method specific data (e.g., which credit card networks are supported).
const methodData = [ { supportedMethods: "basic-card", data: { supportedNetworks: ["visa", "mastercard"], }, }, { supportedMethods: "https://example.com/bobpay", data: { merchantIdentifier: "XXXX", bobPaySpecificField: true, }, }, ];
When constructing a new PaymentRequest, a merchant uses the second argument of the constructor (details) to provide the details of the transaction that the user is being asked to complete. This includes the total of the order and, optionally, some line items that can provide a detailed breakdown of what is being paid for.
const details = { id: "super-store-order-123-12312", displayItems: [ { label: "Sub-total", amount: { currency: "USD", value: "55.00" }, }, { label: "Sales Tax", amount: { currency: "USD", value: "5.00" }, type: "tax" }, ], total: { label: "Total due", // The total is USD$65.00 here because we need to // add shipping (below). The selected shipping // costs USD$5.00. amount: { currency: "USD", value: "65.00" }, }, };
Here we see an example of how to add two shipping options to the details.
const shippingOptions = [ { id: "standard", label: "🚛 Ground Shipping (2 days)", amount: { currency: "USD", value: "5.00" }, selected: true, }, { id: "drone", label: "🚀 Drone Express (2 hours)", amount: { currency: "USD", value: "25.00" } }, ]; Object.assign(details, { shippingOptions });
Here we see how to add a processing fee for using a card on a particular network. Notice that it requires recalculating the total.
// Certain cards incurs a $3.00 processing fee. const cardFee = { label: "Card processing fee", amount: { currency: "USD", value: "3.00" }, }; // Modifiers apply when the user chooses to pay with // a card. const modifiers = [ { additionalDisplayItems: [cardFee], supportedMethods: "basic-card", total: { label: "Total due", amount: { currency: "USD", value: "68.00" }, }, data: { supportedNetworks: networks, }, }, ]; Object.assign(details, { modifiers });
Some financial transactions require a user to provide specific information in order for a merchant to fulfill a purchase (e.g., the user's shipping address, in case a physical good needs to be shipped). To request this information, a merchant can pass a third optional argument (options) to the PaymentRequest constructor indicating what information they require. When the payment request is shown, the user agent will request this information from the end user and return it to the merchant when the user accepts the payment request.
const options = { requestPayerEmail: false, requestPayerName: true, requestPayerPhone: false, requestShipping: true, }
PaymentRequest
Having gathered all the prerequisite bits of information, we can now construct a PaymentRequest and request that the browser present it to the user:
async function doPaymentRequest() { try { const request = new PaymentRequest(methodData, details, options); // See below for a detailed example of handling these events request.onshippingaddresschange = ev => ev.updateWith(details); request.onshippingoptionchange = ev => ev.updateWith(details); const response = await request.show(); await validateResponse(response); } catch (err) { // AbortError, SecurityError console.error(err); } } async function validateResponse(response) { try { const errors = await checkAllValuesAreGood(response); if (errors.length) { await request.retry(errors); return validateResponse(response); } await response.complete("success"); } catch (err) { // Something went wrong... await response.complete("fail"); } } doPaymentRequest();
Prior to the user accepting to make payment, the site is given an opportunity to update the payment request in response to user input. This can include, for example, providing additional shipping options (or modifying their cost), removing items that cannot ship to a particular address, etc.
const request = new PaymentRequest(methodData, details, options); // Async update to details request.onshippingaddresschange = ev => { ev.updateWith(checkShipping(request)); }; // Sync update to the total request.onshippingoptionchange = ev => { // selected shipping option const { shippingOption } = request; const newTotal = { currency: "USD", label: "Total due", value: calculateNewTotal(shippingOption), }; ev.updateWith({ total: newTotal }); }; async function checkShipping(request) { try { const json = request.shippingAddress.toJSON(); await ensureCanShipTo(json); const { shippingOptions, total } = await calculateShipping(json); return { shippingOptions, total }; } catch (err) { return { error: `Sorry! we can't ship to your address.` }; } }
A developer can use the shippingAddressErrors member of the PaymentDetailsUpdate dictionary to indicate that there are validation errors with specific attributes of a PaymentAddress. The shippingAddressErrors member is a AddressErrors dictionary, whose members specifically demarcate the fields of a physical address that are erroneous while also providing helpful error messages to be displayed to the end user.
request.onshippingaddresschange = ev => { ev.updateWith(validateAddress(request.shippingAddress)); }; function validateAddress(shippingAddress) { const error = "Can't ship to this address."; const shippingAddressErrors = { city: "FarmVille is not a real place.", postalCode: "Unknown postal code for your country.", }; // Empty shippingOptions implies that we can't ship // to this address. const shippingOptions = []; return { error, shippingAddressErrors, shippingOptions }; }
It's expected that data in a PaymentResponse will be POSTed back to a server for processing. To make this as easy as possible, PaymentResponse provides a toJSON() method that serializes the object directly into JSON. This makes it trivial to POST the resulting JSON back to a server using the Fetch API:
async function doPaymentRequest() { const payRequest = new PaymentRequest(methodData, details, options); const payResponse = await payRequest.show(); let result = ""; try { const httpResponse = await fetch("/process-payment", { method: "POST", headers: { "Content-Type": "application/json" }, body: payResponse.toJSON(), }); result = httpResponse.ok ? "success" : "fail"; } catch (err) { console.error(err); result = "fail"; } await payResponse.complete(result); } doPaymentRequest();
[Constructor(sequence<PaymentMethodData> methodData, PaymentDetailsInit details, optional PaymentOptions options), SecureContext, Exposed=Window] interface PaymentRequest : EventTarget { [NewObject] Promise<PaymentResponse> show(optional Promise<PaymentDetailsUpdate> detailsPromise); [NewObject] Promise<void> abort(); [NewObject] Promise<boolean> canMakePayment(); readonly attribute DOMString id; readonly attribute PaymentAddress? shippingAddress; readonly attribute DOMString? shippingOption; readonly attribute PaymentShippingType? shippingType; attribute EventHandler onmerchantvalidation; attribute EventHandler onshippingaddresschange; attribute EventHandler onshippingoptionchange; attribute EventHandler onpaymentmethodchange; };
A developer creates a PaymentRequest to make a payment request. This is typically associated with the user initiating a payment process (e.g., by activating a "Buy," "Purchase," or "Checkout" button on a web site, selecting a "Power Up" in an interactive game, or paying at a kiosk in a parking structure). The PaymentRequest allows developers to exchange information with the user agent while the user is providing input (up to the point of user approval or denial of the payment request).
The shippingAddress, shippingOption, and shippingType attributes are populated during processing if the requestShipping member is set.
A request's payment-relevant browsing context is that PaymentRequest's relevant global object's browsing context's top-level browsing context. Every payment-relevant browsing context has a payment request is showing boolean, which prevents showing more than one payment UI at a time.
The payment request is showing boolean simply prevents more than one payment UI being shown in a single browser tab. However, a payment handler can restrict the user agent to showing only one payment UI across all browser windows and tabs. Other payment handlers might allow showing a payment UI across disparate browser tabs.
The PaymentRequest is constructed using the supplied sequence of PaymentMethodData methodData including any payment method specific data, the PaymentDetailsInit details, and the PaymentOptions options.
The PaymentRequest(methodData,
details, options)
constructor MUST
act as follows:
Convert object to an IDL value of the type specified by the specification that defines the paymentMethod.supportedMethods (e.g., a BasicCardRequest in the case of [[?payment-method-basic-card]]). Rethrow any exceptions.
This step assures that any IDL type conversion errors are caught as early as possible.
sequence
<PaymentShippingOption>.
sequence
<PaymentDetailsModifier>.
When getting, the id attribute returns this PaymentRequest's [[\details]].id.
The show() method is called when a developer wants to begin user interaction for the payment request. The show() method returns a Promise that will be resolved when the user accepts the payment request. Some kind of user interface will be presented to the user to facilitate the payment request after the show() method returns.
Each payment handler controls what happens when multiple browsing context simultaneously call the show() method. For instance, some payment handlers will allow multiple payment UIs to be shown in different browser tabs/windows. Other payment handlers might only allow a single payment UI to be shown for the entire user agent.
The show(optional detailsPromise)
method MUST
act as follows:
Optionally, if the user agent wishes to disallow the call to show() to protect the user, then return a promise rejected with a "SecurityError" DOMException. For example, the user agent may limit the rate at which a page can call show(), as described in section .
Optionally:
This allows the user agent to act as if the user had immediately aborted the payment request, at its discretion. For example, in "private browsing" modes or similar, user agents might take advantage of this step.
PaymentRequest
's details
algorithm with detailsPromise, request,
and null.
Based on how the detailsPromise settles, the
update a PaymentRequest
's details
algorithm determines how the payment UI behaves. That is,
upon rejection of the detailsPromise, the
payment request aborts. Otherwise, upon fulfillment
detailsPromise, the user agent re-enables the
payment request UI and the payment flow can continue.
Pass the converted second element in the paymentMethod tuple and modifiers. Optionally, the user agent SHOULD send the appropriate data from request to the user-selected payment handler in order to guide the user through the payment process. This includes the various attributes and other internal slots of request (some MAY be excluded for privacy reasons where appropriate).
Handling of multiple applicable modifiers in the [[\serializedModifierData]] internal slot is payment handler specific and beyond the scope of this specification. Nevertheless, it is RECOMMENDED that payment handlers use a "last one wins" approach with items in the [[\serializedModifierData]] list: that is to say, an item at the end of the list always takes precedence over any item at the beginning of the list (see example below).
The acceptPromise will later be resolved or rejected by either the user accepts the payment request algorithm or the user aborts the payment request algorithm, which are triggered through interaction with the user interface.
If document stops being fully active while the user interface is being shown, or no longer is by the time this step is reached, then:
The abort() method is called if a developer wishes to tell the user agent to abort the payment request and to tear down any user interface that might be shown. The abort() can only be called after the show() method has been called (see states) and before this instance's [[\acceptPromise]] has been resolved. For example, developers might choose to do this if the goods they are selling are only available for a limited amount of time. If the user does not accept the payment request within the allowed time period, then the request will be aborted.
A user agent might not always be able to abort a request. For example, if the user agent has delegated responsibility for the request to another app. In this situation, abort() will reject the returned Promise.
See also the algorithm when the user aborts the payment request.
The abort() method MUST act as follows:
The canMakePayment() method can be used by the developer to determine if the PaymentRequest object can be used to make a payment, before they call show(). It returns a Promise that will be fulfilled with true if the user agent supports any of the desired payment methods supplied to the PaymentRequest constructor, and false if none are supported. If the method is called too often, the user agent might instead return a promise rejected with a "NotAllowedError" DOMException, at its discretion.
The canMakePayment() method MUST act as follows:
A PaymentRequest's shippingAddress attribute is populated when the user provides a shipping address. It is null by default. When a user provides a shipping address, the shipping address changed algorithm runs.
A PaymentRequest's shippingType attribute is the type of shipping used to fulfill the transaction. Its value is either a PaymentShippingType enum value, or null if none is provided by the developer during construction (see PaymentOptions's shippingType member).
A PaymentRequest's onmerchantvalidation attribute is an EventHandler for a MerchantValidationEvent named "merchantvalidation".
A PaymentRequest's onshippingaddresschange attribute is an EventHandler for a PaymentRequestUpdateEvent named shippingaddresschange.
A PaymentRequest's shippingOption attribute is populated when the user chooses a shipping option. It is null by default. When a user chooses a shipping option, the shipping option changed algorithm runs.
A PaymentRequest's onshippingoptionchange attribute is an EventHandler for a PaymentRequestUpdateEvent named shippingoptionchange.
A PaymentRequest's onpaymentmethodchange attribute is an EventHandler for a PaymentMethodChangeEvent named "paymentmethodchange".
Instances of PaymentRequest are created with the internal slots in the following table:
Internal Slot | Description (non-normative) |
---|---|
[[\serializedMethodData]] |
The methodData supplied to the constructor, but
represented as tuples containing supported methods and a string
or null for data (instead of the original object form).
|
[[\serializedModifierData]] | A list containing the serialized string form of each data member for each corresponding item in the sequence [[\details]].modifier, or null if no such member was present. |
[[\details]] | The current PaymentDetailsBase for the payment request initially supplied to the constructor and then updated with calls to updateWith(). Note that all data members of PaymentDetailsModifier instances contained in the modifiers member will be removed, as they are instead stored in serialized form in the [[\serializedModifierData]] internal slot. |
[[\options]] | The PaymentOptions supplied to the constructor. |
[[\state]] |
The current state of the payment request, which transitions from:
The state transitions are illustrated in the figure below: |
[[\updating]] | True if there is a pending updateWith() call to update the payment request and false otherwise. |
[[\acceptPromise]] | The pending Promise created during show that will be resolved if the user accepts the payment request. |
[[\response]] | Null, or the PaymentResponse instantiated by this PaymentRequest. |
dictionary PaymentMethodData { required DOMString supportedMethods; object data; };
A PaymentMethodData dictionary is used to indicate a set of supported payment methods and any associated payment method specific data for those methods.
The value of supportedMethods
was changed from array to
string, but the name was left as a plural to maintain compatibility
with existing content on the Web.
dictionary PaymentCurrencyAmount { required DOMString currency; required DOMString value; };
A PaymentCurrencyAmount dictionary is used to supply monetary amounts.
An [[ISO4217]] well-formed 3-letter alphabetic code (i.e., the numeric codes are not supported). Their canonical form is upper case. However, the set of combinations of currency code for which localized currency symbols are available is implementation dependent. Where a localized currency symbol is not available, a user agent SHOULD use U+00A4 (¤) for formatting. User agents MAY format the display of the currency member to adhere to OS conventions (e.g., for localization purposes).
User agents implementing this specification enforce [[ISO4217]]'s 3-letter codes format via ECMAScript’s isWellFormedCurrencyCode abstract operation, which is invoked as part of the check and canonicalize amount algorithm. When a code does not adhere to the [[ISO4217]] defined format, a RangeError is thrown.
Current implementations will therefore allow the use of well-formed currency codes that are not part of the official [[ISO4217]] list (e.g., XBT, XRP, etc.). If the provided code is a currency that the browser knows how to display, then an implementation will generally display the appropriate currency symbol in the user interface (e.g., "USD" is shown as "$", "GBP" is "£", and the non-standard "XBT" could be shown as "Ƀ"). When a code cannot be matched, the specification recommends browsers show a scarab "¤".
Efforts are underway at ISO to account for digital currencies, which may result in an update to the [[ISO4217]] registry or an entirely new registry. The community expects this will resolve ambiguities that have crept in through the use of non-standard 3-letter codes; for example, does "BTC" refer to Bitcoin or to a future Bhutan currency? At the time of publication, it remains unclear what form this evolution will take, or even the time frame in which the work will be completed. The W3C Web Payments Working Group is liaising with ISO so that, in the future, revisions to this specification remain compatible with relevant ISO registries.
{ "currency": "USD", "value": "55.00" }
A JavaScript string is a valid decimal monetary value if it consists of the following code points in the given order:
^-?[0-9]+(\.[0-9]+)?$
To check and canonicalize amount given a PaymentCurrencyAmount amount, run the following steps:
To check and canonicalize total amount given a PaymentCurrencyAmount amount, run the following steps:
dictionary PaymentDetailsBase { sequence<PaymentItem> displayItems; sequence<PaymentShippingOption> shippingOptions; sequence<PaymentDetailsModifier> modifiers; };
A sequence containing the different shipping options for the user to choose from.
If an item in the sequence has the selected member set to true, then this is the shipping option that will be used by default and shippingOption will be set to the id of this option without running the shipping option changed algorithm. If more than one item in the sequence has selected set to true, then the user agent selects the last one in the sequence.
The shippingOptions member is only used if the PaymentRequest was constructed with PaymentOptions and requestShipping set to true.
dictionary PaymentDetailsInit : PaymentDetailsBase { DOMString id; required PaymentItem total; };
In addition to the members inherited from the PaymentDetailsBase dictionary, the following members are part of the PaymentDetailsInit dictionary:
dictionary PaymentDetailsUpdate : PaymentDetailsBase { DOMString error; PaymentItem total; AddressErrors shippingAddressErrors; PayerErrors payerErrors; object paymentMethodErrors; };
The PaymentDetailsUpdate dictionary is used to update the payment request using updateWith().
In addition to the members inherited from the PaymentDetailsBase dictionary, the following members are part of the PaymentDetailsUpdate dictionary:
Algorithms in this specification that accept a PaymentDetailsUpdate dictionary will throw if the total.amount.value is a negative number.
Payment method specific errors. See, for example,
[[?payment-method-basic-card]]'s BasicCardErrors
.
dictionary PaymentDetailsModifier { required DOMString supportedMethods; PaymentItem total; sequence<PaymentItem> additionalDisplayItems; object data; };
The PaymentDetailsModifier dictionary provides details that modify the PaymentDetailsBase based on a payment method identifier. It contains the following members:
It is the developer's responsibility to verify that the total amount is the sum of the displayItems and the additionalDisplayItems.
enum PaymentShippingType { "shipping", "delivery", "pickup" };
dictionary PaymentOptions { boolean requestPayerName = false; boolean requestBillingAddress = false; boolean requestPayerEmail = false; boolean requestPayerPhone = false; boolean requestShipping = false; PaymentShippingType shippingType = "shipping"; };
The PaymentOptions dictionary is passed to the PaymentRequest constructor and provides information about the options desired for the payment request.
The shippingType member only affects the user interface for the payment request.
dictionary PaymentItem { required DOMString label; required PaymentCurrencyAmount amount; boolean pending = false; };
A sequence of one or more PaymentItem dictionaries is included in the PaymentDetailsBase dictionary to indicate what the payment request is for and the value asked for.
A physical address is composed of the following parts.
[SecureContext, Exposed=(Window)] interface PaymentAddress { [Default] object toJSON(); readonly attribute DOMString city; readonly attribute DOMString country; readonly attribute DOMString dependentLocality; readonly attribute DOMString organization; readonly attribute DOMString phone; readonly attribute DOMString postalCode; readonly attribute DOMString recipient; readonly attribute DOMString region; readonly attribute DOMString sortingCode; readonly attribute FrozenArray<DOMString> addressLine; };
The PaymentAddress interface represents a physical address.
The steps to internally construct a
PaymentAddress
with an optional
AddressInit details are given by the following
algorithm:
When called, runs [[WEBIDL]]'s default toJSON operation.
Represents the country of the address. When getting, returns the value of the PaymentAddress's [[\country]] internal slot.
Represents the address line of the address. When getting, returns the value of the PaymentAddress's [[\addressLine]] internal slot.
Represents the region of the address. When getting, returns the value of the PaymentAddress's [[\region]] internal slot.
Represents the city of the address. When getting, returns the value of the PaymentAddress's [[\city]] internal slot.
Represents the dependent locality of the address. When getting, returns the value of the PaymentAddress's [[\dependentLocality]] internal slot.
Represents the postal code of the address. When getting, returns the value of the PaymentAddress's [[\postalCode]] internal slot.
Represents the sorting code of the address. When getting, returns the value of the PaymentAddress's [[\sortingCode]] internal slot.
Represents the organization of the address. When getting, returns the value of the PaymentAddress's [[\organization]] internal slot.
Represents the recipient of the address. When getting, returns the value of the PaymentAddress's [[\recipient]] internal slot.
Represents the phone number of the address. When getting, returns the value of the PaymentAddress's [[\phone]] internal slot.
Internal slot | Description (non-normative) |
---|---|
[[\country]] | A country as an [[ISO3166-1]] alpha-2 code stored in its canonical uppercase form or the empty string. For example, "JP". |
[[\addressLine]] | A frozen array, possibly of zero length, representing an address line. |
[[\region]] | A region as a country subdivision name or the empty string, such as "Victoria", representing the state of Victoria in Australia. |
[[\city]] | A city or the empty string. |
[[\dependentLocality]] | A dependent locality or the empty string. |
[[\postalCode]] | A postal code or the empty string. |
[[\sortingCode]] | A sorting code or the empty string. |
[[\organization]] | An organization or the empty string. |
[[\recipient]] | A recipient or the empty string. |
[[\phone]] | A phone number or the empty string. |
dictionary AddressInit { DOMString country; sequence<DOMString> addressLine; DOMString region; DOMString city; DOMString dependentLocality; DOMString postalCode; DOMString sortingCode; DOMString organization; DOMString recipient; DOMString phone; };
An AddressInit is passed when constructing a PaymentAddress. Its members are as follows.
dictionary AddressErrors { DOMString addressLine; DOMString city; DOMString country; DOMString dependentLocality; DOMString organization; DOMString phone; DOMString postalCode; DOMString recipient; DOMString region; DOMString sortingCode; };
The members of the AddressErrors dictionary represent validation errors with specific parts of a physical address. Each dictionary member has a dual function: firstly, its presence denotes that a particular part of an address is suffering from a validation error. Secondly, the string value allows the developer to describe the validation error (and possibly how the end user can fix the error).
Developers need to be aware that users might not have the ability to fix certain parts of an address. As such, they need to be mindful not to ask the user to fix things they might not have control over.
PaymentAddress
from user-provided input
The steps to create a PaymentAddress
from
user-provided input are given by the following algorithm. The
algorithm takes a list redactList.
The redactList optionally gives user agents the possibility to limit the amount of personal information about the recipient that the API shares with the merchant.
For merchants, the resulting PaymentAddress object provides enough information to, for example, calculate shipping costs, but, in most cases, not enough information to physically locate and uniquely identify the recipient.
Unfortunately, even with the redactList, recipient anonymity cannot be assured. This is because in some countries postal codes are so fine-grained that they can uniquely identify a recipient.
Postal codes in certain countries can be so specific as to uniquely identify an individual. This being a privacy concern, some user agents only return the part of a postal code that they deem sufficient for a merchant to calculate shipping costs. This varies across countries and regions, and so the choice to redact part, or all, of the postal code is left to the discretion of implementers in the interest of protecting users' privacy.
If "region" is not in redactList:
In some countries (e.g., Belgium) it is uncommon for users to include a region as part of a physical address (even if all the regions of a country are part of [[ISO3166-2]]). As such, when the user agent knows that the user is inputting the address for a particular country, it might not provide a field for the user to input a region. In such cases, the user agent returns an empty string for both PaymentAddress's region attribute - but the address can still serve its intended purpose (e.g., be valid for shipping or billing purposes).
PaymentAddress
with details and
return the result.
dictionary PaymentShippingOption { required DOMString id; required DOMString label; required PaymentCurrencyAmount amount; boolean selected = false; };
The PaymentShippingOption dictionary has members describing a shipping option. Developers can provide the user with one or more shipping options by calling the updateWith() method in response to a change event.
enum PaymentComplete { "fail", "success", "unknown" };
[SecureContext, Exposed=Window] interface PaymentResponse : EventTarget { [Default] object toJSON(); readonly attribute DOMString requestId; readonly attribute DOMString methodName; readonly attribute object details; readonly attribute PaymentAddress? shippingAddress; readonly attribute DOMString? shippingOption; readonly attribute DOMString? payerName; readonly attribute DOMString? payerEmail; readonly attribute DOMString? payerPhone; [NewObject] Promise<void> complete(optional PaymentComplete result = "unknown"); [NewObject] Promise<void> retry(optional PaymentValidationErrors errorFields); attribute EventHandler onpayerdetailchange; };
A PaymentResponse is returned when a user has selected a payment method and approved a payment request.
The retry(errorFields)
method MUST act as
follows:
The retryPromise will later be resolved by the user accepts the payment request algorithm, or rejected by either the user aborts the payment request algorithm or abort the update.
dictionary PaymentValidationErrors { PayerErrors payer; AddressErrors shippingAddress; DOMString error; object paymentMethod; };
BasicCardErrors
.
dictionary PayerErrors { DOMString email; DOMString name; DOMString phone; };
The PayerErrors is used to represent validation errors with one or more payer details.
Payer details are any of the payer's name, payer's phone number, and payer's email.
const payer = { email: "The domain is invalid.", phone: "Unknown country code.", name: "Not in database.", }; await response.retry({ payer });
When called, runs [[WEBIDL]]'s default toJSON operation.
The payment method identifier for the payment method that the user selected to fulfill the transaction.
An object or dictionary generated by a payment method that a merchant can use to process or validate a transaction (depending on the payment method).
If the requestShipping member was set to true in the PaymentOptions passed to the PaymentRequest constructor, then shippingAddress will be the full and final shipping address chosen by the user.
If the requestShipping member was set to true in the PaymentOptions passed to the PaymentRequest constructor, then shippingOption will be the id attribute of the selected shipping option.
If the requestPayerName member was set to true in the PaymentOptions passed to the PaymentRequest constructor, then payerName will be the name provided by the user.
If the requestPayerEmail member was set to true in the PaymentOptions passed to the PaymentRequest constructor, then payerEmail will be the email address chosen by the user.
If the requestPayerPhone member was set to true in the PaymentOptions passed to the PaymentRequest constructor, then payerPhone will be the phone number chosen by the user.
The corresponding payment request id that spawned this payment response.
The complete() method is called after the user has accepted the payment request and the [[\acceptPromise]] has been resolved. Calling the complete() method tells the user agent that the payment interaction is over (and SHOULD cause any remaining user interface to be closed).
After the payment request has been accepted and the PaymentResponse returned to the caller, but before the caller calls complete(), the payment request user interface remains in a pending state. At this point the user interface SHOULD NOT offer a cancel command because acceptance of the payment request has been returned. However, if something goes wrong and the developer never calls complete() then the user interface is blocked.
For this reason, implementations MAY impose a timeout for developers to call complete(). If the timeout expires then the implementation will behave as if complete() was called with no arguments.
The complete(result)
method MUST act
as follows:
Allows a developer to handle "payerdetailchange" events.
Instances of PaymentResponse are created with the internal slots in the following table:
Internal Slot | Description (non-normative) |
---|---|
[[\complete]] | Is true if the request for payment has completed (i.e., complete() was called, or there was a fatal error that made the response not longer usable), or false otherwise. |
[[\request]] | The PaymentRequest instance that instantiated this PaymentResponse. |
[[\retryPromise]] | Null, or a Promise that resolves when a user accepts the payment request or rejects if the user aborts the payment request. |
PaymentRequest
and iframe
elements
To indicate that a cross-origin iframe is allowed to invoke the payment request API, the allowpaymentrequest attribute can be specified on the iframe element. See for details of how allowpaymentrequest and Feature Policy interact.
This specification defines a policy-controlled feature identified by
the string "payment
". Its default allowlist is
'self
'.
A document’s feature policy determines whether any content in that document is allowed to construct PaymentRequest instances. If disabled in any document, no content in the document will be allowed to use the PaymentRequest constructor (trying to create an instance will throw).
The allowpaymentrequest attribute of the HTML iframe
element affects the container policy for any
document nested in that iframe. Unless overridden by the
allow
attribute, setting allowpaymentrequest on an iframe is
equivalent to <iframe allow="fullscreen *">
, as
described in Feature Policy
§iframe-allowpaymentrequest-attribute.
Event name | Interface | Dispatched when… | Target |
---|---|---|---|
merchantvalidation
|
MerchantValidationEvent | The user agent requires the merchant to perform merchant validation. | PaymentRequest |
shippingaddresschange
|
PaymentRequestUpdateEvent | The user provides a new shipping address. | PaymentRequest |
shippingoptionchange
|
PaymentRequestUpdateEvent | The user chooses a new shipping option. | PaymentRequest |
payerdetailchange
|
PaymentRequestUpdateEvent | The user changes the payer name, the payer email, or the payer phone (see payer detail changed algorithm). | PaymentResponse |
paymentmethodchange
|
PaymentMethodChangeEvent | The user chooses a different payment method within a payment handler. | PaymentRequest |
[Constructor(DOMString type, optional MerchantValidationEventInit eventInitDict), SecureContext, Exposed=Window] interface MerchantValidationEvent : Event { readonly attribute DOMString methodName; readonly attribute USVString validationURL; void complete(Promise<any> merchantSessionPromise); };
When getting, returns the value it was initialized with. See methodName member of MerchantValidationEventInit for more information.
MerchantValidationEvent
constructor
The event constructing steps, which take a MerchantValidationEvent event, are as follows:
A URL from which a developer can fetch payment handler-specific verification data. By then passing that data (or a promise that resolves with that data) to complete(), the user agent can verify that the payment request is from an authorized merchant.
When getting, returns the value it was initialized with.
The MerchantValidationEvent's
complete(merchantSessionPromise)
method
MUST act as follows:
Instances of MerchantValidationEvent are created with the internal slots in the following table:
Internal Slot | Description (non-normative) |
---|---|
[[\waitForUpdate]] | A boolean indicating whether a complete()-initiated update is currently in progress. |
dictionary MerchantValidationEventInit : EventInit { DOMString methodName = ""; USVString validationURL = ""; };
[Constructor(DOMString type, optional PaymentMethodChangeEventInit eventInitDict), SecureContext, Exposed=Window] interface PaymentMethodChangeEvent : PaymentRequestUpdateEvent { readonly attribute DOMString methodName; readonly attribute object? methodDetails; };
When getting, returns the value it was initialized with. See methodDetails member of PaymentMethodChangeEventInit for more information.
When getting, returns the value it was initialized with. See methodName member of PaymentMethodChangeEventInit for more information.
dictionary PaymentMethodChangeEventInit : PaymentRequestUpdateEventInit { DOMString methodName = ""; object? methodDetails = null; };
[Constructor(DOMString type, optional PaymentRequestUpdateEventInit eventInitDict), SecureContext, Exposed=Window] interface PaymentRequestUpdateEvent : Event { void updateWith(Promise<PaymentDetailsUpdate> detailsPromise); };
The PaymentRequestUpdateEvent enables developers to update the details of the payment request in response to a user interaction.
The PaymentRequestUpdateEvent(type,
eventInitDict)
constructor MUST act as
follows:
The updateWith(detailsPromise)
method MUST act as follows:
PaymentRequest
's details
algorithm with detailsPromise, request,
and pmi.
Instances of PaymentRequestUpdateEvent are created with the internal slots in the following table:
Internal Slot | Description (non-normative) |
---|---|
[[\waitForUpdate]] | A boolean indicating whether an updateWith()-initiated update is currently in progress. |
dictionary PaymentRequestUpdateEventInit : EventInit {};
When the internal slot [[\state]] of a PaymentRequest object is set to "interactive", the user agent will trigger the following algorithms based on user interaction.
Merchant validation is the process by which a payment handler validates the identity of a merchant against some value (usually some cryptographic challenge response). Validated merchants are allowed to interface with a payment handler. Details of how actual validation is performed is outside the scope of this specification.
It is OPTIONAL for a payment handler to support merchant validation.
For payment methods that support merchant validation,
the user agent runs the request merchant validation
algorithm. The algorithm takes a USVString
merchantSpecificURL, provided by the payment
handler:
isTrusted
attribute to
true.
The shipping address changed algorithm runs when the user provides a new shipping address. It MUST run the following steps:
The redactList limits the amount of personal information about the recipient that the API shares with the merchant.
For merchants, the resulting PaymentAddress object provides enough information to, for example, calculate shipping costs, but, in most cases, not enough information to physically locate and uniquely identify the recipient.
Unfortunately, even with the redactList, recipient anonymity cannot be assured. This is because in some countries postal codes are so fine-grained that they can uniquely identify a recipient.
PaymentAddress
from user-provided
input with redactList.
The shipping option changed algorithm runs when the user chooses a new shipping option. It MUST run the following steps:
id
string of the
PaymentShippingOption provided by the user.
A payment handler MAY run the payment method changed algorithm when the user changes payment method with methodDetails, which is a dictionary or an object or null, and a methodName, which is a DOMString that represents the payment method identifier of the payment handler the user is interacting with.
When the user selects or changes a payment method (e.g., a credit card), the PaymentMethodChangeEvent includes redacted billing address information for the purpose of performing tax calculations. Redacted attributes include, but are not limited to, address line, dependent locality, organization, phone number, and recipient.
The PaymentRequest updated algorithm is run by other algorithms above to fire an event to indicate that a user has made a change to a PaymentRequest called request with an event name of name:
type
attribute to name.
The user agent MUST run the payer detail changed algorithm when the user changes the payer name, or the payer email, or the payer phone in the user interface:
type
attribute to
"payerdetailchange".
The user accepts the payment request algorithm runs when the user accepts the payment request and confirms that they want to pay. It MUST queue a task on the user interaction task source to perform the following steps:
PaymentAddress
from user-provided input with
redactList.
The user aborts the payment request algorithm runs when the user aborts the payment request through the currently interactive user interface. It MUST queue a task on the user interaction task source to perform the following steps:
PaymentRequest
's details algorithm
The update a PaymentRequest
's details
algorithm takes a PaymentDetailsUpdate
detailsPromise, a PaymentRequest
request, and pmi that is either a DOMString or
null (a payment method identifier). The steps are conditional
on the detailsPromise settling. If
detailsPromise never settles then the payment request is
blocked. The user agent SHOULD provide the user with a means to abort
a payment request. Implementations MAY choose to implement a timeout
for pending updates if detailsPromise doesn't settle in a
reasonable amount of time.
In the case where a timeout occurs, or the user manually aborts, or the payment handler decides to abort this particular payment, the user agent MUST run the user aborts the payment request algorithm.
sequence
<PaymentShippingOption>.
If request.[[\options]].requestShipping is true, and request.[[\details]].shippingOptions is empty, then the developer has signified that there are no valid shipping options for the currently-chosen shipping address (given by request's shippingAddress).
In this case, the user agent SHOULD display an error indicating this, and MAY indicate that the currently-chosen shipping address is invalid in some way. The user agent SHOULD use the error member of details, if it is present, to give more information about why there are no valid shipping options for that address.
Further, if details["shippingAddressErrors"] member is present, the user agent SHOULD display an error specifically for each erroneous field of the shipping address. This is done by matching each present member of the AddressErrors to a corresponding input field in the shown user interface.
Similarly, if details["payerErrors"] member is present and request.[[\options]]'s requestPayerName, requestPayerEmail, or requestPayerPhone is true, then display an error specifically for each erroneous field.
Likewise, if details["paymentMethodErrors"] is present, then display errors specifically for each erroneous input field for the particular payment method.
To abort the update with a PaymentRequest request and exception exception:
Abort the update runs when there is a fatal error updating the payment request, such as the supplied detailsPromise rejecting, or its fulfillment value containing invalid data. This would potentially leave the payment request in an inconsistent state since the developer hasn't successfully handled the change event.
Consequently, the PaymentRequest moves to a "closed" state. The error is signaled to the developer through the rejection of the [[\acceptPromise]], i.e., the promise returned by show().
Similarly, abort the update occurring during retry() causes the [[\retryPromise]] to reject, and the corresponding PaymentRequest's [[\complete]] internal slot will be set to true (i.e., it can no longer be used).
The validate merchant's details algorithm takes a Promise opaqueDataPromise and a PaymentRequest request. The steps are conditional on the opaqueDataPromise settling. If opaqueDataPromise never settles then the payment request is blocked. The user agent SHOULD provide the user with a means to abort a payment request. Implementations MAY choose to implement a timeout for pending updates if opaqueDataPromise doesn't settle in a reasonable amount of time. If an implementation chooses to implement a timeout, they MUST execute the steps listed below in the "upon rejection" path. Such a timeout is a fatal error for the payment request.
show()
method
To help ensure that users do not inadvertently share sensitive credentials with an origin, this API requires that PaymentRequest's show() method be triggered by user activation (e.g., via a click or press).
To avoid a confusing user experience, this specification limits the user agent to displaying one at a time via the show() method. In addition, the user agent can limit the rate at which a page can call show().
The API defined in this specification is only exposed in secure contexts. In practice, this means that this API is only available over HTTPS. This is to limit the possibility of payment method data (e.g., credit card numbers) being sent in the clear.
It is common for merchants and other payees to delegate checkout and other e-commerce activities to payment service providers through an iframe. This API supports payee-authorized cross-origin iframes through [[HTML]]'s allowpaymentrequest attribute.
Payment handlers have access to both the origin that hosts the iframe and the origin of the iframe content (where the PaymentRequest initiates).
The PaymentRequest API does not directly support encryption of data fields. Individual payment methods may choose to include support for encrypted data but it is not mandatory that all payment methods support this.
As part of show(), the user agent typically displays a list of matching payment handlers that satisfy the payment methods accepted by the merchant and other conditions. Matching can take into account payment method information provided as input to the API, information provided by the payment method owner, the payment handlers registered by the user, user preferences, and other considerations.
For security reasons, a user agent can limit matching (in show() and canMakePayment()) to payment handlers from the same origin as a URL payment method identifier.
The user agent MUST NOT share information about the user with a developer (e.g., the shipping address) without user consent.
One way that the API supports limited information sharing is through the "redactList" associated with the creation of physical addresses throughout the API. This feature enables user agents to provide the payee with enough information to compute shipping costs or tax information, while limiting the payee's ability to identify the payer via the address.
The user agent MUST NOT share the values of the displayItems member or additionalDisplayItems member with a third-party payment handler without user consent.
Where sharing of privacy-sensitive information might not be obvious to users (e.g., when changing payment methods), it is RECOMMENDED that user agents inform the user of exactly what information is being shared with a merchant.
It is important that the validationURL in a MerchantValidationEvent does not expose personally identifying information to unauthorized parties.
canMakePayment()
protections
The canMakePayment() method enables the payee to determine — before calling show() — whether the user agent knows of any payment handlers available to the user that support the payment methods provided to the PaymentRequest constructor. If no payment handlers support the payment methods, this enables the payee to fall back to a legacy checkout experience. Because this method shares some potentially unique information with the payee, user agents are expected to protect the user from abuse of the method. For example, user agents can reduce user fingerprinting by:
For rate-limiting the user agent might look at repeated calls from:
These rate-limiting techniques intend to increase the cost associated with repeated calls, whether it is the cost of managing multiple eTLDs or the user experience friction of opening multiple windows (tabs or pop-ups).
For the user-facing aspects of Payment Request API, implementations integrate with platform accessibility APIs via form controls and other input modalities. Furthermore, to increase the intelligibility of total, shipping addresses, and contact information, implementations format data according to system conventions.
This specification relies on several other underlying specifications.
EventHandler
,
queue a
task, user interaction
task source, top-level browsing
context, current settings
object, allowed to use,
triggered by
user activation, in parallel, the
iframe
element, and the allowpaymentrequest
attribute.
RangeError
,
TypeError
,
and JSON.stringify
are
defined by [[ECMASCRIPT]].
The term JSON-serialize applied to a given object means to run the algorithm specified by the original value of the JSON.stringify function on the supplied object, passing the supplied object as the sole argument, and return the resulting string. This can throw an exception.
Event
interface,
the EventTarget
interface, the
EventInit
dictionary, and the terms fire an event, dispatch flag, stop propagation flag,
isTrusted
attribute, context object,
and stop
immediate propagation flag are defined by [[DOM]].
When this specification says to throw an error, the user agent must throw an error as described in [[WEBIDL]]. When this occurs in a sub-algorithm, this results in termination of execution of the sub-algorithm and all ancestor algorithms until one is reached that explicitly describes procedures for catching exceptions.
The algorithm for converting an ECMAScript value to a dictionary is defined by [[WEBIDL]].
DOMException
and the
following DOMException types from [[WEBIDL]] are used:
"AbortError
",
"InvalidStateError
",
"NotAllowedError
",
"NotSupportedError
", and
"SecurityError
".
There is only one class of product that can claim conformance to this specification: a user agent.
Although this specification is primarily targeted at web browsers, it is feasible that other software could also implement this specification in a conforming manner.
User agents MAY implement algorithms given in this specification in any way desired, so long as the end result is indistinguishable from the result that would be obtained by the specification's algorithms.
User agents MAY impose implementation-specific limits on otherwise unconstrained inputs, e.g., to prevent denial of service attacks, to guard against running out of memory, or to work around platform-specific limitations. When an input exceeds implementation-specific limit, the user agent MUST throw, or, in the context of a promise, reject with, a TypeError optionally informing the developer of how a particular input exceeded an implementation-specific limit.
This specification was derived from a report published previously by the Web Platform Incubator Community Group.