The freedom to transact and exchange value has become an important cornerstone of the Web. This specification outlines a browser polyfill that makes financial transactions easier to initiate and verify while also making them more secure. The solution is designed to work with both proprietary (PayPal, Google Wallet) and non-proprietary (PaySwarm, Bitcoin, Ripple) payment solutions.

This document is heavily influenced by the MozPay API from Mozilla, which was authored by Andreas Gal, Fernando Jiménez, Mike Hanson, and Kumar McMillan.

There are a number of ways that one may participate in the development of this specification:

Introduction

This API enables a web application to initiate payment for a product or service by calling a navigator.transact.pay() method. The implementation of this feature is expected to be provided by a JavaScript polyfill at first, and if deployment is successful, native browser implementations will eventually surface to enhance usability and transaction security.

How to Read this Document

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:

Terminology

There are a number of terms used throughout this document that readers may not be familiar with. This section can be used as a reference for that terminology.

application
A web application which offers goods or services to be sold. The Web App charges users via navigator.transact.pay() and requires a client and server.
vendor identity
A public identifier that can be transmitted in a JSON object so that a payment provider can identify the vendor.
vendor
A seller of goods or services.
payment provider
A client/server web application that serves content in a dialog controlled by navigator.transact.pay(). The provider accepts payment from a buyer and disperses income to the vendor.
buyer
A person, organization, or other entity who wants to purchase a good or service.
user agent
A tool that is used to interact with the Web. This can be a web browser, mobile phone operating system, or similar software system.

Payment Flow Overview

A web app running on the vendor's website will interact with a payment provider via navigator.transact.pay(). Results of the payment request will be POSTed to the vendor's server.

  1. The app initiates a payment by calling navigator.transact.pay(request).
  2. This starts the buyflow by POSTing a purchase request to the payment provider. The payment provider is either the vendor-provided default, or a payment provider specified by the customer.
  3. A purchasing flow is served from the payment provider as an HTML5 document inside a dialog.
  4. The buyer is authenticated by the payment provider.
  5. The buyer accepts or cancels the purchase.
  6. A callback is called on the vendor's page that notfies the vendors software that the purchase was successful, or canceled.
  7. The vendor software receives a digital receipt of the sale and may then verify whether or not the payment was processed successfully.

The diagram below outlines the basic payment flow described above.

Detailed Payment Flow

Describe the payment flow in detail here. Primarily, express how this approach utilizes a mechanism like Persona to deploy the API on websites using a centralized service and then decentralize the payment mechanism through the implementation of the dialog via a Web browser.

Buyer Registration and Login

This is an implementation detail of the payment provider. The navigator.transact.pay() API does not prescribe any user authorization scheme.

Specify user identity management, e.g. Persona, and describe how Persona coupled with the Web Identity specification can be used to discover a customer's payment provider.

Vendor Registration

This is an implementation detail of the payment provider. The navigator.transact.pay() API facilities two parties in executing a transaction: 1) a buyer and 2) a vendor. It does not facilitate any part of the registration process.

The Payment Request

  1. The buyer navigates to a vendor's website.
  2. The buyer finds something interesting to purchase, like a one year subscription to a blog.
  3. The buyer clicks a Buy button.
  4. The application generates a purchase request, which is a [[!JSON-LD]] object that contains the minimum amount of information for the payment provider to perform discovery on the product price and other information such as name, description, price, etc.
  5. The navigator.transact.pay() method is called with the purchase request. This begins the payment provider buy flow inside a dialog in the user agent.
var purchaseRequest = {
  '@context': 'https://w3id.org/web-payments/v1',
  paymentProvider: 'https://paymentprovider.com/transact',
  listing: 'https://example.com/products/test#listing',
  listingHash: '1ecd90db01ba293d097c713150defffa5e38bdabbe56e8950e4b354f514c7018',
  paymentCallback: 'https://store.com/digital-receipts?nonce=2f73a2j'
}
    

Here is a detailed explanation of the purchase request:

