diff --git a/README.rst b/README.rst index 8488dbd..9d9934e 100644 --- a/README.rst +++ b/README.rst @@ -61,58 +61,66 @@ 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/ @@ -120,8 +128,9 @@ while they wait using ``oncheckpayment()`` like this:: 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. @@ -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 @@ -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}); - - +*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 @@ -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 diff --git a/example/js/index.js b/example/js/index.js index 2b7be0f..3148b25 100644 --- a/example/js/index.js +++ b/example/js/index.js @@ -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 = $('
  • ', {class: 'product'}); - li.append($('', {src: prod.icons['64'], height: 64, width: 64})); + li.append($('', {src: prodData.icons['64'], + height: 64, width: 64})); if (opt.showBuy) { - li.append($('').data({productId: prodID, product: prod})); + li.append($('').data({productId: prodID, + product: prodData})); } - li.append($('

    ' + prod.name + '

    ')); - li.append($('

    ' + prod.description + '

    ')); + li.append($('

    ' + prodData.name + '

    ')); + li.append($('

    ' + prodData.description + '

    ')); li.append($('
    ', {class: 'clear'})); parent.append(li); } @@ -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) { @@ -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]); } + }); diff --git a/lib/fxpay.js b/lib/fxpay.js index a9ba836..458e68b 100644 --- a/lib/fxpay.js +++ b/lib/fxpay.js @@ -1,148 +1,173 @@ (function(exports) { "use strict"; - // This is the App object returned from mozApps.getSelf(). - var appSelf; + var settings = { + + // Public settings. + // + apiUrlBase: 'https://marketplace.firefox.com', + // When defined, this will override the API object's default. + apiTimeoutMs: undefined, + apiVersionPrefix: '/api/v1', + log: window.console, + + // Private settings. + // + // This will be the App object returned from mozApps.getSelf(). + appSelf: null, + callbacks: { + onerror: function(err) { + throw err; + }, + oninit: function() { + settings.log('all products set up successfully'); + } + }, + // A copy of a setup error for later retrieval. + initError: 'NOT_INITIALIZED', + mozPay: navigator.mozPay, + mozApps: navigator.mozApps, + }; - // A copy of a startup error for later retrieval. - exports._startupError = 'NOT_STARTED'; - exports.startup = function _startup(opt) { - opt = opt || {}; - opt.log = opt.log || window.console; - if (typeof opt.mozApps === 'undefined') { - opt.mozApps = navigator.mozApps; + exports.configure = function _configure(newSettings) { + for (var k in newSettings) { + settings[k] = newSettings[k]; } - opt.onstart = (opt.onstart || window._fxpay_onstart || - function(err) { - if (err) { - opt.log.error('startup failed:', err); - } else { - opt.log.info('fxpay has started ok'); - } - }); + }; + + + exports.init = function _init(opt) { + opt = opt || {}; function storeError(err) { - exports._startupError = err; - return opt.onstart(exports._startupError); + settings.initError = err; + return settings.callbacks.onerror(settings.initError); + } + + if (opt.onerror) { + settings.callbacks.onerror = opt.onerror; + } + if (opt.oninit) { + settings.callbacks.oninit = opt.oninit; } - if (!opt.mozApps || !opt.mozApps.getSelf) { - opt.log.error('Missing pay platform: mozApps was falsey'); + var validOptions = ['onerror', 'oninit']; + for (var k in opt) { + if (validOptions.indexOf(k) === -1) { + settings.log.error('init() received an unknown option:', k); + return settings.callbacks.onerror('INCORRECT_USAGE'); + } + } + + if (!settings.mozApps || !settings.mozApps.getSelf) { + settings.log.error('Missing pay platform: mozApps was falsey'); return storeError('PAY_PLATFORM_UNAVAILABLE'); } - var appRequest = opt.mozApps.getSelf(); + var appRequest = settings.mozApps.getSelf(); appRequest.onsuccess = function() { - appSelf = this.result; - if (!appSelf) { - opt.log.error('falsey app object from getSelf()', appSelf); + settings.appSelf = this.result; + if (!settings.appSelf) { + settings.log.error('falsey app object from getSelf()', + settings.appSelf); return storeError('NOT_INSTALLED_AS_APP'); } - if (!appSelf.addReceipt) { + if (!settings.appSelf.addReceipt) { // addReceipt() is a newer API call but we need it for // in-app product ownership. - opt.log.error('method App.addReceipt does not exist'); + settings.log.error('method App.addReceipt does not exist'); return storeError('PAY_PLATFORM_UNAVAILABLE'); } var numReceipts = 0; - if (appSelf.receipts) { - for (var i = 0; i < appSelf.receipts.length; i++) { - opt.log.info('Installed receipt: ' + appSelf.receipts[i]); + if (settings.appSelf.receipts) { + for (var i = 0; i < settings.appSelf.receipts.length; i++) { + settings.log.info('Installed receipt: ' + + settings.appSelf.receipts[i]); numReceipts++; } } - opt.log.info('Number of receipts already installed: ' + numReceipts); + settings.log.info('Number of receipts already installed: ' + numReceipts); // Startup succeeded; clear the stored error. - exports._startupError = null; - opt.onstart(); + settings.initError = null; + settings.callbacks.oninit(); }; appRequest.onerror = function() { var err = this.error.name; - opt.log.error('mozApps.getSelf() returned an error', err); + settings.log.error('mozApps.getSelf() returned an error', err); storeError(err); }; }; - exports.startup(); - - exports.purchase = function _purchase(productId, opt) { + exports.purchase = function _purchase(productId, onPurchase, opt) { opt = opt || {}; - opt.log = opt.log || window.console; - opt.onpurchase = opt.onpurchase || function(err) { - if (err) { - throw err; - } - }; - opt.oncheckpayment = opt.oncheckpayment || function() {}; - opt.mozPay = opt.mozPay || navigator.mozPay; opt.maxTries = opt.maxTries || undefined; opt.pollIntervalMs = opt.pollIntervalMs || undefined; - opt.apiTimeoutMs = opt.apiTimeoutMs || undefined; - opt.apiUrlBase = (opt.apiUrlBase || - 'https://marketplace.firefox.com'); - opt.apiVersionPrefix = (opt.apiVersionPrefix || '/api/v1'); - - var _appSelf = opt.appSelf || appSelf; + if (!onPurchase) { + onPurchase = function _onPurchase(err, info) { + if (err) { + throw err; + } + console.log('product', info.productId, 'purchased'); + }; + } - if (exports._startupError) { - opt.log.error('startup failed:', exports._startupError); - return opt.onpurchase(exports._startupError); + if (settings.initError) { + settings.log.error('init failed:', settings.initError); + return onPurchase(settings.initError); } - if (!opt.mozPay) { - opt.log.error('Missing pay platform: mozPay was falsey'); - return opt.onpurchase('PAY_PLATFORM_UNAVAILABLE'); + if (!settings.mozPay) { + settings.log.error('Missing pay platform: mozPay was falsey'); + return onPurchase('PAY_PLATFORM_UNAVAILABLE'); } - startPurchase(productId, _appSelf, opt); + startPurchase(productId, onPurchase, settings.appSelf, opt); }; - function startPurchase(productId, appSelf, opt) { + function startPurchase(productId, onPurchase, appSelf, opt) { opt = opt || {}; - opt.log = opt.log || window.console; + opt.maxTries = opt.maxTries || undefined; + opt.pollIntervalMs = opt.pollIntervalMs || undefined; - var log = opt.log; - var api = new API(opt.apiUrlBase, - {log: log, - timeoutMs: opt.apiTimeoutMs, - versionPrefix: opt.apiVersionPrefix}); + var info = {productId: productId, + newPurchase: true}; + var log = settings.log; + var api = new API(settings.apiUrlBase); log.debug('starting purchase for product', productId); var path = "/webpay/inapp/prepare/"; api.post(path, {inapp: productId}, function(err, productData) { if (err) { - return opt.onpurchase(err); + return onPurchase(err); } log.debug('xhr load: JSON', productData); - var payReq = opt.mozPay([productData.webpayJWT]); + var payReq = settings.mozPay([productData.webpayJWT]); payReq.onerror = function() { log.error('mozPay: received onerror():', this.error.name); - opt.onpurchase(this.error.name); + onPurchase(this.error.name); }; payReq.onsuccess = function() { log.debug('mozPay: received onsuccess()'); // The payment flow has closed. Let's wait for // payment verification. - opt.oncheckpayment(); getTransactionResult( api, api.url( productData.contribStatusURL, {versioned: false} ), function(err, data) { - onTransaction(err, data, appSelf, opt); + onTransaction(err, onPurchase, data, appSelf, info); }, { - log: log, maxTries: opt.maxTries, pollIntervalMs: opt.pollIntervalMs } @@ -152,24 +177,24 @@ } - function onTransaction(err, data, appSelf, opt) { + function onTransaction(err, onPurchase, data, appSelf, info) { if (err) { - return opt.onpurchase(err); + return onPurchase(err); } - opt.log.info('received completed transaction:', data); + settings.log.info('received completed transaction:', data); - opt.log.info('adding receipt to device'); + settings.log.info('adding receipt to device'); var receiptReq = appSelf.addReceipt(data.receipt); receiptReq.onsuccess = function() { - opt.log.info('item fully purchased and receipt installed'); - opt.onpurchase(null); + settings.log.info('item fully purchased and receipt installed'); + onPurchase(null, info); }; receiptReq.onerror = function() { var err = this.error.name; - opt.log.error('error calling app.addReceipt', err); - opt.onpurchase(err); + settings.log.error('error calling app.addReceipt', err); + onPurchase(err); }; } @@ -177,12 +202,11 @@ // NOTE: if you change this function signature, change the setTimeout below. function getTransactionResult(api, transStatusPath, cb, opt) { opt = opt || {}; - opt.log = opt.log || window.console; opt.maxTries = opt.maxTries || 10; opt.pollIntervalMs = opt.pollIntervalMs || 1000; opt._tries = opt._tries || 1; - var log = opt.log; + var log = settings.log; log.debug('Getting transaction state at', transStatusPath, 'tries=', opt._tries); @@ -204,7 +228,6 @@ opt.pollIntervalMs, 'ms'); window.setTimeout(function() { getTransactionResult(api, transStatusPath, cb, { - log: log, maxTries: opt.maxTries, pollIntervalMs: opt.pollIntervalMs, _tries: opt._tries + 1 @@ -222,9 +245,9 @@ function API(baseUrl, opt) { opt = opt || {}; this.baseUrl = baseUrl; - this.log = opt.log || window.console; - this.timeoutMs = opt.timeoutMs || 5000; - this.versionPrefix = opt.versionPrefix || undefined; + this.log = settings.log; + this.timeoutMs = settings.apiTimeoutMs || 10000; + this.versionPrefix = settings.apiVersionPrefix || undefined; } exports.API = API; diff --git a/tests/test-fxpay.js b/tests/test-fxpay.js index fd9b04a..12373a4 100644 --- a/tests/test-fxpay.js +++ b/tests/test-fxpay.js @@ -4,33 +4,50 @@ describe('fxpay', function () { beforeEach(function() { console.log('beginEach'); server = sinon.fakeServer.create(); - if (window._fxpay_onstart) { - delete window._fxpay_onstart; - } - fxpay._startupError = null; + fxpay.configure({ + apiUrlBase: 'http://tests-should-never-hit-this.com', + callbacks: {}, + initError: null, + mozApps: mozAppsStub + }); }); afterEach(function() { server.restore(); }); - describe('startup()', function() { + describe('init()', function() { beforeEach(function() { appSelf.init(); }); it('should call back when started', function (done) { - fxpay.startup({ - onstart: function(err) { + fxpay.init({ + onerror: function(err) { done(err); }, - mozApps: mozAppsStub, + oninit: function() { + done(); + } }); appSelf.onsuccess(); }); + it('should error with unknown options', function (done) { + fxpay.init({ + onerror: function(err) { + assert.equal(err, 'INCORRECT_USAGE'); + done(); + }, + oninit: function() { + done('init should not have been called'); + }, + notAvalidOption: false + }); + }); + it('should error when addReceipt does not exist', function (done) { var appStub = { addReceipt: undefined, // older FxOSs do not have this. @@ -39,28 +56,30 @@ describe('fxpay', function () { }; appStub.result = appStub; // result of DOM request. - fxpay.startup({ - onstart: function(err) { - assert.equal(err, 'PAY_PLATFORM_UNAVAILABLE'); - done(); - }, + fxpay.configure({ mozApps: { getSelf: function() { return appStub; } - }, + } + }); + + fxpay.init({ + onerror: function(err) { + assert.equal(err, 'PAY_PLATFORM_UNAVAILABLE'); + done(); + } }); appStub.onsuccess(); }); it('should error when not running as app', function (done) { - fxpay.startup({ - onstart: function(err) { + fxpay.init({ + onerror: function(err) { assert.equal(err, 'NOT_INSTALLED_AS_APP'); done(); - }, - mozApps: mozAppsStub, + } }); // This happens when you access the app from a browser @@ -70,13 +89,12 @@ describe('fxpay', function () { }); it('should pass through apps platform errors', function (done) { - fxpay.startup({ - onstart: function(err) { + fxpay.init({ + onerror: function(err) { console.log('GOT error', err); assert.equal(err, 'INVALID_MANIFEST'); done(); - }, - mozApps: mozAppsStub, + } }); // Simulate an apps platform error. @@ -85,35 +103,28 @@ describe('fxpay', function () { }); it('should error when apps are not supported', function (done) { - fxpay.startup({ - onstart: function(err) { + fxpay.configure({ + mozApps: {} // invalid mozApps. + }); + fxpay.init({ + onerror: function(err) { console.log('GOT error', err); assert.equal(err, 'PAY_PLATFORM_UNAVAILABLE'); done(); - }, - mozApps: {}, // invalid mozApps. + } }); }); it('should error when no apps API at all', function (done) { - fxpay.startup({ - onstart: function(err) { + fxpay.configure({ + mozApps: null // no API, like Chrome or whatever. + }); + fxpay.init({ + onerror: function(err) { console.log('GOT error', err); assert.equal(err, 'PAY_PLATFORM_UNAVAILABLE'); done(); - }, - mozApps: null, // no API, like Chrome or whatever. - }); - }); - - it('should support a global startup error handler', function (done) { - window._fxpay_onstart = function(err) { - console.log('GOT error', err); - assert.equal(err, 'PAY_PLATFORM_UNAVAILABLE'); - done(); - }; - fxpay.startup({ - mozApps: {}, // invalid mozApps. + } }); }); @@ -125,6 +136,10 @@ describe('fxpay', function () { beforeEach(function() { mozPay = sinon.spy(mozPayStub); appSelf.init(); + fxpay.configure({ + appSelf: appSelf, + mozPay: mozPay + }); }); afterEach(function() { @@ -132,45 +147,45 @@ describe('fxpay', function () { receiptAdd.reset(); }); - it('should pass through startup errors', function (done) { - // Trigger a startup error: - fxpay.startup({ + it('should pass through setup errors', function (done) { + // Trigger a setup error: + fxpay.configure({ mozApps: {}, // invalid mozApps. }); + fxpay.init({ + onerror: function(err) { + console.log('ignoring err', err); + } + }); + // Try to start a purchase. - fxpay.purchase('123', { - onpurchase: function(err) { - assert.equal(err, 'PAY_PLATFORM_UNAVAILABLE'); - done(); - }, - mozPay: mozPay, - appSelf: appSelf, + fxpay.purchase('123', function(err) { + assert.equal(err, 'PAY_PLATFORM_UNAVAILABLE'); + done(); }); }); it('should send a JWT to mozPay', function (done) { var webpayJWT = ''; - var apiUrl = 'https://not-the-real-marketplace'; - var versionPrefix = '/api/v1'; - - fxpay.purchase('123', { - onpurchase: function(err) { - if (!err) { - assert.ok(mozPay.called); - assert.ok(mozPay.calledWith([webpayJWT]), mozPay.firstCall); - } - done(err); - }, - mozPay: mozPay, - appSelf: appSelf, - apiUrlBase: apiUrl, - apiVersionPrefix: versionPrefix + var productId = '1234'; + var cfg = { + apiUrlBase: 'https://not-the-real-marketplace', + apiVersionPrefix: '/api/v1' + }; + fxpay.configure(cfg); + + fxpay.purchase(productId, function(err, info) { + assert.ok(mozPay.called); + assert.ok(mozPay.calledWith([webpayJWT]), mozPay.firstCall); + assert.equal(info.productId, productId); + assert.equal(info.newPurchase, true); + done(err); }); // Respond to fetching the JWT. server.respondWith( 'POST', - apiUrl + versionPrefix + '/webpay/inapp/prepare/', + cfg.apiUrlBase + cfg.apiVersionPrefix + '/webpay/inapp/prepare/', // TODO: assert somehow that productId is part of post data. productData({webpayJWT: webpayJWT})); server.respond(); @@ -179,7 +194,7 @@ describe('fxpay', function () { server.respondWith( 'GET', - apiUrl + '/transaction/XYZ', + cfg.apiUrlBase + '/transaction/XYZ', transactionData()); server.respond(); @@ -188,14 +203,10 @@ describe('fxpay', function () { it('should timeout polling the transaction', function (done) { - fxpay.purchase('123', { - onpurchase: function(err) { - console.log('GOT error', err); - assert.equal(err, 'TRANSACTION_TIMEOUT'); - done(); - }, - mozPay: mozPay, - appSelf: appSelf, + fxpay.purchase('123', function(err) { + assert.equal(err, 'TRANSACTION_TIMEOUT'); + done(); + }, { maxTries: 2, pollIntervalMs: 1 }); @@ -217,48 +228,11 @@ describe('fxpay', function () { server.respond(); }); - it('should call back when mozPay window closes', function (done) { - - fxpay.purchase(123, { - oncheckpayment: function() { - done(); - }, - onpurchase: function(err) { - // Make sure we don't have an unexpected error. - assert.equal(err, null) - }, - mozPay: mozPay, - appSelf: appSelf, - }); - - // Respond to fetching the JWT. - server.respondWith( - 'POST', - /.*webpay\/inapp\/prepare/, - productData()); - server.respond(); - - mozPay.returnValues[0].onsuccess(); - - // Respond to polling the transaction. - server.respondWith( - 'GET', - /.*\/transaction\/XYZ/, - transactionData()); - server.respond(); - - receiptAdd.onsuccess(); - }); - it('should call back with mozPay error', function (done) { - fxpay.purchase(123, { - onpurchase: function(err) { - assert.equal(err, 'DIALOG_CLOSED_BY_USER'); - done(); - }, - mozPay: mozPay, - appSelf: appSelf, + fxpay.purchase('123', function(err) { + assert.equal(err, 'DIALOG_CLOSED_BY_USER'); + done(); }); // Respond to fetching the JWT. @@ -274,15 +248,10 @@ describe('fxpay', function () { }); it('should report invalid transaction state', function (done) { - var productId = 123; - fxpay.purchase(productId, { - onpurchase: function(err) { - assert.equal(err, 'INVALID_TRANSACTION_STATE'); - done(); - }, - mozPay: mozPay, - appSelf: appSelf + fxpay.purchase('123', function(err) { + assert.equal(err, 'INVALID_TRANSACTION_STATE'); + done(); }); // Respond to fetching the JWT. @@ -305,29 +274,20 @@ describe('fxpay', function () { }); it('should error when mozPay is not supported', function (done) { - fxpay.purchase('123', { - onpurchase: function(err) { - console.log('GOT error', err); - assert.equal(err, 'PAY_PLATFORM_UNAVAILABLE'); - done(); - }, - mozPay: undefined, - appSelf: appSelf, + fxpay.configure({mozPay: undefined}); + + fxpay.purchase('123', function(err) { + assert.equal(err, 'PAY_PLATFORM_UNAVAILABLE'); + done(); }); }); it('should add a Marketplace receipt to device', function (done) { var receipt = ''; - fxpay.purchase('123', { - onpurchase: function(err) { - if (!err) { - assert.equal(receiptAdd._receipt, receipt); - } - done(err); - }, - mozPay: mozPay, - appSelf: appSelf, + fxpay.purchase('123', function(err) { + assert.equal(receiptAdd._receipt, receipt); + done(err); }); // Respond to fetching the JWT. @@ -349,13 +309,10 @@ describe('fxpay', function () { }); it('should pass through receipt errors', function (done) { - fxpay.purchase('123', { - onpurchase: function(err) { - assert.equal(err, 'ADD_RECEIPT_ERROR'); - done(); - }, - mozPay: mozPay, - appSelf: appSelf, + + fxpay.purchase('123', function(err) { + assert.equal(err, 'ADD_RECEIPT_ERROR'); + done(); }); // Respond to fetching the JWT. @@ -386,7 +343,8 @@ describe('fxpay', function () { var versionPrefix = '/api/v1'; beforeEach(function() { - api = new fxpay.API(baseUrl, {versionPrefix: versionPrefix}); + fxpay.configure({apiVersionPrefix: versionPrefix}); + api = new fxpay.API(baseUrl); }); it('should handle POSTs', function (done) {