v3 payments: port amazon payments

This commit is contained in:
Matteo Pagliazzi
2016-05-10 16:56:01 +02:00
parent 03008c0d40
commit 1a43ab35c0
4 changed files with 137 additions and 146 deletions

View File

@@ -127,12 +127,12 @@ function($rootScope, User, $http, Content) {
var url = '/amazon/createOrderReferenceId' var url = '/amazon/createOrderReferenceId'
$http.post(url, { $http.post(url, {
billingAgreementId: Payments.amazonPayments.billingAgreementId billingAgreementId: Payments.amazonPayments.billingAgreementId
}).success(function(data){ }).success(function(res){
Payments.amazonPayments.loggedIn = true; Payments.amazonPayments.loggedIn = true;
Payments.amazonPayments.orderReferenceId = data.orderReferenceId; Payments.amazonPayments.orderReferenceId = res.data.orderReferenceId;
Payments.amazonPayments.initWidgets(); Payments.amazonPayments.initWidgets();
}).error(function(res){ }).error(function(res){
alert(res.err); alert(res.message);
}); });
} }
}, },
@@ -146,7 +146,7 @@ function($rootScope, User, $http, Content) {
var url = '/amazon/verifyAccessToken' var url = '/amazon/verifyAccessToken'
$http.post(url, response).error(function(res){ $http.post(url, response).error(function(res){
alert(res.err); alert(res.message);
}); });
}); });
}, },
@@ -232,7 +232,7 @@ function($rootScope, User, $http, Content) {
Payments.amazonPayments.reset(); Payments.amazonPayments.reset();
window.location.reload(true); window.location.reload(true);
}).error(function(res){ }).error(function(res){
alert(res.err); alert(res.message);
Payments.amazonPayments.reset(); Payments.amazonPayments.reset();
}); });
}else if(Payments.amazonPayments.type === 'subscription'){ }else if(Payments.amazonPayments.type === 'subscription'){
@@ -246,7 +246,7 @@ function($rootScope, User, $http, Content) {
Payments.amazonPayments.reset(); Payments.amazonPayments.reset();
window.location.reload(true); window.location.reload(true);
}).error(function(res){ }).error(function(res){
alert(res.err); alert(res.message);
Payments.amazonPayments.reset(); Payments.amazonPayments.reset();
}); });
} }

View File

