The freedom to transact and exchange value has become an important cornerstone of the Web. This specification outlines several browser-based mechanisms that make financial transactions easier to initiate while also making them more secure.
There are a number of ways that one may participate in the development of this specification:
This API enables Web content to initiate payment or issue a refund for a
product or service. Once implemented in the browser, an author may issue
navigator.transact.pay()
function to initiate a payment.
This document is a detailed specification for an application programming interface (API) for initiating payments from within a browser environment. The document is primarily intended for the following audiences:
An open web app will interact with a
Payment Provider
via navigator.transact.pay()
and receive notifications POSTed to its
server about the result of each payment. Users will see a payment flow hosted
by the Payment Provider in a special window on the device.
navigator.transact.pay()
.
Describe high-level payment flow, which is based on Mozilla's initial implementation for Firefox OS.
navigator.transact.pay()
and require a client and server.
navigator.transact.pay()
function to charge users for
application purchases.
navigator.transact.pay()
. This conforms to
the
Payment Provider
spec. The provider accepts payment from a user and disperses income to the Developer.
This is an implementation detail of the navigator.transact.pay()
API does not prescribe any user
authorization scheme.
Specify user identity management, e.g. Persona.
This is an implementation detail of the navigator.transact.pay()
API facilities two parties in making a
transaction: 1) a Developer and 2) a Payment Provider. It does not
facilitate any part of the registration process.
A developer who creates a JSON Web Token [[!JWT]] must do so with an
In Mozilla's implementation,
a developer signs up through the Firefox Marketplace Developer Hub,
enters bank account details for payouts, obtains an
navigator.transact.pay([theJWT])
. This begins the hosted buy flow
within a special window on the paymentJWT = jwt.encode({ "iss": APPLICATION_KEY, "aud": "marketplace.firefox.com", "typ": "mozilla/payments/pay/v1", "iat": 1337357297, "exp": 1337360897, "request": { "id": "915c07fc-87df-46e5-9513-45cb6e504e39", "pricePoint": 1, "name": "Magical Unicorn", "description": "Adventure Game item", "icons": { "64": "https://yourapp.com/img/icon-64.png", "128": "https://yourapp.com/img/icon-128.png" }, "productData": "user_id=1234&my_session_id=XYZ", "postbackURL": "https://yourapp.com/payments/postback", "chargebackURL": "https://yourapp.com/payments/chargeback" } }, APPLICATION_SECRET)
Here is a detailed explanation of the payment JWT:
For a user to make a purchase, the Application must execute the Javascript
method navigator.transact.pay()
with one or more signed payment
requests (the JWTs). For example, the app might have a 'buy' button that
triggers this method when clicked. Then navigator.transact.pay()
method
should take the signed payment JWT or an array of them. It will return a
DOMRequest object
that the developer can use to monitor the progress of the operation.
var request = navigator.transact.pay([signedJWT1, signedJWTn]); request.onsuccess = function () { // The payment buy flow completed without errors. // This does NOT mean the payment was successful. waitForServerPostback(); } request.onerror = function (errorMsg) { console.log('navigator.transact.pay() error: ' + this.error.name + ': ' + errorMsg); }
navigator.transact.pay
method will open a payment request
confirmation screen based on the received JWTs, so the user can confirm and
choose the payment method that is more appropriate for him.
The DOMRequest.onerror
callback.
The Application must only rely on server side notifications to determine the outcome of a purchase. The Payment Provider will POST a confirmation message (a JWT) to the postbackURL (on success) or the chargebackURL (on error). The Application provides these URLs in the original JWT request.
The POST request will have a content-type of
application/x-www-form-urlencoded
and the JWT will be in the
notice form parameter. This JWT contains a copy of the
original payment request plus a new response object that has a
transactionID which identifies the Payment Provider's
transaction.
When a JWT is received, the Application first needs to verify the signature using its Application Secret. If the signature is not valid, it probably was not sent from the Payment Provider and should be ignored. If the signtature is valid, then the application server should decode the JWT, record it, and respond with a 200 OK that contains the transactionID in plain text. If the Application server responds with an error status or does not respond with the right transactionID, the Payment Provider will consider this a failure. It will retry and/or notify the Developer about the failure. The Application must respond to the request in plain text containing just the transactionID value.
Here is an example of a JWT POSTed via the notice parameter to postbackURL that indicates a transaction was fully processed and was successful:
{ "iss": "marketplace.firefox.com", "aud": APPLICATION_KEY, "typ": "mozilla/payments/pay/postback/v1", "exp": 1337370900, "iat": 1337360900, "request": { "id": "915c07fc-87df-46e5-9513-45cb6e504e39", "pricePoint": 1, "name": "Magical Unicorn", "description": "Adventure Game item", "icons": { "64": "https://yourapp.com/img/icon-64.png", "128": "https://yourapp.com/img/icon-128.png" }, "productData": "user_id=1234&my_session_id=XYZ", "postbackURL": "https://yourapp.com/payments/postback", "chargebackURL": "https://yourapp.com/payments/chargeback" }, "response": { "transactionID": "webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9" } }
Here is an example response that includes just the transactionID:
HTTP/1.1 200 OK Content-Type: text/plain webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9
Here is an example of a JWT POSTed via the notice parameter to chargebackURL that indicates a transaction was fully processed but was unsuccessful:
{ "iss": "marketplace.firefox.com", "aud": APPLICATION_KEY, "typ": "mozilla/payments/pay/chargeback/v1", "exp": 1337370900, "iat": 1337360900, "request": { "id": "915c07fc-87df-46e5-9513-45cb6e504e39", "pricePoint": 1, "name": "Magical Unicorn", "description": "Adventure Game item", "icons": { "64": "https://yourapp.com/img/icon-64.png", "128": "https://yourapp.com/img/icon-128.png" }, "productData": "user_id=1234&my_session_id=XYZ", "postbackURL": "https://yourapp.com/payments/postback", "chargebackURL": "https://yourapp.com/payments/chargeback" }, "response": { "transactionID": "webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9", "reason": "refund" } }
A chargeback JWT might be received instead of or in addition to a postback. The response will contain a reason attribute, as follows:
Here is an example response that includes just the transactionID:
HTTP/1.1 200 OK Content-Type: text/plain webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9
Refunds are not yet supported by the navigator.transact.pay()
API.
At a future date, an Application may be able to request a refund
like this:
{ "iss": APPLICATION_KEY, "aud": "marketplace.firefox.com", "typ": "mozilla/payments/refund/v1", "exp": 1337370900, "iat": 1337360900, "request": { "transactionID": "webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9", "reason": "User requested refund", "chargebackURL": "https://yourapp.com/payments/chargeback" } }
This would initiate a refund flow and POST a confirmation JWT when completed.
Figure out how to reference the
WebPaymentProvider
spec for details on how to implement a payment provider for
navigator.transact.pay()
. It may be that we need to fold that into this
spec.
This API provides a clean mechanism that enables developers to
initiate payments in a
Navigator Transactions is the name of the high-level programming
interface that Web developers use to initiate payments. If MUST be
made available via the navigator.transact
object.
Initiates a payment or refund given an array of JSON Web Tokens [[!JWT]] describing the types of actions to perform.
Should we break refunds out into their own method call? The downside of doing that is that the API is no longer fairly generic. However, .pay() doesn't really imply "refund", unless you think of a refund like a reverse payment? The other thing we could do is use .process() or .transact() as the method call and change the interface name to something like navigator.funds.transact() or navigator.wallet.transact().
Each request object will contain a typ
key. The value associated
with this key will be used to perform matchmaking between the customer's list
of registered payment providers and the list of preferred payment providers
for the Merchant. Typically, a
Should the merchant provide a payment provider registration link as an option to the call in the case where there is no match between the list of payment providers accepted by the merchant vs. the list of registered providers for the customer?
Registers a payment provider with the browser such that future transactions may provide an interface to the user to select which payment provider that they would like to use to complete a transaction.
Registering a payment provider can alter the choices given to the User when a transaction is processed. In the worst case, an attacker could register a fake payment provider that collects all of the User's credit card or banking details. In order to ensure that the User is protected from this sort of attack, the User Agent MUST provide a special interface that is not easily spoofed by an attacker.
When a
id
and services
URLs must match
the domain for the page requesting the registration, etc.
id
https://example.org/
. Ideally,
this URL could be modified by appending a relative path, such as .well-known/
to discover more services related to the payment provider.
name
(optional)id
SHOULD be used.
icon
(optional)id
SHOULD be used.
transactionType
A free-form string, or array of strings, advertising the type of transactions
supported by the payment provider. Other specifications, such as
[[WEB-PAYMENTS]], outline the acceptable values for this parameter. This
parameter is used by the pay()
method to determine which
registered payment provider is capable of processing a particular
transaction type (for example: credit cards, mobile phone carrier-based billing,
or PaySwarm).
services
(optional)Retrieves all of the payment providers that are registered with the User Agent that match the query criteria given and were authorized to be included in the response by the User.
It is not always preferable for a User to have all of their payment providers exposed to a Merchant. In the worst case, an attacker can use this information to execute spear-fishing attacks against the User. The User Agent should provide the User with an interface that allows them to select which providers they want to expose to the merchant. In many cases, the User will only want to expose one, which will make the purchase process proceed more fluidly.
The result will be an array of payment providers that are in the same format
as the objects passed to the registerProvider()
method.
The authors would like to thank ...
Thanks to the following individuals, in order of their first name, for their input on the specification: ...