WebAPI/WebPayment: Difference between revisions

No edit summary
 
(133 intermediate revisions by 6 users not shown)
Line 1: Line 1:
= WebPayment API Specification (DRAFT)=
= WebPayment API Specification (DRAFT)=
'''Note:''' This API is in the process of being removed from Android and Desktop, long term status in B2G is unclear.
== Goal ==
== Goal ==
The goal of this API is to allow Open Web Apps to initiative a payment (or a refund) form the user for a virtual good.
The goal of this API is to enable web content to collect payment (or issue a refund) for a virtual good via the <code>navigator.mozPay()</code> function.


== Status ==
== Status ==
See {{bug|767818}} for the 'navigator.pay' implementation.
See {{bug|767818}} for the <code>navigator.mozPay()</code> implementation.


Currently only implemented for B2G.
Currently only implemented for B2G.
Line 13: Line 16:
  interface nsIDOMNavigatorPayment
  interface nsIDOMNavigatorPayment
  {
  {
   DOMRequest pay(in jsval jwts);
   DOMRequest mozPay(in jsval jwts);
  }
  }
</code>
</code>


== Proposers ==
== Proposers ==
Andreas Gal and Fernando Jiménez.
Andreas Gal and Fernando Jiménez. Based on Mike Hanson's and Kumar McMillan's work for [https://developer.mozilla.org/en-US/docs/Apps/Publishing/In-app_payments Firefox Marketplace in-app payments].


= WebPayments Architecture (DRAFT) =
= WebPayments Architecture (DRAFT) =
== Introduction ==
== Introduction ==
The goal of this Web Payments architecture is to allow Open Web Apps to initiate a payment (or a refund) from the user for a virtual good via the <code>navigator.pay</code> function.
An open web app will interact with a [https://wiki.mozilla.org/WebAPI/WebPaymentProvider Payment Provider] via <code>navigator.mozPay()</code> 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.
 
An OWA should be able to access the in-app web payments services using an API (<code>navigator.pay</code>) exposed by the User Agent on the device (for the B2G use case). The UA must use an asynchronous message loop to convey billing requests and responses between the application and the Payment Processor (or Provider). In practice, the applications should never directly communicate with the Payment Processors's servers. Instead, the application should send billing requests to the UA via 'navigator.pay()' and should receive purchase responses from the UA in the form of asynchronous callbacks.


== Payment flow overview ==
== Payment flow overview ==
* The app initiates a payment by signing a JWT request and calling <code>navigator.pay()</code>.
* The app initiates a payment by signing a [http://openid.net/specs/draft-jones-json-web-token-07.html JSON Web Token (JWT)] request and calling <code>navigator.mozPay()</code>.
* This starts the buyflow in a content iframe inside a trusted dialog ("chrome dialog").
* This starts the buyflow in a content iframe inside a trusted dialog ("chrome dialog").
* A purchasing flow is served from the Payment Provider's server as an HTML5 document inside the trusted dialog.
* A purchasing flow is served from the [https://wiki.mozilla.org/WebAPI/WebPaymentProvider Payment Provider]'s server as an HTML5 document inside the trusted dialog.
* The buyer is authenticated by the Payment Provider (via the network (radio) or BrowserID assertion in the B2G scenario).
* The buyer is authenticated by the [https://wiki.mozilla.org/WebAPI/WebPaymentProvider Payment Provider] (via the network (radio), Mozilla Persona assertion or whatever authentication mechanisms the Payment Provider chooses).
* The buyer completes or cancels the purchase. (Note that the Payment Provider might require an authorization step).
* The buyer completes or cancels the purchase.
* The app receives a Javascript callback when the buyer accepts or cancels the purchase.
* The app receives a Javascript callback when the buyer completes or cancels the purchase.
* The app serser receives a signed POST request with a Payment Provider transaction identifier indicating that the purchase was completed successfully.
* The app server receives a signed POST request with a Payment Provider transaction identifier indicating that the purchase was completed successfully (or it failed).


== Detailed payment flow ==
== Detailed payment flow ==
<span style="color:#009000">'''This detailed payment flow is based on the proposal for supporting Telefónica's Carrier Billing functionality in B2G, but it could be extended to any other Payment Provider.'''</span>
<span style="color:#009000">'''This detailed payment flow is based on Mozilla's [https://developer.mozilla.org/en-US/docs/Apps/Publishing/In-app_payments initial implementation for Firefox OS] but it could be extended to any other Payment Provider.'''</span>


=== Entities ===
=== Definitions ===
; Application: Open Web App which offers digital goods to be sold. OWAs that charge users via <code>navigator.pay()</code> require a client and server side.
; Application: Open Web App which offers digital goods to be sold. OWAs charge users via <code>navigator.mozPay()</code> and require a client and server.
; BlueVia portal: BlueVia is Telefónica's developer program. Developers can register in BlueVia portal in order to make use of TEF Carrier APIs, such as the payment API. We will be using Telefónica's Payment Provider as an example for the payment flow explanation and BlueVia is part of it.
; Application Key: A public identifier that can be transmitted in a JSON object so that a Payment Provider can identify the Application.
; Application Secret: A private string shared between Developer and Payment Provider. This will be used to sign [http://openid.net/specs/draft-jones-json-web-token-07.html JSON Web Tokens (JWTs)] and must be securely protected on a web server.
; Developer: Application developer, seller of digital goods.
; Developer: Application developer, seller of digital goods.
; Mozilla Marketplace:Developer portal and application repository. Developers can submit apps to the Mozilla Marketplace so the users can purchase and download this apps. The Mozilla Marketplace would also be using the <code>navigator.pay</code> function to charge users for application purchases.
; Firefox Marketplace: Developer portal and application repository. Developers can submit apps to the [https://marketplace.firefox.com/ Firefox Marketplace] so users can purchase and download the apps. The Firefox Marketplace uses the <code>navigator.mozPay</code> function to charge users for application purchases.
; Payment Provider (or Processor): Offers merchants online services for accepting electronic payments by a variety of payment methods (credict card (CC), direct to bill (D2B), etc.).
; Payment Provider: A client/server web application that serves content in a special iframe controlled by <code>navigator.mozPay()</code>. This conforms to the [https://wiki.mozilla.org/WebAPI/WebPaymentProvider Payment Provider] spec. The provider accepts payment from a user and disperses income to the Developer.
; TU ID Connect: Telefónica's End user/Customer portal. This customers will be the ones charged for application downloads or in-app purchases.
; User: End user who wants to purchase a digital good.
; User: End user who wants to make a purchase for a digital good.
; User Agent: B2G (Firefox OS)
; User Agent: B2G


=== User sign-up and sign-in ===
=== User sign-in ===
'''In order to allow billing charges when the user can't be identified via the MSISDN transmitter over the network, the user should be registered and logged in TU ID Connect (Telefónica's customers portal).'''
This is an implementation detail of the [https://wiki.mozilla.org/WebAPI/WebPaymentProvider Payment Provider]. The <code>navigator.mozPay()</code> API does not prescribe any user authorization scheme.  


User sign-up and sign-in in TU ID Connect is beyond the scope of this document.
In [https://github.com/mozilla/webpay Mozilla's Payment Provider] implementation, users will sign-in via [https://login.persona.org/ Persona].


=== Developer registration ===
=== Developer registration ===
'''In order to submit applications that makes use of the Telefónica Carrier Billing functionality to the Mozilla Marketplace an app developer should be registered in BlueVia (Telefónica's developers portal).'''
This is an implementation detail of the [https://wiki.mozilla.org/WebAPI/WebPaymentProvider Payment Provider]. The <code>navigator.mozPay()</code> API facilities two parties -- a Developer and a Payment Provider -- in making a transaction but it does not facilitate any part of the registration process.
 
A developer who creates a JSON Web Token must do so with an Application Secret obtained from the Payment Provider. In [https://developer.mozilla.org/en-US/docs/Apps/Publishing/In-app_payments Mozilla's implementation], a developer signs up through the [https://marketplace.firefox.com/developers/ Firefox Marketplace Developer Hub], enters bank account details for payouts, obtains an Application Key and Application Secret, and can begin signing JWTs for purchase.
 
=== Initiating a Payment ===
* The user opens an Application
* The user finds something interesting to purchase. Let's say it's a game and the user wants to purchase a Magical Unicorn to excel in the game.
* The user clicks a Buy Unicorn button.
* The Application server responds by first signing a [http://openid.net/specs/draft-jones-json-web-token-07.html JSON Web Token (JWT)] using its Application Secret. The JWT is a base64-encoded signed JSON object. The JSON contains all the details about the product such as name, description, price, etc.
* The Application bubbles up the JWT to the client and calls <code>navigator.mozPay([theJWT])</code>. This begins the hosted buy flow within a special window on the User Agent.


For v1, B2G applications are going to be purchased via Mozilla Marketplace. So application developers that requires BlueVia in-app billing functionality should also be registeredd in BlueVia as part of his Mozilla Marketplace registration.
Example of server-side JWT generation in Python using [https://pypi.python.org/pypi/PyJWT-mozilla PyJWT-mozilla]:
<code><pre>
  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",
      "defaultLocale": "en",
      "locales": {
        "de": {
          "name": "Magisches Einhorn",
          "description": "Adventure Game Artikel"
        }
      }
    }
  }, APPLICATION_SECRET)
</pre></code>


A privileged developer registration API is exposed by BlueVia.
Here is a detailed explanation of the payment JWT:
*'''iss''' (mandatory): The issuer of the JWT. This is the Application Key assigned during the app registration process for this specific payment provider (e.g. Firefox Marketplace).
*'''typ''' (mandatory): The JWT type. This identifies the payment provider, e.g. Firefox Marketplace, and the JWT version that must be supported by the provider. On the User Agent, typ is used to look up white-listed Payment Providers.
*'''iat''': (mandatory) Issued at time. This is a UTC Unix timestamp of when the JWT was issued.
*'''exp''': (mandatory) Expiration. A UTC Unix timestamp of when the JWT should expire.
*'''nbf''': (optional) Not-before time. A UTC Unix timestamp of the earliest time the JWT can be processed.
*'''request''' (mandatory): Request object.
** '''id''' (mandatory): A unique identifier for the product you are selling. This only needs to be unique within your own catalog, not unique among all products from all apps.
** '''pricePoint''' (mandatory): An identifier that corresponds to a price according to the Payment Provider. For example, pricePoint 1 might translate into €0.89 when the buyer is in Europe or $0.99 when in the US, etc. The exact price point values are managed by the Payment Provider and they may change based on currency exchange rates.
** '''name''' (mandatory): A short description of the product.
** '''description''' (mandatory): A long description of the product.
** '''icons''' (optional): A map of icon URLs for the product you are selling. The keys are width/height pixel values (images must be square). The Payment Provider will use an image at the appropriate size on the payment confirmation page. For details about how Mozilla's Payment Provider handles icons, see the [https://developer.mozilla.org/en-US/docs/Web/Apps/Publishing/In-app_payments payments guide].
** '''productData''' (optional): A freeform string, no longer than 255 characters. This can be anything the app might need to identify the product with when a postback is sent back to the app.
** '''postbackURL''' (mandatory): URL where the payment processor sends an HTTP POST message to whenever a purchase completes. The application server needs to acknowledge these POST messages, or else the transactions will be canceled.
** '''chargebackURL''' (mandatory): URL where the payment processor sends an HTTP POST message to whenever a refund associated with this transaction is done.
** '''defaultLocale''' (mandatory if '''locales''' is defined, otherwise it is optional): Describes what language the '''name''' and '''description''' are in.
** '''locales''' (optional): is a map of one or more locale-specific overrides of the data contained in the in-app product, which UIs use to provide localized views based on the accessing device's locale. For example, if you have Italian readers installing your app, you probably want to give them Italian UI text. Each locale entry is keyed on a language tag ([http://www.ietf.org/rfc/rfc4646.txt RFC 4646]) and contains the keys you want to replace. You can only override name and description.


=== Application registration ===
* For a user to make a purchase, the Application must execute the Javascript method <code>navigator.mozPay()</code> 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 <code>navigator.mozPay</code> method should take the signed payment JWT or an array of them. It will return a [https://developer.mozilla.org/en/DOM/DOMRequest DOMRequest] object that the developer can use to monitor the progress of the operation.
'''In order to provide in-app billing functionality an application should be registered in BlueVia'''
<code>
This is a similar case as developer registration. Open Web Apps that requires BlueVia in-app billing capabilities should be registered via Mozilla Marketplace (or potentialy other supported marketplace) and also automatically be registered in BlueVia.
  var request = navigator.mozPay([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.mozPay() error: ' + this.error.name + ': ' + errorMsg);
  }
</code>
* The <code>navigator.mozPay</code> 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. B2G would only show the payment providers for the JWT '''typ''' values that are pre-registered in the User Agent. JWTs containing a '''typ''' value not registered in the UA would be considered as invalid.
* Once the user selects a Payment Provider the UA will open a special window (a "chrome" dialog) containing the Payment Provider's buy flow that is registered in the User Agent for the '''typ''' value of the passed JWT (e.g. Firefox Marketplace in the example above).
* Why an array of JWTs? The Developer will have a specific contract with each Payment Provider and will have a unique Application Secret for each provider. When a payment is initiated, the Application must send all JWTs. The user will select only one Payment Provider to complete the purchase. If there is only one JWT to choose from, the user will automatically use that Payment Provider, and this is the case for the first version of B2G.
* The details of the exact buy flow are implemented by the [https://wiki.mozilla.org/WebAPI/WebPaymentProvider Payment Provider]. In Mozilla's pay flow, the user logs in via Persona, enters a PIN, and is presented with details about the item to be purchased.
* The user confirms the purchase or cancels it.
* If the user cancels or something goes wrong with the payment process, the flow returns to the <code>DOMRequest.onerror</code> callback.


An Application Key and Application Secret (generated by BlueVia) should be assigned and provided to the application developer. The application developer can provide BlueVia (via Mozilla Marketplace app registration) with <code>postback</code> and <code>chargeback</code> URLs (this URLs should be editable via Mozilla Marketplace / BlueVia portal).
=== Notifications ===


The application secret will be used by the developer to sign the JWT included with the navigator.pay() function. The developer must save the application key and application secret securely in his app server. He must generate the signed JWT using server-side code.
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.


A privileged app registration/edition API is exposed by BlueVia.
The POST request will have a content-type of <code>application/x-www-form-urlencoded</code> 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.
[[File:WebPaymentAppRegistrationFlow.png|600px|thumb|center|Application registration]]
 
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.
 
==== Postback ====
 
Here is an example of a JWT POSTed via the '''notice''' parameter to '''postbackURL''' that indicates a transaction was fully processed and was successful:
 
<code><pre>
  {
    "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",
      "defaultLocale": "en",
      "locales": {
        "de": {
          "name": "Magisches Einhorn",
          "description": "Adventure Game Artikel"
        }
      }
    },
    "response": {
      "transactionID": "webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9"
    }
  }
</pre></code>
 
Here is an example response that includes just the transactionID:
 
  <code>
  HTTP/1.1 200 OK
  Content-Type: text/plain
 
  webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9
  </code>
 
==== Chargeback ====
 
Here is an example of a JWT POSTed via the '''notice''' parameter to '''chargebackURL''' that indicates a transaction was fully processed but was unsuccessful:
 
<code><pre>
  {
    "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",
      "defaultLocale": "en",
      "locales": {
        "de": {
          "name": "Magisches Einhorn",
          "description": "Adventure Game Artikel"
        }
      }
    },
    "response": {
      "transactionID": "webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9",
      "reason": "refund"
    }
  }
</pre></code>
 
A chargeback JWT might be received instead of or in addition to a postback. The response will contain a '''reason''' attribute, as follows:
; refund: The payment was refunded either upon request of the customer or by an administrator.
; reversal: A buyer has asked the credit card issuer to reverse a transaction after it has been completed. The buyer might do this through the credit card company as part of a dispute.
 
Here is an example response that includes just the transactionID:
 
  <code>
  HTTP/1.1 200 OK
  Content-Type: text/plain
 
  webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9
  </code>
 
== Refunds ==
 
Refunds are not yet supported by the <code>navigator.mozPay</code> API. '''At a future date, an Application may be able to request a refund like this''':


=== In-app payment ===
* The user installs a previously registered application via Mozilla Marketplace.
* As his daily use the user decides to purchase a digital good offered by the application.
* The app generates a payment request that contains all the information about the item being purchased: price, currency, name, description, and so on. The app signs the payment request with its app secret and encodes the whole thing as a [http://openid.net/specs/draft-jones-json-web-token-07.html  JSON Web Token (JWT)]. The developer must generate the signed JWT using server-side code. <span style="color:#800000">The Mozilla Marketplace '''might''' expose a JWT generation tool based on the payment information provided by the developer and the supported payment methods that the developer should have previously set up. (TBD)</span>. In any case, there are several libraries (like [https://github.com/progrium/pyjwt PyJWT], [https://github.com/progrium/ruby-jwt ruby-jwt], [https://github.com/hokaccha/node-jwt-simple node-jwt-simple], [https://github.com/luciferous/jwt PHP luciferous/jwt], [http://code.google.com/p/jsontoken/ Java jsontoken] and [http://json.codeplex.com/ JSON.NET]) to help encoding and decoding JWT.
Example of server-side JWT generation:
<code>
<code>
   cakeToken = jwt.encode({
   {
     "iss" : sellerIdentifier,
     "iss": APPLICATION_KEY,
     "typ" : "tu.com/payments/inapp/v1",
     "aud": "marketplace.firefox.com",
     "exp" : int(time.time() + 3600),
    "typ": "mozilla/payments/refund/v1",
     "iat" : int(time.time()),
     "exp": 1337370900,
     "request" :{
     "iat": 1337360900,
       "name" : "Piece of Cake",
     "request": {
      "description" : "Virtual chocolate cake to fill your virtual tummy",
       "transactionID": "webpay:84294ec6-7352-4dc7-90fd-3d3dd36377e9",
       "price" : "10.50",
       "reason": "User requested refund",
       "currencyCode" : "USD",
       "chargebackURL": "https://yourapp.com/payments/chargeback"
      "productData" => "my_product_id=1234&my_session_id=XYZ",
      “postbackURL” => “http://developersserver.com/postback”,
      “chargebackURL” => “http://developerserver.com/chargeback”
     }
     }
   }, APP_SECRET)
   }
</code>
 
This would initiate a refund flow and POST a confirmation JWT when completed.
 
= Payment Provider facing API =
 
See the [[WebAPI/WebPaymentProvider|WebPaymentProvider]] spec for details on how to implement a payment provider for <code>navigator.mozPay()</code>.
 
= Testing =
 
[https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS Firefox OS] and the [https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/Using_Firefox_OS_Simulator Firefox OS Simulator] ship only with settings to make real payments. If you want to test payments, you can use the simulation feature [https://developer.mozilla.org/en-US/docs/Apps/Publishing/In-app_payments described here]. To test with a custom Payment Provider you need to adjust your settings.
 
Consult the [https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/Using_the_B2G_desktop_client#Generating_a_profile B2G guide] for how to set custom settings on a device.
 
Here is a helpful setting to disable HTTPS checks for testing:
 
<code>
  pref("dom.payment.skipHTTPSCheck", true);
</code>
</code>


Here is a detailed explanation of the in-app payment JWT:
Here is a rough example of how you could add a custom Payment Provider:
;iss:
 
<code>
  pref("dom.payment.provider.1.name", "mockpayprovider");
  pref("dom.payment.provider.1.description", "Mock Payment Provider");
  pref("dom.payment.provider.1.type", "mock/payments/inapp/v1");
  pref("dom.payment.provider.1.uri", "https://mockpayprovider.phpfogapp.com/?req=");
  pref("dom.payment.provider.1.requestMethod", "GET");
</code>
 
== Test apps ==
* https://github.com/ferjm/Payment-tests
* https://github.com/kumar303/inapp-pay-test
 
= Current implementation =
[[File:MozPayFlow.jpg|600px|thumb|center|Current implementation]]
 
[1] The payment confirmation screen will not be shown if the <code>navigator.mozPay</code> call contains only one valid payment request ([https://bugzilla.mozilla.org/show_bug.cgi?id=793811 Bug 793811])
 
[2] Once the payment flow is loaded within the trusted UI the chrome code injects in the payment flow content the necessary functions to notify the platform about the successfull or failed purchase.
 
== Modules ==
=== DOM part in Gecko ===
Contains the common code that exposes the <code>navigator.mozPay</code> function to the DOM and that is supposed to be shared by all the platforms (B2G, Fennec, Firefox).
 
This code lives in [http://mxr.mozilla.org/mozilla-central/source/dom/payment/ dom/payment].
 
=== B2G Glue part in Gecko ===
Contains the specific B2G code that triggers the trusted UI creation to embed the payment flow iframe and that injects the required <code>paymentSuccess()</code> and <code>paymentFailed()</code> functions within the payment flow content.
 
It implements the <code>nsIPaymentUIGlue</code> interface
 
<code>
interface nsIPaymentUIGlueCallback
{
    void onresult(in DOMString result);
};
interface nsIPaymentUIGlue
{
    // The 'paymentRequestsInfo' contains the payment request information
    // for each JWT provided via navigator.mozPay call.   
    void confirmPaymentRequest(in jsval paymentRequestsInfo,
                              in nsIPaymentUIGlueCallback successCb,
                              in nsIPaymentUIGlueCallback errorCb);
    void showPaymentFlow(in nsIPaymentFlowInfo paymentFlowInfo,
                        in nsIPaymentUIGlueCallback errorCb);
};
</code>
 
This code lives in [http://mxr.mozilla.org/mozilla-central/source/b2g/components/PaymentGlue.js b2g/components] and [http://mxr.mozilla.org/mozilla-central/source/b2g/chrome/content/payment.js b2g/chrome/content].
 
=== Gaia part ===
Contains the require code to create, open and close the trusted UI and the payment confirmation screen.
 
This code lives in the Gaia system app, specifically [https://github.com/mozilla-b2g/gaia/blob/master/apps/system/js/payment.js apps/system/js/payment.js] and [https://github.com/mozilla-b2g/gaia/blob/master/apps/system/js/popup_manager.js apps/system/js/popup_manager.js]
 
== Communication ==
The communication between Gecko and Gaia is done via <code>mozChromeEvent</code> and <code>mozContentEvent</code>, which is a communication mechanism between chrome and the Gaia system app. <code>mozChromeEvent</code> are sent from chrome to content and <code>mozContentEvent</code> are sent from content to crhome. Where "sent" is used loosely here, since "broascast" is probably a better term. This communication is required to control the creation and closure of the trusted UI and to inject the required functions to notify the success or failure of the payment flow within the payment provider content.
 
= Helper Libraries =
 
These are specific <code>navigator.mozPay()</code> libraries:
* [http://mozpay.readthedocs.org/en/latest/ mozpay-py], a Python library
* [https://github.com/mozilla/mozpay-js mozpay-js], a NodeJS library
 
These are generic JWT libraries that you can use:
* [https://pypi.python.org/pypi/PyJWT-mozilla PyJWT-mozilla]
* [https://github.com/progrium/ruby-jwt ruby-jwt]
* [https://github.com/hokaccha/node-jwt-simple node-jwt-simple]
* [https://github.com/luciferous/jwt PHP luciferous/jwt]
* [http://code.google.com/p/jsontoken/ Java jsontoken]
* [http://json.codeplex.com/ JSON.NET]
 
= See also =
* [[WebAPI/WebPaymentProvider|WebPaymentProvider]]
* [http://www.w3.org/community/webpayments/ W3C WebPayments working group]
* [https://docs.google.com/document/d/1NLKbHVPQXa9uvDBC3cfgOD7sIrtIxi0qDoXMQrxcCsI/edit First draft]
* [https://wiki.mozilla.org/Security/Reviews/navigator.pay Security review]
* [https://docs.google.com/document/d/1EBkzFye6DqCQkMEkXkjLjMvaYYCqvCGJrYhyG38R4DU/edit#heading=h.q13jnkaxpfle Threat model]
* [https://developer.mozilla.org/en-US/docs/Apps/Publishing/In-app_payments Firefox Marketplace In-app payments]
* [https://wiki.mozilla.org/B2G/QA/WebAPI_Test_Plan/WebPayment Test Plan]
* [https://github.com/mozilla/apps-payment-server/ Original Mozilla Labs Prototype]
 
= Similar APIs =
* [https://developers.google.com/commerce/wallet/digital/ Google Wallet's in-app payment API]
* [https://developer.apple.com/appstore/in-app-purchase/index.html iOS in-app payment API]
 
[[Category:Web APIs]]

Latest revision as of 20:39, 25 February 2016

WebPayment API Specification (DRAFT)

Note: This API is in the process of being removed from Android and Desktop, long term status in B2G is unclear.

Goal

The goal of this API is to enable web content to collect payment (or issue a refund) for a virtual good via the navigator.mozPay() function.

Status

See bug 767818 for the navigator.mozPay() implementation.

Currently only implemented for B2G.

Proposed API

Expose the pay function to the navigator object

interface nsIDOMNavigatorPayment
{
  DOMRequest mozPay(in jsval jwts);
}

Proposers

Andreas Gal and Fernando Jiménez. Based on Mike Hanson's and Kumar McMillan's work for Firefox Marketplace in-app payments.

WebPayments Architecture (DRAFT)

Introduction

An open web app will interact with a Payment Provider via navigator.mozPay() 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.

Payment flow overview

  • The app initiates a payment by signing a JSON Web Token (JWT) request and calling navigator.mozPay().
  • This starts the buyflow in a content iframe inside a trusted dialog ("chrome dialog").
  • A purchasing flow is served from the Payment Provider's server as an HTML5 document inside the trusted dialog.
  • The buyer is authenticated by the Payment Provider (via the network (radio), Mozilla Persona assertion or whatever authentication mechanisms the Payment Provider chooses).
  • The buyer completes or cancels the purchase.
  • The app receives a Javascript callback when the buyer completes or cancels the purchase.
  • The app server receives a signed POST request with a Payment Provider transaction identifier indicating that the purchase was completed successfully (or it failed).

Detailed payment flow

This detailed payment flow is based on Mozilla's initial implementation for Firefox OS but it could be extended to any other Payment Provider.

Definitions

Application
Open Web App which offers digital goods to be sold. OWAs charge users via navigator.mozPay() and require a client and server.
Application Key
A public identifier that can be transmitted in a JSON object so that a Payment Provider can identify the Application.
Application Secret
A private string shared between Developer and Payment Provider. This will be used to sign JSON Web Tokens (JWTs) and must be securely protected on a web server.
Developer
Application developer, seller of digital goods.
Firefox Marketplace
Developer portal and application repository. Developers can submit apps to the Firefox Marketplace so users can purchase and download the apps. The Firefox Marketplace uses the navigator.mozPay function to charge users for application purchases.
Payment Provider
A client/server web application that serves content in a special iframe controlled by navigator.mozPay(). This conforms to the Payment Provider spec. The provider accepts payment from a user and disperses income to the Developer.
User
End user who wants to purchase a digital good.
User Agent
B2G (Firefox OS)

User sign-in

This is an implementation detail of the Payment Provider. The navigator.mozPay() API does not prescribe any user authorization scheme.

In Mozilla's Payment Provider implementation, users will sign-in via Persona.

Developer registration

This is an implementation detail of the Payment Provider. The navigator.mozPay() API facilities two parties -- a Developer and a Payment Provider -- in making a transaction but it does not facilitate any part of the registration process.

A developer who creates a JSON Web Token must do so with an Application Secret obtained from the Payment Provider. In Mozilla's implementation, a developer signs up through the Firefox Marketplace Developer Hub, enters bank account details for payouts, obtains an Application Key and Application Secret, and can begin signing JWTs for purchase.

Initiating a Payment

  • The user opens an Application
  • The user finds something interesting to purchase. Let's say it's a game and the user wants to purchase a Magical Unicorn to excel in the game.
  • The user clicks a Buy Unicorn button.
  • The Application server responds by first signing a JSON Web Token (JWT) using its Application Secret. The JWT is a base64-encoded signed JSON object. The JSON contains all the details about the product such as name, description, price, etc.
  • The Application bubbles up the JWT to the client and calls navigator.mozPay([theJWT]). This begins the hosted buy flow within a special window on the User Agent.

Example of server-side JWT generation in Python using PyJWT-mozilla:

  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",
      "defaultLocale": "en",
      "locales": {
        "de": {
          "name": "Magisches Einhorn",
          "description": "Adventure Game Artikel"
        }
      }
    }
  }, APPLICATION_SECRET)

Here is a detailed explanation of the payment JWT:

  • iss (mandatory): The issuer of the JWT. This is the Application Key assigned during the app registration process for this specific payment provider (e.g. Firefox Marketplace).
  • typ (mandatory): The JWT type. This identifies the payment provider, e.g. Firefox Marketplace, and the JWT version that must be supported by the provider. On the User Agent, typ is used to look up white-listed Payment Providers.
  • iat: (mandatory) Issued at time. This is a UTC Unix timestamp of when the JWT was issued.
  • exp: (mandatory) Expiration. A UTC Unix timestamp of when the JWT should expire.
  • nbf: (optional) Not-before time. A UTC Unix timestamp of the earliest time the JWT can be processed.
  • request (mandatory): Request object.
    • id (mandatory): A unique identifier for the product you are selling. This only needs to be unique within your own catalog, not unique among all products from all apps.
    • pricePoint (mandatory): An identifier that corresponds to a price according to the Payment Provider. For example, pricePoint 1 might translate into €0.89 when the buyer is in Europe or $0.99 when in the US, etc. The exact price point values are managed by the Payment Provider and they may change based on currency exchange rates.
    • name (mandatory): A short description of the product.
    • description (mandatory): A long description of the product.
    • icons (optional): A map of icon URLs for the product you are selling. The keys are width/height pixel values (images must be square). The Payment Provider will use an image at the appropriate size on the payment confirmation page. For details about how Mozilla's Payment Provider handles icons, see the payments guide.
    • productData (optional): A freeform string, no longer than 255 characters. This can be anything the app might need to identify the product with when a postback is sent back to the app.
    • postbackURL (mandatory): URL where the payment processor sends an HTTP POST message to whenever a purchase completes. The application server needs to acknowledge these POST messages, or else the transactions will be canceled.
    • chargebackURL (mandatory): URL where the payment processor sends an HTTP POST message to whenever a refund associated with this transaction is done.
    • defaultLocale (mandatory if locales is defined, otherwise it is optional): Describes what language the name and description are in.
    • locales (optional): is a map of one or more locale-specific overrides of the data contained in the in-app product, which UIs use to provide localized views based on the accessing device's locale. For example, if you have Italian readers installing your app, you probably want to give them Italian UI text. Each locale entry is keyed on a language tag (RFC 4646) and contains the keys you want to replace. You can only override name and description.
  • For a user to make a purchase, the Application must execute the Javascript method navigator.mozPay() 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.mozPay 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.mozPay([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.mozPay() error: ' + this.error.name + ': ' + errorMsg);
 }

  • The navigator.mozPay 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. B2G would only show the payment providers for the JWT typ values that are pre-registered in the User Agent. JWTs containing a typ value not registered in the UA would be considered as invalid.
  • Once the user selects a Payment Provider the UA will open a special window (a "chrome" dialog) containing the Payment Provider's buy flow that is registered in the User Agent for the typ value of the passed JWT (e.g. Firefox Marketplace in the example above).
  • Why an array of JWTs? The Developer will have a specific contract with each Payment Provider and will have a unique Application Secret for each provider. When a payment is initiated, the Application must send all JWTs. The user will select only one Payment Provider to complete the purchase. If there is only one JWT to choose from, the user will automatically use that Payment Provider, and this is the case for the first version of B2G.
  • The details of the exact buy flow are implemented by the Payment Provider. In Mozilla's pay flow, the user logs in via Persona, enters a PIN, and is presented with details about the item to be purchased.
  • The user confirms the purchase or cancels it.
  • If the user cancels or something goes wrong with the payment process, the flow returns to the DOMRequest.onerror callback.

Notifications

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.

Postback

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",
      "defaultLocale": "en",
      "locales": {
        "de": {
          "name": "Magisches Einhorn",
          "description": "Adventure Game Artikel"
        }
      }
    },
    "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
 

Chargeback

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",
      "defaultLocale": "en",
      "locales": {
        "de": {
          "name": "Magisches Einhorn",
          "description": "Adventure Game Artikel"
        }
      }
    },
    "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:

refund
The payment was refunded either upon request of the customer or by an administrator.
reversal
A buyer has asked the credit card issuer to reverse a transaction after it has been completed. The buyer might do this through the credit card company as part of a dispute.

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

Refunds are not yet supported by the navigator.mozPay 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.

Payment Provider facing API

See the WebPaymentProvider spec for details on how to implement a payment provider for navigator.mozPay().

Testing

Firefox OS and the Firefox OS Simulator ship only with settings to make real payments. If you want to test payments, you can use the simulation feature described here. To test with a custom Payment Provider you need to adjust your settings.

Consult the B2G guide for how to set custom settings on a device.

Here is a helpful setting to disable HTTPS checks for testing:

 pref("dom.payment.skipHTTPSCheck", true);

Here is a rough example of how you could add a custom Payment Provider:

 pref("dom.payment.provider.1.name", "mockpayprovider");
 pref("dom.payment.provider.1.description", "Mock Payment Provider");
 pref("dom.payment.provider.1.type", "mock/payments/inapp/v1");
 pref("dom.payment.provider.1.uri", "https://mockpayprovider.phpfogapp.com/?req=");
 pref("dom.payment.provider.1.requestMethod", "GET");

Test apps

Current implementation

 
Current implementation

[1] The payment confirmation screen will not be shown if the navigator.mozPay call contains only one valid payment request (Bug 793811)

[2] Once the payment flow is loaded within the trusted UI the chrome code injects in the payment flow content the necessary functions to notify the platform about the successfull or failed purchase.

Modules

DOM part in Gecko

Contains the common code that exposes the navigator.mozPay function to the DOM and that is supposed to be shared by all the platforms (B2G, Fennec, Firefox).

This code lives in dom/payment.

B2G Glue part in Gecko

Contains the specific B2G code that triggers the trusted UI creation to embed the payment flow iframe and that injects the required paymentSuccess() and paymentFailed() functions within the payment flow content.

It implements the nsIPaymentUIGlue interface


interface nsIPaymentUIGlueCallback
{
   void onresult(in DOMString result);
};

interface nsIPaymentUIGlue
{
   // The 'paymentRequestsInfo' contains the payment request information
   // for each JWT provided via navigator.mozPay call.    
   void confirmPaymentRequest(in jsval paymentRequestsInfo,
                              in nsIPaymentUIGlueCallback successCb,
                              in nsIPaymentUIGlueCallback errorCb);

   void showPaymentFlow(in nsIPaymentFlowInfo paymentFlowInfo,
                        in nsIPaymentUIGlueCallback errorCb);
};

This code lives in b2g/components and b2g/chrome/content.

Gaia part

Contains the require code to create, open and close the trusted UI and the payment confirmation screen.

This code lives in the Gaia system app, specifically apps/system/js/payment.js and apps/system/js/popup_manager.js

Communication

The communication between Gecko and Gaia is done via mozChromeEvent and mozContentEvent, which is a communication mechanism between chrome and the Gaia system app. mozChromeEvent are sent from chrome to content and mozContentEvent are sent from content to crhome. Where "sent" is used loosely here, since "broascast" is probably a better term. This communication is required to control the creation and closure of the trusted UI and to inject the required functions to notify the success or failure of the payment flow within the payment provider content.

Helper Libraries

These are specific navigator.mozPay() libraries:

These are generic JWT libraries that you can use:

See also

Similar APIs