mozilla

Revision 351809 of Validating a receipt

  • Revision slug: Apps/Validating_a_receipt
  • Revision title: Validating a receipt
  • Revision id: 351809
  • Created:
  • Creator: markg
  • Is current revision? No
  • Comment bring back payments info

Revision Content

{{MarketplaceStatus()}}

Note: Paid apps and in-app payments are currently disabled in the Mozilla Marketplace (August 2012). It is expected that they will be available again in October 2012.

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 Mozilla Marketplace. The receipt is placed on the user's machine using the second argument to the mozApps.install() method. The Mozilla Marketplace calls install() for your app using the receipt from the PayPal payment operation.

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 using the manifest on your site.

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 Mozilla Marketplace validation service. If your app is HTML-only, where the server doesn't do anything but serve static files, you can use the following JavaScript library:

https://github.com/mozilla/receiptverifier

Include the receiptverifier.js script in your app and use the verifyReceipts() function. See the GitHub page for more information.

More information on validation

You might also need to handle the validation in a more manual way, so here is more information on that.

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.

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 Mozilla 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 req = window.navigator.mozApps.mgmt.getAll();
req.onsuccess = function() {
  // Get the first receipt of the first app and decode it
  console.log(atob(req.result[0].receipts[0].split('~')[1].split('.')[1])); 
};

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

  • Saves the return from mozApps.mgmt.getAll() in a variable. getAll() returns an array of App objects.
  • Goes into the first App object, and splits up the JWT to get the part that contains the receipt.
  • Decodes the Base64-encoded receipt using the JavaScript 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 the first receipt of the first app in the sample above.

req.result[0].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.

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 Mozilla 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
This must be the string "purchase-receipt".
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 he purchases.
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 Mozilla 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.

Revision Source

<p>{{MarketplaceStatus()}}</p>
<div class="note">
  <p><strong>Note:</strong>&nbsp;Paid apps and in-app payments are currently disabled in the Mozilla Marketplace (August 2012). It is expected that they will be available again in October 2012.</p>
</div>
<p>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 Mozilla Marketplace.&nbsp;The receipt is placed on the user's machine using the second argument to the&nbsp;<a href="/en/DOM/Apps.install" title="https://developer.mozilla.org/en/Apps/Apps_JavaScript_API/navigator.mozApps.install">mozApps.install()</a>&nbsp;method. The Mozilla Marketplace calls <code>install()</code>&nbsp;for your app using the receipt from the PayPal payment operation.</p>
<p>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 using the manifest on your site.</p>
<h3 id="When_to_validate_a_receipt">When to validate a receipt</h3>
<p>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.</p>
<p>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.</p>
<h3 id="How_to_validate_a_receipt">How to validate a receipt</h3>
<p>The simplest way to validate a receipt is to use the Mozilla Marketplace validation service. If your app is HTML-only, where the server doesn't do anything but serve static files, you can use the following JavaScript library:</p>
<p><a class="link-https" href="https://github.com/mozilla/receiptverifier" title="https://github.com/mozilla/receiptverifier">https://github.com/mozilla/receiptverifier</a></p>
<p>Include the <code>receiptverifier.js</code> script in your app and use the <code>verifyReceipts()</code>&nbsp;function. See the GitHub page for more information.</p>
<h3 id="More_information_on_validation">More information on validation</h3>
<p>You might also need to handle the validation in a more manual way, so here is more information on that.</p>
<p>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&nbsp;<a class="external" href="http://self-issued.info/docs/draft-jones-json-web-token.html" title="http://self-issued.info/docs/draft-jones-json-web-token.html">JSON Web Token</a>&nbsp;(JWT). This format is universally readable by clients and servers. There are JWT libraries available for many programming languages.</p>
<p>Without using a JWT library, you can find the URL for the validation service in the <code>verify</code> field in the receipt, and then send the receipt to the validation service. Each app has a different URL to the Mozilla Marketplace validation service.</p>
<p>The code sample below will show the <code>verify</code> URL in the receipt. The code is set up to be used in the Firefox web console developer tool.</p>
<pre class="brush: js">
var req = window.navigator.mozApps.mgmt.getAll();
req.onsuccess = function() {
  // Get the first receipt of the first app and decode it
  console.log(atob(req.result[0].receipts[0].split('~')[1].split('.')[1])); 
};</pre>
<p>To get the <code>verify</code> URL, this sample does the following things:</p>
<ul>
  <li>Saves the return from&nbsp;<a href="/en/DOM/Apps.mgmt.getAll" title="https://developer.mozilla.org/en/DOM/Apps.mgmt.getAll"><code>mozApps.mgmt.getAll()</code></a> in a variable. <code>getAll()</code> returns an array of <a href="/en/DOM/App" title="https://developer.mozilla.org/en/Apps/Apps_JavaScript_API/app_object"><code>App</code></a> objects.</li>
  <li>Goes into the first <code>App</code> object, and splits up the JWT&nbsp;to get the part that contains the receipt.</li>
  <li>Decodes the Base64-encoded receipt using&nbsp;the JavaScript <a href="/en/DOM/window.atob" title="https://developer.mozilla.org/en/DOM/window.atob"><code>atob()</code></a> function.</li>