paymentProvider (mandatory)
The payment provider URL to use when initiating the transaction. The purchase request will be POSTed to this location.
listing (mandatory)
A URL for the identifier of the product listing (product and pricing information).
listingHash
A SHA-256 hash of the listing if the vendor would like to ensure that the listing that is fetched matches the hash that they generated. Warning: This isn't completely collision proof, but it's very good protection as long as the size of the listing is limited to less than 512Kb. (We need to calculate max listing sizes that protect against collision attacks on SHA-256).
paymentCallback (mandatory)
The callback that will be called with the result of the purchase, either a digital receipt or a cancelation reason.

For a user to make a purchase, the Application must execute the Javascript method navigator.transact.pay() with the listing. For example, the app might have a 'buy' button that triggers this method when clicked.

navigator.transact.pay(purchaseRequest);
    
  1. The navigator.transact.pay method will POST the purchase request to the payment provider and the result will be shown in a dialog, so the buyer can confirm payment.
  2. The details of the exact buy flow are implemented by the payment provider.
  3. The buyer confirms the purchase or cancels it.
  4. If the buyer cancels or something goes wrong with the payment process, an error is posted to the callback

The Digital Receipt

The user agent will POST a digital receipt to the paymentCallback if the payment is successful. If the payment is not successful, an error will be posted to the paymentCallback.

When a digital receipt is received, the application must verify the signature. If the signature is not valid, it should be ignored. If the signature is valid, the application should grant the buyer access to the item they purchased.

The vendor is going to have to have a whitelist of some sort to understand which payment processors' signatures it trusts on digital receipts. For example, perhaps PayPal, Google Wallet, and Amazon will be in a whitelist. This creates undue advantage to those organizations, so a generalized whitelist is going to be necessary if the system is going to scale to thousands of payment processors. This will most likely require some sort of regulatory body that approves payment processors (ensures that they've met some sort of fiduciary criteria).

Here is an example of a callback POSTed to the purchaseCallback that indicates a transaction was fully processed and was successful:

{
  "@context": "https://w3id.org/web-payments/v1",
  "id": "https://paymentprovider.com/transactions/1.3.12e.1e",
  "type": ["Transaction", "Receipt"],
  "amount": "0.05",
  "currency": "USD",
  "assetAcquirer": "https://paymentprovider.com/i/manu",
  "assetProvider": "https://artists.com/i/artist",
  "asset": "http://artists.com/writing/recipe#asset",
  "assetHash": "urn:sha256:9b757aa92e3cad...16524fe58fc8bc6857a051",
  "license": "https://w3id.org/web-payments/licenses/blogging",
  "licenseHash": "urn:sha256:d9dcfb7b3b...d58e1b9",
  "listing": "http://vendor.com/articles/10317#listing"
  "listingHash": "urn:sha256:58c4bbff8073...67711454a10155",
  "authorized": "2013-09-18T19:12:22Z",
  "created": "2013-09-18T19:11:48Z",
  "settled": "2013-09-18T19:12:22Z",
  "vendor": "https://vendorpayments.com/i/vendor"
  "signature": {
    "type": "GraphSignature2012",
    "created": "2014-02-18T09:10:58+00:00",
    "creator": "https://paymentprovider.com/i/paymentprovider/keys/1",
    "signatureValue": "dSVNyy+UJR3Y6zmw...XEoJfEdcHw=="
  }
}
    

The Application Programming Interface

This browser-based API is provided for convenience purposes only. The existence of the API is absolutely not a requirement for the basic operation of the Web Payments protocol. No browser API is necessary in order for a Web application to initiate payment and receive a digital receipt of the result of the payment.

This API provides a clean mechanism that enables developers to initiate payments in a User Agent. A conformant Payment Initator MUST implement the entirety of the following API.

NavigatorTransactions

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.

DOMRequest pay()

Initiates a payment or refund given an array of [[!JSON-LD]] encoded payment requests describing the type of financial operations 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 User Agent will request that the customer rank their registered payment providers in most preferable to least preferable order. If this ranking is performed, a payment provider can be selected automatically by picking highest ranked payment provider in the customer's payment provider list with a payment provider that also exists in the Merchant's payment provider list.

object[] requests
An array of [[!JSON-LD]] encoded payment requests describing the type of financial operations to perform.

Acknowledgements

The editor would like to thank Andreas Gal, Fernando Jiménez, Mike Hanson, and Kumar McMillan for their work on the MozPay API.

Thanks to the following individuals, in order of their first name, for their input on the specification: ...