@@ -18,13 +18,11 @@ let api = {};
/** /**
* @apiIgnore Payments are considered part of the private API * @apiIgnore Payments are considered part of the private API
* @api {post} /amazon/verifyAccessToken verify access token * @api {post} /amazon/verifyAccessToken Amazon Payments: verify access token
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName AmazonVerifyAccessToken * @apiName AmazonVerifyAccessToken
* @apiGroup Payments * @apiGroup Payments
* *
* @apiParam {string} access_token the access token
*
* @apiSuccess {Object} data Empty object * @apiSuccess {Object} data Empty object
**/ **/
api.verifyAccessToken = { api.verifyAccessToken = {
@@ -32,56 +30,53 @@ api.verifyAccessToken = {
url: '/amazon/verifyAccessToken', url: '/amazon/verifyAccessToken',
middlewares: [authWithHeaders()], middlewares: [authWithHeaders()],
async handler (req, res) { async handler (req, res) {
try { let accessToken = req.body.access_token;
await amzLib.getTokenInfo(req.body.access_token);
res.respond(200, {}); if (!accessToken) throw new BadRequest('Missing req.body.access_token');
} catch (error) {
throw new BadRequest(error.body.error_description); await amzLib.getTokenInfo(accessToken);
} res.respond(200, {});
}, },
}; };
/** /**
* @apiIgnore Payments are considered part of the private API * @apiIgnore Payments are considered part of the private API
* @api {post} /amazon/createOrderReferenceId create order reference id * @api {post} /amazon/createOrderReferenceId Amazon Payments: create order reference id
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName AmazonCreateOrderReferenceId * @apiName AmazonCreateOrderReferenceId
* @apiGroup Payments * @apiGroup Payments
* *
* @apiParam {string} billingAgreementId billing agreement id * @apiSuccess {string} data.orderReferenceId The order reference id.
*
* @apiSuccess {object} data.orderReferenceId The order reference id.
**/ **/
api.createOrderReferenceId = { api.createOrderReferenceId = {
method: 'POST', method: 'POST',
url: '/amazon/createOrderReferenceId', url: '/amazon/createOrderReferenceId',
middlewares: [authWithHeaders()], middlewares: [authWithHeaders()],
async handler (req, res) { async handler (req, res) {
try { let billingAgreementId = req.body.billingAgreementId;
let response = await amzLib.createOrderReferenceId({
Id: req.body.billingAgreementId, if (!billingAgreementId) throw new BadRequest('Missing req.body.billingAgreementId');
IdType: 'BillingAgreement',
ConfirmNow: false, let response = await amzLib.createOrderReferenceId({
}); Id: billingAgreementId,
res.respond(200, { IdType: 'BillingAgreement',
orderReferenceId: response.OrderReferenceDetails.AmazonOrderReferenceId, ConfirmNow: false,
}); });
} catch (error) {
throw new BadRequest(error); res.respond(200, {
} orderReferenceId: response.OrderReferenceDetails.AmazonOrderReferenceId,
});
}, },
}; };
/** /**
* @apiIgnore Payments are considered part of the private API * @apiIgnore Payments are considered part of the private API
* @api {post} /amazon/checkout do checkout * @api {post} /amazon/checkout Amazon Payments: checkout
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName AmazonCheckout * @apiName AmazonCheckout
* @apiGroup Payments * @apiGroup Payments
* *
* @apiParam {string} billingAgreementId billing agreement id * @apiSuccess {object} data Empty object
*
* @apiSuccess {object} object containing { orderReferenceId }
**/ **/
api.checkout = { api.checkout = {
method: 'POST', method: 'POST',
@@ -93,6 +88,8 @@ api.checkout = {
let orderReferenceId = req.body.orderReferenceId; let orderReferenceId = req.body.orderReferenceId;
let amount = 5; let amount = 5;
if (!orderReferenceId) throw new BadRequest('Missing req.body.orderReferenceId');
if (gift) { if (gift) {
if (gift.type === 'gems') { if (gift.type === 'gems') {
amount = gift.gems.amount / 4; amount = gift.gems.amount / 4;
@@ -101,68 +98,62 @@ api.checkout = {
} }
} }
try { await amzLib.setOrderReferenceDetails({
await amzLib.setOrderReferenceDetails({ AmazonOrderReferenceId: orderReferenceId,
AmazonOrderReferenceId: orderReferenceId, OrderReferenceAttributes: {
OrderReferenceAttributes: { OrderTotal: {
OrderTotal: {
CurrencyCode: 'USD',
Amount: amount,
},
SellerNote: 'HabitRPG Payment',
SellerOrderAttributes: {
SellerOrderId: shared.uuid(),
StoreName: 'HabitRPG',
},
},
});
await amzLib.confirmOrderReference({ AmazonOrderReferenceId: orderReferenceId });
await amzLib.authorize({
AmazonOrderReferenceId: orderReferenceId,
AuthorizationReferenceId: shared.uuid().substring(0, 32),
AuthorizationAmount: {
CurrencyCode: 'USD', CurrencyCode: 'USD',
Amount: amount, Amount: amount,
}, },
SellerAuthorizationNote: 'HabitRPG Payment', SellerNote: 'HabitRPG Payment',
TransactionTimeout: 0, SellerOrderAttributes: {
CaptureNow: true, SellerOrderId: shared.uuid(),
}); StoreName: 'HabitRPG',
},
},
});
await amzLib.closeOrderReference({ AmazonOrderReferenceId: orderReferenceId }); await amzLib.confirmOrderReference({ AmazonOrderReferenceId: orderReferenceId });
// execute payment await amzLib.authorize({
let method = 'buyGems'; AmazonOrderReferenceId: orderReferenceId,
let data = { user, paymentMethod: 'Amazon Payments' }; AuthorizationReferenceId: shared.uuid().substring(0, 32),
if (gift) { AuthorizationAmount: {
if (gift.type === 'subscription') method = 'createSubscription'; CurrencyCode: 'USD',
gift.member = await User.findById(gift ? gift.uuid : undefined); Amount: amount,
data.gift = gift; },
data.paymentMethod = 'Gift'; SellerAuthorizationNote: 'HabitRPG Payment',
} TransactionTimeout: 0,
await payments[method](data); CaptureNow: true,
});
res.respond(200); await amzLib.closeOrderReference({ AmazonOrderReferenceId: orderReferenceId });
} catch (error) {
throw new BadRequest(error); // execute payment
let method = 'buyGems';
let data = { user, paymentMethod: 'Amazon Payments' };
if (gift) {
if (gift.type === 'subscription') method = 'createSubscription';
gift.member = await User.findById(gift ? gift.uuid : undefined);
data.gift = gift;
data.paymentMethod = 'Gift';
} }
await payments[method](data);
res.respond(200);
}, },
}; };
/** /**
* @apiIgnore Payments are considered part of the private API * @apiIgnore Payments are considered part of the private API
* @api {post} /amazon/subscribe Subscribe * @api {post} /amazon/subscribe Amazon Payments: subscribe
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName AmazonSubscribe * @apiName AmazonSubscribe
* @apiGroup Payments * @apiGroup Payments
* *
* @apiParam {string} billingAgreementId billing agreement id * @apiSuccess {object} data Empty object
* @apiParam {string} subscription Subscription plan
* @apiParam {string} coupon Coupon
*
* @apiSuccess {object} data.orderReferenceId The order reference id.
**/ **/
api.subscribe = { api.subscribe = {
method: 'POST', method: 'POST',
@@ -174,67 +165,62 @@ api.subscribe = {
let coupon = req.body.coupon; let coupon = req.body.coupon;
let user = res.locals.user; let user = res.locals.user;
if (!sub) { if (!sub) throw new BadRequest(res.t('missingSubscriptionCode'));
throw new BadRequest(res.t('missingSubscriptionCode')); if (!billingAgreementId) throw new BadRequest('Missing req.body.billingAgreementId');
if (sub.discount) { // apply discount
if (!coupon) throw new BadRequest(res.t('couponCodeRequired'));
let result = await Coupon.findOne({_id: cc.validate(coupon), event: sub.key});
if (!result) throw new NotAuthorized(res.t('invalidCoupon'));
} }
try { await amzLib.setBillingAgreementDetails({
if (sub.discount) { // apply discount AmazonBillingAgreementId: billingAgreementId,
if (!coupon) throw new BadRequest(res.t('couponCodeRequired')); BillingAgreementAttributes: {
let result = await Coupon.findOne({_id: cc.validate(coupon), event: sub.key}); SellerNote: 'HabitRPG Subscription',
if (!result) throw new BadRequest(res.t('invalidCoupon')); SellerBillingAgreementAttributes: {
} SellerBillingAgreementId: shared.uuid(),
await amzLib.setBillingAgreementDetails({
AmazonBillingAgreementId: billingAgreementId,
BillingAgreementAttributes: {
SellerNote: 'HabitRPG Subscription',
SellerBillingAgreementAttributes: {
SellerBillingAgreementId: shared.uuid(),
StoreName: 'HabitRPG',
CustomInformation: 'HabitRPG Subscription',
},
},
});
await amzLib.confirmBillingAgreement({
AmazonBillingAgreementId: billingAgreementId,
});
await amzLib.authorizeOnBillingAgreement({
AmazonBillingAgreementId: billingAgreementId,
AuthorizationReferenceId: shared.uuid().substring(0, 32),
AuthorizationAmount: {
CurrencyCode: 'USD',
Amount: sub.price,
},
SellerAuthorizationNote: 'HabitRPG Subscription Payment',
TransactionTimeout: 0,
CaptureNow: true,
SellerNote: 'HabitRPG Subscription Payment',
SellerOrderAttributes: {
SellerOrderId: shared.uuid(),
StoreName: 'HabitRPG', StoreName: 'HabitRPG',
CustomInformation: 'HabitRPG Subscription',
}, },
}); },
});
await payments.createSubscription({ await amzLib.confirmBillingAgreement({
user, AmazonBillingAgreementId: billingAgreementId,
customerId: billingAgreementId, });
paymentMethod: 'Amazon Payments',
sub,
});
res.respond(200); await amzLib.authorizeOnBillingAgreement({
} catch (error) { AmazonBillingAgreementId: billingAgreementId,
throw new BadRequest(error); AuthorizationReferenceId: shared.uuid().substring(0, 32),
} AuthorizationAmount: {
CurrencyCode: 'USD',
Amount: sub.price,
},
SellerAuthorizationNote: 'HabitRPG Subscription Payment',
TransactionTimeout: 0,
CaptureNow: true,
SellerNote: 'HabitRPG Subscription Payment',
SellerOrderAttributes: {
SellerOrderId: shared.uuid(),
StoreName: 'HabitRPG',
},
});
await payments.createSubscription({
user,
customerId: billingAgreementId,
paymentMethod: 'Amazon Payments',
sub,
});
res.respond(200);
}, },
}; };
/** /**
* @apiIgnore Payments are considered part of the private API * @apiIgnore Payments are considered part of the private API
* @api {get} /amazon/subscribe/cancel SubscribeCancel * @api {get} /amazon/subscribe/cancel Amazon Payments: subscribe cancel
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName AmazonSubscribe * @apiName AmazonSubscribe
* @apiGroup Payments * @apiGroup Payments
@@ -249,21 +235,20 @@ api.subscribeCancel = {
if (!billingAgreementId) throw new NotAuthorized(res.t('missingSubscription')); if (!billingAgreementId) throw new NotAuthorized(res.t('missingSubscription'));
try { await amzLib.closeBillingAgreement({
await amzLib.closeBillingAgreement({ AmazonBillingAgreementId: billingAgreementId,
AmazonBillingAgreementId: billingAgreementId, });
});
let data = { await payments.cancelSubscription({
user, user,
nextBill: moment(user.purchased.plan.lastBillingDate).add({ days: 30 }), nextBill: moment(user.purchased.plan.lastBillingDate).add({ days: 30 }),
paymentMethod: 'Amazon Payments', paymentMethod: 'Amazon Payments',
}; });
await payments.cancelSubscription(data);
if (req.query.noRedirect) {
res.respond(200);
} else {
res.redirect('/'); res.redirect('/');
} catch (error) {
throw new BadRequest(error.message);
} }
}, },
}; };

View File

@@ -45,6 +45,8 @@ api.checkout = {
let coupon; let coupon;
let response; let response;
if (!token) throw new BadRequest('Missing req.body.id');
if (sub) { if (sub) {
if (sub.discount) { if (sub.discount) {
if (!req.query.coupon) throw new BadRequest(res.t('couponCodeRequired')); if (!req.query.coupon) throw new BadRequest(res.t('couponCodeRequired'));
@@ -127,10 +129,12 @@ api.subscribeEdit = {
let customerId = user.purchased.plan.customerId; let customerId = user.purchased.plan.customerId;
if (!customerId) throw new NotAuthorized(res.t('missingSubscription')); if (!customerId) throw new NotAuthorized(res.t('missingSubscription'));
if (!token) throw new BadRequest('Missing req.body.id');
let subscriptions = await stripe.customers.listSubscriptions(customerId); let subscriptions = await stripe.customers.listSubscriptions(customerId);
let subscriptionId = subscriptions.data[0].id; let subscriptionId = subscriptions.data[0].id;
await stripe.customers.updateSubscription(customerId, subscriptionId, { card: token }); await stripe.customers.updateSubscription(customerId, subscriptionId, { card: token });
res.respond(200, {}); res.respond(200, {});
}, },
}; };

View File

@@ -6,7 +6,9 @@ import {
BadRequest, BadRequest,
} from './errors'; } from './errors';
const t = common.i18n.t; // TODO better handling of errors
const i18n = common.i18n;
const IS_PROD = nconf.get('NODE_ENV') === 'production'; const IS_PROD = nconf.get('NODE_ENV') === 'production';
let amzPayment = amazonPayments.connect({ let amzPayment = amazonPayments.connect({
@@ -30,7 +32,7 @@ let authorizeOnBillingAgreement = (inputSet) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
amzPayment.offAmazonPayments.authorizeOnBillingAgreement(inputSet, (err, response) => { amzPayment.offAmazonPayments.authorizeOnBillingAgreement(inputSet, (err, response) => {
if (err) return reject(err); if (err) return reject(err);
if (response.AuthorizationDetails.AuthorizationStatus.State === 'Declined') return reject(new BadRequest(t('paymentNotSuccessful'))); if (response.AuthorizationDetails.AuthorizationStatus.State === 'Declined') return reject(new BadRequest(i18n.t('paymentNotSuccessful')));
return resolve(response); return resolve(response);
}); });
}); });
@@ -40,7 +42,7 @@ let authorize = (inputSet) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
amzPayment.offAmazonPayments.authorize(inputSet, (err, response) => { amzPayment.offAmazonPayments.authorize(inputSet, (err, response) => {
if (err) return reject(err); if (err) return reject(err);
if (response.AuthorizationDetails.AuthorizationStatus.State === 'Declined') return reject(new BadRequest(t('paymentNotSuccessful'))); if (response.AuthorizationDetails.AuthorizationStatus.State === 'Declined') return reject(new BadRequest(i18n.t('paymentNotSuccessful')));
return resolve(response); return resolve(response);
}); });
}); });