</ul>
<p>The following is a pretty-printed example of the result of the code above. It is a full receipt, minus the JWT parts.</p>
<pre class="brush: js">
{
  "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
}</pre>
<p>Once you get the <code>verify</code> 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 the first receipt of the first app in the sample above.</p>
<pre>
req.result[0].receipts[0]</pre>
<p>Note that the cryptographic aspects of a JWT are not dealt with in the code above.&nbsp; If you verify the receipt with the Marketplace it will check the cryptographic signature itself.</p>
<h3 id="Combating_app_piracy">Combating app piracy</h3>
<p>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.</p>
<p>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 Mozilla 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.</p>
<p><a class="link-https" href="https://github.com/andymckay/receipts" title="https://github.com/andymckay/receipts">This Python code</a> is an in-progress project that could give you some ideas for a proxy server.&nbsp;<a class="external" href="http://django-receipts.herokuapp.com/" title="http://django-receipts.herokuapp.com/">Django Receipts</a> 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.&nbsp;<a class="link-https" href="https://wiki.mozilla.org/Apps/WebApplicationReceipt" title="https://wiki.mozilla.org/Apps/WebApplicationReceipt">Here is more information</a> on verifying receipts yourself.</p>
<h3 id="Receipt_fields">Receipt fields</h3>
<p>A receipt contains the following fields:</p>
<p><span id="cke_bm_88C" style="display: none;">&nbsp;</span></p>
<dl>
  <dt>
    typ</dt>
  <dd>
    This must be the string "purchase-receipt".</dd>
  <dt>
    product</dt>
  <dd>
    JSON object identifying the product that the receipt covers and any store-specific data. It has the following fields:
    <ul>
      <li><code>url</code> - URL representing the root of a domain, without a trailing slash (for example, "<a class="link-https" href="https://someapp.com" rel="freelink">https://someapp.com</a>").&nbsp;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.</li>
      <li><code>storedata</code> - A string that uniquely identifies this app for the verifier of the receipt.</li>
    </ul>
  </dd>
  <dt>
    user</dt>
  <dd>
    JSON object containing a user ID for the user who made the purchase. &nbsp;It has the following fields:
    <ul>
      <li><code>type</code> - A string with the value <code>"directed-identifier"</code>.</li>
      <li><code>value</code> - A string that is a unique ID for the user. A given user will show up as a different user ID for each app he purchases.</li>
    </ul>
  </dd>
  <dt>
    iss</dt>
  <dd>
    Domain for the store that issued the receipt.</dd>
  <dt>
    nbf</dt>
  <dd>
    "Not-before" timestamp indicating when the purchase was completed.&nbsp;The timestamp is the number of seconds from 1970-01-01T00:00:00Z in UTC, RFC 3339.</dd>
  <dt>
    iat</dt>
  <dd>
    "Issued-at" timestamp indicating when the receipt was issued.&nbsp;Same timestamp format as nbf.&nbsp;You can use this value to determine the age of the receipt.</dd>
  <dt>
    exp</dt>
  <dd>
    (optional) Expiry timestamp&nbsp;indicating when the receipt will expire.&nbsp;Same timestamp format as nbf.</dd>
  <dt>
    detail</dt>
  <dd>
    (optional) URL that contains additional human- or machine-readable detail about the purchase.&nbsp;If a transaction log or refund capability is provided for the purchase, it is expected that this page will contain those functions.</dd>
  <dt>
    verify</dt>
  <dd>
    (optional) URL that can be used by an authenticated application to verify a receipt. Note that the Mozilla Marketplace always provides this field for an app. If you are going to create your own app marketplace, you might not use this field.</dd>
  <dt>
    reissue</dt>
  <dd>
    (optional) URL that can be used to re-issue a new receipt.</dd>
</dl>
Revert to this revision