From 54a94db2de729049e3d70f71a509192640d4d357 Mon Sep 17 00:00:00 2001 From: Victor Piousbox Date: Thu, 21 Apr 2016 03:52:29 +0000 Subject: [PATCH 1/2] working on amazon payments --- common/locales/en/api-v3.json | 3 +- ...ayments_amazon_verify_access_token.test.js | 2 +- test/api/v3/unit/libs/amazonPayments.test.js | 62 ++++++++- .../controllers/top-level/payments/amazon.js | 123 +++++++++--------- website/src/libs/api-v3/amazonPayments.js | 56 +++++++- 5 files changed, 175 insertions(+), 71 deletions(-) diff --git a/common/locales/en/api-v3.json b/common/locales/en/api-v3.json index 8c0ec78de0..5b1d57383e 100644 --- a/common/locales/en/api-v3.json +++ b/common/locales/en/api-v3.json @@ -173,5 +173,6 @@ "equipmentAlreadyOwned": "You already own that piece of equipment", "missingAccessToken": "The request is missing a required parameter : access_token", "missingBillingAgreementId": "Missing billing agreement id", - "missingAttributesFromAmazon": "Missing attributes from Amazon" + "missingAttributesFromAmazon": "Missing attributes from Amazon", + "paymentNotSuccessful": "The payment was not successful" } diff --git a/test/api/v3/integration/payments/POST-payments_amazon_verify_access_token.test.js b/test/api/v3/integration/payments/POST-payments_amazon_verify_access_token.test.js index 494c387f14..ecc021e25d 100644 --- a/test/api/v3/integration/payments/POST-payments_amazon_verify_access_token.test.js +++ b/test/api/v3/integration/payments/POST-payments_amazon_verify_access_token.test.js @@ -11,7 +11,7 @@ describe('payments : amazon', () => { user = await generateUser(); }); - it('verify access token', async () => { + it('verifies access token', async () => { await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({ code: 400, error: 'BadRequest', diff --git a/test/api/v3/unit/libs/amazonPayments.test.js b/test/api/v3/unit/libs/amazonPayments.test.js index aa5a9588c6..9529c3e985 100644 --- a/test/api/v3/unit/libs/amazonPayments.test.js +++ b/test/api/v3/unit/libs/amazonPayments.test.js @@ -1,6 +1,7 @@ -import * as amz from '../../../../../website/src/libs/api-v3/amazonPayments'; +import * as amzLib from '../../../../../website/src/libs/api-v3/amazonPayments'; // import * as amzStub from 'amazon-payments'; import amazonPayments from 'amazon-payments'; +var User = require('mongoose').model('User'); describe('amazonPayments', () => { beforeEach(() => { @@ -25,7 +26,7 @@ describe('amazonPayments', () => { }); it('returns tokenInfo', async (done) => { - let result = await amz.getTokenInfo(); + let result = await amzLib.getTokenInfo(); expect(result).to.eql(thisToken); done(); }); @@ -34,7 +35,7 @@ describe('amazonPayments', () => { describe('#getTokenInfo', () => { it('validates access_token parameter', async (done) => { try { - await amz.getTokenInfo(); + await amzLib.getTokenInfo(); } catch (e) { expect(e.type).to.eql('invalid_request'); done(); @@ -43,7 +44,60 @@ describe('amazonPayments', () => { }); describe('#createOrderReferenceId', () => { - it('succeeds', () => { + it('verifies billingAgreementId', async (done) => { + try { + let inputSet = {}; + delete inputSet.Id; + await amzLib.createOrderReferenceId(inputSet); + } catch (e) { + + /* console.log('error!', e); + console.log('error keys!', Object.keys(e)); + for (var key in e) { + console.log(e[key]); + } // */ + + expect(e.type).to.eql('InvalidParameterValue'); + expect(e.body.ErrorResponse.Error.Message).to.eql('Parameter AWSAccessKeyId cannot be empty.'); + done(); + } + }); + + xit('succeeds', () => { }); }); + + describe('#checkout', () => { + xit('succeeds'); + }); + + describe('#setOrderReferenceDetails', () => { + xit('succeeds'); + }); + + describe('#confirmOrderReference', () => { + xit('succeeds'); + }); + + describe('#authorize', () => { + xit('succeeds'); + + xit('was declined'); + + xit('had an error'); + }); + + describe('#closeOrderReference', () => { + xit('succeeds'); + }); + + describe.only('#executePayment', () => { + it('succeeds', () => { + }); + + it('succeeds as a gift', () => { + }); + }); + + }); diff --git a/website/src/controllers/top-level/payments/amazon.js b/website/src/controllers/top-level/payments/amazon.js index a2316f21e6..16c835c2c3 100644 --- a/website/src/controllers/top-level/payments/amazon.js +++ b/website/src/controllers/top-level/payments/amazon.js @@ -10,7 +10,8 @@ import { // NotAuthorized, BadRequest, } from '../../../libs/api-v3/errors'; -import amz from '../../../libs/api-v3/amazonPayments'; +import amzLib from '../../../libs/api-v3/amazonPayments'; +import { authWithHeaders } from '../../../middlewares/api-v3/auth'; let api = {}; @@ -26,7 +27,7 @@ api.verifyAccessToken = { method: 'POST', url: '/payments/amazon/verifyAccessToken', async handler (req, res) { - await amz.getTokenInfo(req.body.access_token) + await amzLib.getTokenInfo(req.body.access_token) .then(() => { res.respond(200, {}); }).catch((error) => { @@ -46,47 +47,59 @@ api.verifyAccessToken = { api.createOrderReferenceId = { method: 'POST', url: '/payments/amazon/createOrderReferenceId', + // middlewares: [authWithHeaders()], async handler (req, res) { - if (!req.body.billingAgreementId) { - throw new BadRequest(res.t('missingBillingAgreementId')); - } - let response = await amz.createOrderReferenceId({ - Id: req.body.billingAgreementId, - IdType: 'BillingAgreement', - ConfirmNow: false, - }).then(() => { + try { + let response = await amzLib.createOrderReferenceId({ + Id: req.body.billingAgreementId, + IdType: 'BillingAgreement', + ConfirmNow: false, + AWSAccessKeyId: 'something', + }); res.respond(200, { orderReferenceId: response.OrderReferenceDetails.AmazonOrderReferenceId, }); - }).catch(errStr => { - throw new BadRequest(res.t(errStr)); - }); + } catch (error) { + throw new BadRequest(error); + } + }, }; -/* -api.checkout = function checkout (req, res, next) { - if (!req.body || !req.body.orderReferenceId) { - return res.status(400).json({err: 'Billing Agreement Id not supplied.'}); - } +/** + * @api {post} /api/v3/payments/amazon/checkout do checkout + * @apiVersion 3.0.0 + * @apiName AmazonCheckout + * @apiGroup Payments + * + * @apiParam {string} billingAgreementId billing agreement id + * @apiSuccess {object} object containing { orderReferenceId } + **/ +api.checkout = { + method: 'POST', + url: '/payments/amazon/checkout', + middlewares: [authWithHeaders()], + async handler (req, res) { + let gift = req.body.gift; + let user = res.locals.user; + let orderReferenceId = req.body.orderReferenceId; + let amount = 5; - let gift = req.body.gift; - let user = res.locals.user; - let orderReferenceId = req.body.orderReferenceId; - let amount = 5; - - if (gift) { - if (gift.type === 'gems') { - amount = gift.gems.amount / 4; - } else if (gift.type === 'subscription') { - amount = shared.content.subscriptionBlocks[gift.subscription.key].price; + if (gift) { + if (gift.type === 'gems') { + amount = gift.gems.amount / 4; + } else if (gift.type === 'subscription') { + amount = shared.content.subscriptionBlocks[gift.subscription.key].price; + } } - } - async.series({ - setOrderReferenceDetails (cb) { - amzPayment.offAmazonPayments.setOrderReferenceDetails({ + /* if (!req.body || !req.body.orderReferenceId) { + return res.status(400).json({err: 'Billing Agreement Id not supplied.'}); + } */ + + try { + await amzLib.setOrderReferenceDetails({ AmazonOrderReferenceId: orderReferenceId, OrderReferenceAttributes: { OrderTotal: { @@ -99,17 +112,11 @@ api.checkout = function checkout (req, res, next) { StoreName: 'HabitRPG', }, }, - }, cb); - }, + }); - confirmOrderReference (cb) { - amzPayment.offAmazonPayments.confirmOrderReference({ - AmazonOrderReferenceId: orderReferenceId, - }, cb); - }, + await amzLib.confirmOrderReference({ AmazonOrderReferenceId: orderReferenceId }); - authorize (cb) { - amzPayment.offAmazonPayments.authorize({ + await amzLib.authorize({ AmazonOrderReferenceId: orderReferenceId, AuthorizationReferenceId: shared.uuid().substring(0, 32), AuthorizationAmount: { @@ -119,23 +126,16 @@ api.checkout = function checkout (req, res, next) { SellerAuthorizationNote: 'HabitRPG Payment', TransactionTimeout: 0, CaptureNow: true, - }, function checkAuthorizationStatus (err) { - if (err) return cb(err); - - if (res.AuthorizationDetails.AuthorizationStatus.State === 'Declined') { - return cb(new Error('The payment was not successfull.')); - } - - return cb(); }); - }, - closeOrderReference (cb) { - amzPayment.offAmazonPayments.closeOrderReference({ - AmazonOrderReferenceId: orderReferenceId, - }, cb); - }, + await amzLib.closeOrderReference({ AmazonOrderReferenceId: orderReferenceId }); + res.respond(200); + } catch(error) { + throw new BadRequest(error); + } + + /* executePayment (cb) { async.waterfall([ function findUser (cb2) { @@ -153,16 +153,13 @@ api.checkout = function checkout (req, res, next) { } payments[method](data, cb2); - }, - ], cb); - }, - }, function result (err) { - if (err) return next(err); - - res.sendStatus(200); - }); + }, */ + }, }; + + +/* api.subscribe = function subscribe (req, res, next) { if (!req.body || !req.body.billingAgreementId) { return res.status(400).json({err: 'Billing Agreement Id not supplied.'}); diff --git a/website/src/libs/api-v3/amazonPayments.js b/website/src/libs/api-v3/amazonPayments.js index c7bf5202b3..3d92132f2f 100644 --- a/website/src/libs/api-v3/amazonPayments.js +++ b/website/src/libs/api-v3/amazonPayments.js @@ -1,6 +1,7 @@ import amazonPayments from 'amazon-payments'; import nconf from 'nconf'; - +import common from '../../../../common'; +let t = common.i18n.t; const IS_PROD = nconf.get('NODE_ENV') === 'production'; let api = {}; @@ -31,11 +32,62 @@ api.createOrderReferenceId = (inputSet) => { amzPayment.offAmazonPayments.createOrderReferenceForId(inputSet, (err, response) => { if (err) return reject(err); if (!response.OrderReferenceDetails || !response.OrderReferenceDetails.AmazonOrderReferenceId) { - return reject('missingAttributesFromAmazon'); + return reject(t('missingAttributesFromAmazon')); } return resolve(response); }); }); }; +api.setOrderReferenceDetails = (inputSet) => { + let amzPayment = connect(amazonPayments); + return new Promise((resolve, reject) => { + amzPayment.offAmazonPayments.setOrderReferenceDetails(inputSet, (err, response) => { + if (err) return reject(err); + return resolve(response); + }); + }); +}; + +api.confirmOrderReference = (inputSet) => { + let amzPayment = connect(amazonPayments); + return new Promise((resolve, reject) => { + amzPayment.offAmazonPayments.confirmOrderReference(inputSet, (err, response) => { + if (err) return reject(err); + return resolve(response); + }); + }); +}; + +api.authorize = (inputSet) => { + let amzPayment = connect(amazonPayments); + return new Promize((resolve, reject) => { + amzPayment.offAmazonPayments.authorize(inputSet, (err, response) => { + if (err) return reject(err); + if (response.AuthorizationDetails.AuthorizationStatus.State === 'Declined') return reject(t('paymentNotSuccessful')); + return resolve(response); + }); + }); +}; + +api.closeOrderReference = (inputSet) => { + let amzPayment = connect(amazonPayments); + return new Promize((resolve, reject) => { + amzPayment.offAmazonPayments.closeOrderReference(inputSet, (err, response) => { + if (err) return reject(err); + return resolve(response); + }); + }); +}; + +api.executePayment = (inputSet) => { + let amzPayment = connect(amazonPayments); + return new Promize((resolve, reject) => { + amzPayment.offAmazonPayments.closeOrderReference(inputSet, (err, response) => { + if (err) return reject(err); + return resolve(response); + }); + }); +}; + module.exports = api; From cbf1a4c8d32e82765ff07cb4e29fc596de4d91e7 Mon Sep 17 00:00:00 2001 From: Victor Piousbox Date: Thu, 21 Apr 2016 04:37:25 +0000 Subject: [PATCH 2/2] breaking changes promisifying amazon payments --- test/api/v3/unit/libs/amazonPayments.test.js | 2 +- test/api/v3/unit/libs/paymentsIndex.test.js | 11 ++++++ .../controllers/top-level/payments/amazon.js | 34 +++++++------------ .../controllers/top-level/payments/index.js | 9 +++-- 4 files changed, 32 insertions(+), 24 deletions(-) create mode 100644 test/api/v3/unit/libs/paymentsIndex.test.js diff --git a/test/api/v3/unit/libs/amazonPayments.test.js b/test/api/v3/unit/libs/amazonPayments.test.js index 9529c3e985..b2bf480e01 100644 --- a/test/api/v3/unit/libs/amazonPayments.test.js +++ b/test/api/v3/unit/libs/amazonPayments.test.js @@ -92,7 +92,7 @@ describe('amazonPayments', () => { }); describe.only('#executePayment', () => { - it('succeeds', () => { + it('succeeds not as a gift', () => { }); it('succeeds as a gift', () => { diff --git a/test/api/v3/unit/libs/paymentsIndex.test.js b/test/api/v3/unit/libs/paymentsIndex.test.js new file mode 100644 index 0000000000..74b9d29273 --- /dev/null +++ b/test/api/v3/unit/libs/paymentsIndex.test.js @@ -0,0 +1,11 @@ + +describe('payments/index', () => { + beforeEach(() => { + }); + + describe('#createSubscription', async () => { + }); + + describe('#buyGems', async () => { + }); +}); diff --git a/website/src/controllers/top-level/payments/amazon.js b/website/src/controllers/top-level/payments/amazon.js index 16c835c2c3..785efb06b9 100644 --- a/website/src/controllers/top-level/payments/amazon.js +++ b/website/src/controllers/top-level/payments/amazon.js @@ -12,6 +12,7 @@ import { } from '../../../libs/api-v3/errors'; import amzLib from '../../../libs/api-v3/amazonPayments'; import { authWithHeaders } from '../../../middlewares/api-v3/auth'; +var payments = require('./index'); let api = {}; @@ -130,31 +131,22 @@ api.checkout = { await amzLib.closeOrderReference({ AmazonOrderReferenceId: orderReferenceId }); + // execute payment + let giftUser = await User.findById(gift ? gift.uuid : undefined); + let data = { giftUser, paymentMethod: 'Amazon Payments' }; + let method = 'buyGems'; + if (gift) { + if (gift.type === 'subscription') method = 'createSubscription'; + gift.member = giftUser; + data.gift = gift; + data.paymentMethod = 'Gift'; + } + await payments[method](data); + res.respond(200); } catch(error) { throw new BadRequest(error); } - - /* - executePayment (cb) { - async.waterfall([ - function findUser (cb2) { - User.findById(gift ? gift.uuid : undefined, cb2); - }, - function executeAmazonPayment (member, cb2) { - let data = {user, paymentMethod: 'Amazon Payments'}; - let method = 'buyGems'; - - if (gift) { - if (gift.type === 'subscription') method = 'createSubscription'; - gift.member = member; - data.gift = gift; - data.paymentMethod = 'Gift'; - } - - payments[method](data, cb2); - }, */ - }, }; diff --git a/website/src/controllers/top-level/payments/index.js b/website/src/controllers/top-level/payments/index.js index 6c9ddea60d..6ee56bf0d2 100644 --- a/website/src/controllers/top-level/payments/index.js +++ b/website/src/controllers/top-level/payments/index.js @@ -36,6 +36,9 @@ function revealMysteryItems (user) { }); } +// @TODO: HEREHERE +api.createSubscription = async function createSubscription (data) { +} api.createSubscription = function createSubscription (data, cb) { let recipient = data.gift ? data.gift.member : data.user; let plan = recipient.purchased.plan; @@ -150,6 +153,9 @@ api.cancelSubscription = function cancelSubscription (data, cb) { analytics.track('unsubscribe', analyticsData); }; +// @TODO: HEREHERE +api.buyGems = async function buyGems (data) { +}; api.buyGems = function buyGems (data, cb) { let amt = data.amount || 5; amt = data.gift ? data.gift.gems.amount / 4 : amt; @@ -229,5 +235,4 @@ api.amazonSubscribeCancel = amazon.subscribeCancel; api.iapAndroidVerify = iap.androidVerify; api.iapIosVerify = iap.iosVerify; -// module.exports = api; -module.exports = {}; // @TODO HEREHERE +module.exports = api;