Skip to content
This repository was archived by the owner on Mar 15, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 81 additions & 68 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,67 +61,76 @@ Set Up Your Products

Log into the `Firefox Marketplace Developer Hub`_. There will be a page
where you can enter the names and prices for each of your products.
These docs will be updated with a link when the page exists :)
These docs will be updated with a link when the page is working :)

When you create a product on the Developer Hub you'll get
a unique identifier, such as ``543123``.
You'll use this ID number to reference the product when
working with the ``fxpay`` library.

Initialization
~~~~~~~~~~~~~~

When your app starts up, you need to initialize ``fxpay`` so it can
check for any existing product receipts. This is also your chance to
register some callbacks for general error handling and other events.

::

fxpay.init({
onerror: function(error) {
console.error('An error occurred:', error);
},
oninit: function() {
console.log('fxpay initialized without errors');
}
});

Capture A Purchase
~~~~~~~~~~~~~~~~~~

When you create a product on the Developer Hub you'll get
unique identifiers for each product, such as ``543123``.
Make a screen in your app where you offer a product for purchase.
Create a buy button that when tapped, runs this code::
You can call ``fxpay.purchase()`` to start the buy flow for an
item.
First, you'll probably want to make a screen in your app
where you offer some product for purchase.
Create a buy button that when tapped, calls ``fxpay.purchase()`` like this::

var productId = 543123;

