Validating a receipt Redirect 2

If you have a paid app, you should check that a user has a valid receipt. If you do not check, someone can buy the app, get an immediate refund, and then have the app for free. Or someone can simply install the app by referencing the manifest file directly from your site. This article goes through the code and workflow needed to build in your own receipt validation.

A receipt is proof that an app buyer has purchased your app. A receipt encodes an affirmation by a payment processor that a particular user has completed a transaction to acquire a particular digital product. The receipt is sent to the user's machine after the user has paid for an app on the Firefox Marketplace. The receipt is placed on the user's machine using the second argument to the mozApps.install() method. The Firefox Marketplace calls install() for your app using the receipt.

When to validate a receipt

You have to decide when the app will validate the receipt. A usual time to validate is when the user starts the app. If it's a long-running app, you might want to validate on a regular schedule. An app that streams video, for example, might check the receipt every 20 minutes.

Note that if the user is offline, the app will not be able to validate the receipt. What the app does in this situation is up to you. To avoid some user annoyance you might want to be lax on enforcing the receipt checking (let's say the user is temporarily in a tunnel), and just keep trying until the user is online again.

How to validate a receipt

The simplest way to validate a receipt is to use the Firefox Marketplace validation service. If your app is HTML-only, where the server doesn't do anything but serve static files, you can use Mozilla's receiptverifier JavaScript library. It basically involves including the receiptverifier.js script in your app and using the verifyReceipts() function. You can find more information at the previous link.

Receipt contents

The receipt used by an Open Web app is a portable, verifiable proof of purchase token. It is a digitally-signed JSON data structure called a JSON Web Token (JWT). This format is universally readable by clients and servers. There are JWT libraries available for many programming languages.

What receipts you should accept

The receipt is proof of payment, but it is up to the app developer to decide what kind of receipts you would like to accept. Here's a list of the things you should check:

  • The receipt is from a store you are selling the app from. Check that the iss field is in your whitelist.
  • The receipt is for your app. For hosted apps, check that the product URL field is correct. For packaged apps, check that the product storeData is correct until bug 867265 is resolved. The value of storeData is not known until a receipt is issued, but will remain constant from then on.
  • The verification URL is at a domain or subdomain of the store. Check that the verify URL is a subdomain of the iss field.
  • The receipt is for a purchase and not a test receipt. Check the typ field.

The receipt verifier library will do some of these checks for you.

Test receipts

During development the Firefox Marketplace can issue test receipts so that the apps lifecycle can be completely tested. These receipts will have a typ of test-receipt. The app should accept these receipts during development and then not accept them once on sale. Doing so would allow anyone to bypass the sales process and use a test receipt in your app.

By default the receipt verifier library does not allow test-receipts.

Receipt verification

Without using a JWT library, you can find the URL for the validation service in the verify field in the receipt, and then send the receipt to the validation service. Each app has a different URL to the Firefox Marketplace validation service.

The code sample below will show the verify URL in the receipt. The code is set up to be used in the Firefox web console developer tool.

var request = navigator.mozApps.getSelf();
request.onsuccess = function() {
  // Get the app's receipt and decode it
  console.log(atob(request.result.receipts[0].split('~')[1].split('.')[1])); 
};

To get the verify URL, this sample does the following things:

  • It saves the return from mozApps.getSelf() — a reference to the current app — in a request variable.
  • If the request is successful, the app's first receipt is retrieved, and the value fed in from JWT is split to get the actual part that contains the receipt.
  • It decodes the Base64-encoded receipt using the JavaScript Window.atob function.

The following is a pretty-printed example of the result of the code above. It is a full receipt, minus the JWT parts.

{
  "product": {
    "url": "http://example.com",
    "storedata": "id=111111"
  },
  "iss": "https://marketplace.mozilla.org",
  "verify": "https://receiptcheck.marketplace.mozilla.org/verify/111111", // The verify URL
  "detail": "https://marketplace.mozilla.org/en-US/purchases/111111",
  "reissue": "https://marketplace.mozilla.org/en-US/app/example/purchase/reissue",
  "user": {
    "type": "directed-identifier",
    "value": "1234-abcd99ef-a123-456b-bbbb-cccc11112222"
  },
  "exp": 1353028900,
  "iat": 1337304100,
  "typ": "purchase-receipt",
  "nbf": 1337304100
}

Once you get the verify URL, use the POST method to send the full JWT in the body of a message. The validation service response is described above. The following snippet would get the full JWT of the app's first receipt in the sample above.

request.result.receipts[0]

Note that the cryptographic aspects of a JWT are not dealt with in the code above. If you verify the receipt with the Marketplace it will check the cryptographic signature itself.

Note: You can find another, more expansive example of receipt verification in Kumar McMillan's Private Yacht example.

Combating app piracy

Even if you validate receipts for your paid app, it can be pirated if someone passes around the receipt. The receipt validation methods given above do not prevent this.

If you want a better defense against piracy, you might want to set up a proxy server that will be an intermediary between the app and the Firefox Marketplace. The proxy server can check the receipts, IP addresses and other things. The server might do something like notice if the same receipt was coming from different IP addresses and take appropriate action. This setup is probably more appropriate with big complicated apps that use server processing.

This Python code is an in-progress project that could give you some ideas for a proxy server. Django Receipts is a test proxy that will validate receipts. You shouldn't use this as a production proxy but you can learn from it as a test. Here is more information on verifying receipts yourself.

Receipt fields

A receipt contains the following fields:

typ
A string identifying the type of receipt. It must be one of:
  • purchase-receipt - A receipt issued when a transaction is completed. This type of receipt should be accepted by the app at all times.
  • developer-receipt - A receipt issued to the developer of the app. Usually they will be issued by the store for the use of the developer. This receipt may have a short expiry.
  • reveiwer-receipt - A receipt issued to the reviewer of the app. Usually they will be issued by the store for reviewers of the app. This receipt only needs to be accepted during the review period. This receipt may have a short expiry
  • test-receipt - A receipt issued to test the app during development. This receipt SHOULD NOT be accepted, except during development. This receipt may have a short expiry.
product
JSON object identifying the product that the receipt covers and any store-specific data. It has the following fields:
  • url - URL representing the root of a domain, without a trailing slash (for example, "https://someapp.com"). This is conventionally defined to represent "a web application". URLs rooted further inside the site are conventionally defined to represent "in-application purchases", and can use whatever path scheme is convenient to the developer and issuer of the receipt.
  • storedata - A string that uniquely identifies this app for the verifier of the receipt.
user
JSON object containing a user ID for the user who made the purchase. It has the following fields:
  • type - A string with the value "directed-identifier".
  • value - A string that is a unique ID for the user. A given user will show up as a different user ID for each app purchased.
iss
Domain for the store that issued the receipt.
nbf
"Not-before" timestamp indicating when the purchase was completed. The timestamp is the number of seconds from 1970-01-01T00:00:00Z in UTC, RFC 3339.
iat
"Issued-at" timestamp indicating when the receipt was issued. Same timestamp format as nbf. You can use this value to determine the age of the receipt.
exp
(optional) Expiry timestamp indicating when the receipt will expire. Same timestamp format as nbf.
detail
(optional) URL that contains additional human- or machine-readable detail about the purchase. If a transaction log or refund capability is provided for the purchase, it is expected that this page will contain those functions.
verify
(optional) URL that can be used by an authenticated application to verify a receipt. Note that the Firefox Marketplace always provides this field for an app. If you are going to create your own app marketplace, you might not use this field.
reissue
(optional) URL that can be used to re-issue a new receipt.

Document Tags and Contributors

Contributors to this page: Sheppy
Last updated by: Sheppy,