fxpay.purchase(productId, {
onpurchase: function(err) {
if (err) {
throw err;
} else {
console.log('product', productId, 'has been purchased!');
// It is now safe to deliver the product to your user.
}
fxpay.purchase(productId, function(error, info) {
if (error) {
throw error;
}

console.log('product', info.productId, 'was purchased and verified!');
// ***************************************************
// It is now safe to deliver the product to your user.
// ***************************************************
});

When the ``onpurchase()`` callback is executed, the item has been
verifiably purchased. It is safe to deliver the item.
This ``productId`` is the same one you got from the Developer Hub
when you set up your products.

When the user completes the buy flow and the Marketplace server has
verified the receipt, the callback you provided will be invoked with error
string (if applicable) and an ``info`` parameter that provides info about
the purchase. At this time, it is safe to deliver the item.

How does this work? The ``fxpay.purchase()`` function automates
the process of calling `mozPay()`_ then
waiting for and verifying an incoming JWT signature.
If you want to know the specifics, see the `in-app payments guide`_
but you could follow this guide start to finish and you'd already be
doing payments.

``fxpay.purchase()`` kicks the user into a buyflow. When the user
completes the payment, the payment window closes and they are returned
to your app; ``fxpay`` waits for a postback message.
It would be a good idea to show the user a progress indicator
while they wait using ``oncheckpayment()`` like this::

fxpay.purchase(productId, {
oncheckpayment: function() {
// Show a progress indicator in your UI
// while the payment is being checked.
},
onpurchase: function(err) {
if (err) {
throw err;
} else {
// It is now safe to deliver the product to your user.
}
}
});
but that's not mandatory for using the ``fxpay`` library.

.. _`in-app payments guide`: https://developer.mozilla.org/en-US/Marketplace/Monetization/In-app_payments
.. _`Firefox Marketplace Developer Hub`: https://marketplace.firefox.com/developers/

Errors
~~~~~~

Errors come back to you as the first argument to the ``onpurchase(err)``
callback. If no error occurs, ``err`` will be null.
Errors come back to you as the first argument to the ``onerror(error)`` callback
that was passed to ``fxpay.init()`` or as the first argument to the
``fxpay.purchase()`` callback.
The errors are strings and are
meant to be treated like readable codes that you can map to localized text, etc.
A detailed error explanation will be logged; read on for logging details.
Expand All @@ -148,14 +157,18 @@ Here are the possible error strings you might receive and what they mean:
You can probably ignore this error or maybe display a
cancelled message. This error comes from `mozPay()`_.

**INCORRECT_USAGE**
An ``fxpay`` function was used incorrectly. Check the console
for details.

**INVALID_TRANSACTION_STATE**
The transaction was in an invalid state and cannot be processed.

**NOT_STARTED**
The library did not start up yet; no actions can be
performed. Check the console for details on the startup failure.
This could also mean that the library encountered an unexpected
exception.
**NOT_INITIALIZED**
The library was not initialized correctly; no actions can be
performed. This might mean you didn't call ``init()`` or it
could mean there was an uncaught exception. Check the console for
details.

**NOT_INSTALLED_AS_APP**
This platform supports apps but the app has not been installed
Expand Down Expand Up @@ -187,39 +200,38 @@ By default, ``fxpay`` logs everything using `window.console`_. If you want to
replace ``console`` with your own logger, pass in an object as ``log``
that implements the same `window.console`_ methods::

fxpay.purchase(productId, {
onpurchase: function(err) {
if (err) {
throw err;
}
},
fxpay.configure({
log: myConsole
});

.. _`window.console`: https://developer.mozilla.org/en-US/docs/Web/API/console
Configuration
~~~~~~~~~~~~~

Startup
~~~~~~~
You can call ``fxpay.configure(overrides)`` to set some internal variables.
If you call this repeatedly, the old keys will be preserved unless
overidden.

Example::

The ``fxpay`` library has to initialize itself with the mozApps
API when it starts up. You cannot call ``fxpay.purchase()`` until
it has started successfully. To get notified on startup,
define a global function called ``_fxpay_onstart`` *before* you
load the ``fxpay.js`` library into the page. Here is an example::
fxpay.configure({log: myCustomLog});

<script type="text/javascript">
Possible overrides:

window._fxpay_onstart = function(error) {
if (error) {
console.error('fxpay startup error:', error);
}
};
*apiUrlBase*
The base URL of the internal ``fxpay`` API.
Default: ``https://marketplace.firefox.com``.

</script>
<script type="text/javascript" src="fxpay.js"></script>
*apiTimeoutMs*
A length of time in milleseconds until any API request will time out.
Default: 10000.

If an error occurs during startup, that same error will be returned
when you first call ``fxpay.purchase()``.
*apiVersionPrefix*
A Path that gets appended to ``apiUrlBase`` to access the right API version.
Default: ``/api/v1``.

*log*
A log object compatible with `window.console`_ to use internally.
Default: ``window.console``.


Developers
Expand Down Expand Up @@ -267,3 +279,4 @@ The compressed source file will appear in the ``build`` directory.
.. _`NodeJS`: http://nodejs.org/
.. _`npm`: https://www.npmjs.org/
.. _`mozPay()`: https://developer.mozilla.org/en-US/docs/Web/API/Navigator.mozPay
.. _`window.console`: https://developer.mozilla.org/en-US/docs/Web/API/console
55 changes: 34 additions & 21 deletions example/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,20 @@ $(function() {
throw 'unknown API env: ' + env;
}
console.log('setting API to', apiUrlBase);
fxpay.configure({apiUrlBase: apiUrlBase});
}

function addProduct(parent, prodID, prod, opt) {
function addProduct(parent, prodID, prodData, opt) {
opt = opt || {showBuy: true};
var li = $('<li></li>', {class: 'product'});
li.append($('<img />', {src: prod.icons['64'], height: 64, width: 64}));
li.append($('<img />', {src: prodData.icons['64'],
height: 64, width: 64}));
if (opt.showBuy) {
li.append($('<button>Buy</button>').data({productId: prodID, product: prod}));
li.append($('<button>Buy</button>').data({productId: prodID,
product: prodData}));
}
li.append($('<h3>' + prod.name + '</h3>'));
li.append($('<p>' + prod.description + '</p>'));
li.append($('<h3>' + prodData.name + '</h3>'));
li.append($('<p>' + prodData.description + '</p>'));
li.append($('<div></div>', {class: 'clear'}));
parent.append(li);
}
Expand All @@ -67,20 +70,19 @@ $(function() {
var prod = $(this).data('product');
console.log('purchasing', prod.name, id);

fxpay.purchase(id, {
oncheckpayment: function() {
// TODO: update the UI here with a spinner or something.
console.log('checking for payment');
},
onpurchase: function(err) {
if (err) {
return showError(err);
}
$('#your-products ul li.placeholder').remove();
addProduct($('#your-products ul'), id, prod, {showBuy: false});
},
apiUrlBase: apiUrlBase
fxpay.purchase(id, function(err, info) {
if (err) {
return showError(err);
}
console.log('product:', info.productId,
'purchased for the first time?', info.newPurchase);
$('#your-products ul li.placeholder').remove();
var prodData = products[info.productId];
addProduct($('#your-products ul'), info.productId, prodData,
{showBuy: false});
});

// TODO: update the UI here with a spinner or something.
});

$('#api-server').change(function(evt) {
Expand All @@ -91,10 +93,21 @@ $(function() {
// Startup
//
console.log('example app startup');

fxpay.init({
onerror: function(err) {
showError(err);
},
oninit: function() {
console.log('fxpay initialized successfully');
}
});

setApiServer();
var ul = $('#products ul');
for (var prodID in products) {
var prod = products[prodID];
addProduct(ul, prodID, prod);

for (var prodId in products) {
addProduct(ul, prodId, products[prodId]);
}

});
Loading