Skip to content

Commit

Permalink
feat(amazon-payments): working subscriptions, put donations and gifts…
Browse files Browse the repository at this point in the history
… on hold
  • Loading branch information
paglias committed Jun 30, 2015
1 parent 8747cf4 commit 65f4aac
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 64 deletions.
117 changes: 70 additions & 47 deletions website/public/js/services/paymentServices.js
Expand Up @@ -65,6 +65,7 @@ function($rootScope, User, $http, Content) {
console.error(error);
console.log(error.getErrorMessage(), error.getErrorCode());
alert(error.getErrorMessage());
Payments.amazonPayments.reset();
};

Payments.amazonPayments = {};
Expand All @@ -79,24 +80,30 @@ function($rootScope, User, $http, Content) {
Payments.amazonPayments.type = null;
Payments.amazonPayments.loggedIn = false;
Payments.amazonPayments.gift = null;
Payments.amazonPayments.orderReferenceId = null;
Payments.amazonPayments.billingAgreementId = null;
Payments.amazonPayments.paymentSelected = false;
Payments.amazonPayments.recurringConsent = false;
Payments.amazonPayments.subscription = null;
Payments.amazonPayments.coupon = null;
};

// Needs to be called everytime the modal/router is accessed
Payments.amazonPayments.init = function(type, gift, giftedTo){
if(gift){
if(gift.gems && gift.gems.amount && gift.gems.amount <= 0) return;
gift.uuid = giftedTo;
Payments.amazonPayments.init = function(data){
if(!isAmazonReady) return;
if(data.type !== 'donation' && data.type !== 'subscription') return;

if(data.gift){
if(data.gift.gems && data.gift.gems.amount && data.gift.gems.amount <= 0) return;
gift.uuid = data.giftedTo;
}

if(!isAmazonReady) return;
if(type !== 'donation' && type !== 'subscription') return;
if(data.subscription){
Payments.amazonPayments.subscription = data.subscription;
Payments.amazonPayments.coupon = data.coupon;
}

Payments.amazonPayments.gift = gift;
Payments.amazonPayments.type = type;
Payments.amazonPayments.gift = data.gift;
Payments.amazonPayments.type = data.type;

var modal = Payments.amazonPayments.modal = $rootScope.openModal('amazonPayments', {
// Allow the modal to be closed only by pressing cancel
Expand Down Expand Up @@ -153,8 +160,38 @@ function($rootScope, User, $http, Content) {
design: {
designMode: 'responsive'
},
agreementType: 'BillingAgreement',

onPaymentSelect: function(orderReference) {
onReady: function(billingAgreement){
Payments.amazonPayments.billingAgreementId = billingAgreement.getAmazonBillingAgreementId();

if(Payments.amazonPayments.type === 'subscription'){
new OffAmazonPayments.Widgets.Consent({
sellerId: window.env.AMAZON_PAYMENTS.SELLER_ID,
amazonBillingAgreementId: Payments.amazonPayments.billingAgreementId,
design: {
designMode: 'responsive'
},

onReady: function(consent){
$rootScope.$apply(function(){
var getConsent = consent.getConsentStatus
Payments.amazonPayments.recurringConsent = getConsent ? getConsent() : false;
});
},

onConsent: function(consent){
$rootScope.$apply(function(){
Payments.amazonPayments.recurringConsent = consent.getConsentStatus();
});
},

onError: amazonOnError
}).bind('AmazonPayRecurring');
}
},

onPaymentSelect: function() {
$rootScope.$apply(function(){
Payments.amazonPayments.paymentSelected = true;
});
Expand All @@ -163,48 +200,14 @@ function($rootScope, User, $http, Content) {
onError: amazonOnError
};

if(Payments.amazonPayments.type === 'donation'){
walletParams.onOrderReferenceCreate = function(orderReference) {
Payments.amazonPayments.orderReferenceId = orderReference.getAmazonOrderReferenceId();
}
}else if(Payments.amazonPayments.type === 'subscription'){
walletParams.onReady = function(billingAgreement) {
Payments.amazonPayments.billingAgreementId = billingAgreement.getAmazonBillingAgreementId();

new OffAmazonPayments.Widgets.Consent({
sellerId: window.env.AMAZON_PAYMENTS.SELLER_ID,
amazonBillingAgreementId: Payments.amazonPayments.billingAgreementId,
design: {
designMode: 'responsive'
},

onReady: function(consent){
$rootScope.$apply(function(){
Payments.amazonPayments.recurringConsent = consent.getConsentStatus();
});
},

onConsent: function(consent){
$rootScope.$apply(function(){
Payments.amazonPayments.recurringConsent = consent.getConsentStatus();
});
},

onError: amazonOnError
}).bind('AmazonPayRecurring');
};

walletParams.agreementType = 'BillingAgreement';
}

new OffAmazonPayments.Widgets.Wallet(walletParams).bind('AmazonPayWallet');
}

Payments.amazonPayments.checkout = function(){
if(Payments.amazonPayments.type === 'donation'){
var url = '/amazon/checkout'
$http.post(url, {
orderReferenceId: Payments.amazonPayments.orderReferenceId,
billingAgreementId: Payments.amazonPayments.billingAgreementId,
gift: Payments.amazonPayments.gift
}).success(function(){
Payments.amazonPayments.reset();
Expand All @@ -214,13 +217,33 @@ function($rootScope, User, $http, Content) {
Payments.amazonPayments.reset();
});
}else if(Payments.amazonPayments.type === 'subscription'){
return false
var url = '/amazon/subscribe';

$http.post(url, {
billingAgreementId: Payments.amazonPayments.billingAgreementId,
subscription: Payments.amazonPayments.subscription,
coupon: Payments.amazonPayments.coupon
}).success(function(){
Payments.amazonPayments.reset();
window.location.reload(true);
}).error(function(res){
alert(res.err);
Payments.amazonPayments.reset();
});
}
}

Payments.cancelSubscription = function(){
if (!confirm(window.env.t('sureCancelSub'))) return;
window.location.href = '/' + User.user.purchased.plan.paymentMethod.toLowerCase() + '/subscribe/cancel?_id=' + User.user._id + '&apiToken=' + User.user.apiToken;
var paymentMethod = User.user.purchased.plan.paymentMethod;

if(paymentMethod === 'Amazon Payments'){
paymentMethod = 'amazon';
}else{
paymentMethod = paymentMethod.toLowerCase();
}

window.location.href = '/' + paymentMethod + '/subscribe/cancel?_id=' + User.user._id + '&apiToken=' + User.user.apiToken;
}

Payments.encodeGift = function(uuid, gift){
Expand Down
153 changes: 143 additions & 10 deletions website/src/controllers/payments/amazon.js
@@ -1,9 +1,12 @@
var amazonPayments = require('amazon-payments');
var mongoose = require('mongoose');
var moment = require('moment');
var nconf = require('nconf');
var async = require('async');
var User = require('mongoose').model('User');
var shared = require('../../../../common');
var payments = require('./index');
var cc = require('coupon-code');
var isProd = nconf.get("NODE_ENV") === 'production';

var amzPayment = amazonPayments.connect({
Expand All @@ -27,24 +30,49 @@ exports.verifyAccessToken = function(req, res, next){
};

exports.checkout = function(req, res, next){
if(!req.body || !req.body['orderReferenceId']){
return res.json(400, {err: 'Order Reference Id not supplied.'});
if(!req.body || !req.body['billingAgreementId']){
return res.json(400, {err: 'Billing Agreement Id not supplied.'});
}

var gift = req.body.gift;
var user = res.locals.user;
var orderReferenceId = req.body.orderReferenceId;
var billingAgreementId = req.body.billingAgreementId;
var orderReferenceId;
var 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;
}
}

async.series({
createOrderReferenceForId: function(cb){
amzPayment.offAmazonPayments.createOrderReferenceForId({
Id: billingAgreementId,
IdType: 'BillingAgreement'
}, function(err, response){
if(err) return cb(err);
if(!response.OrderReferenceDetails || !response.OrderReferenceDetails.AmazonOrderReferenceId){
return cb('Missing attributes in Amazon response.');
}

orderReferenceId = response.OrderReferenceDetails.AmazonOrderReferenceId;
return cb();
});
},

setOrderReferenceDetails: function(cb){
amzPayment.offAmazonPayments.setOrderReferenceDetails({
AmazonOrderReferenceId: orderReferenceId,
OrderReferenceAttributes: {
OrderTotal: {
CurrencyCode: 'USD',
Amount: gift ? (gift.gems.amount/4) : 5
Amount: amount
},
SellerNote: 'HabitRPG Gems',
SellerNote: 'HabitRPG Payment',
SellerOrderAttributes: {
SellerOrderId: shared.uuid(),
StoreName: 'HabitRPG'
Expand All @@ -65,9 +93,9 @@ exports.checkout = function(req, res, next){
AuthorizationReferenceId: shared.uuid().substring(0, 32),
AuthorizationAmount: {
CurrencyCode: 'USD',
Amount: gift ? (gift.gems.amount/4) : 5
Amount: amount
},
SellerAuthorizationNote: 'HabitRPG Donation',
SellerAuthorizationNote: 'HabitRPG Payment',
TransactionTimeout: 0,
CaptureNow: true
}, cb);
Expand All @@ -87,12 +115,13 @@ exports.checkout = function(req, res, next){
var method = 'buyGems';

if (gift){
if (gift.type == 'subscription') method = 'createSubscription';
gift.member = member;
data.gift = gift;
data.paymentMethod = 'Gift';
}

payments.buyGems(data, cb2);
payments[method](data, cb2);
}
], cb);
}
Expand All @@ -104,8 +133,112 @@ exports.checkout = function(req, res, next){

};

exports.setupSubscription = function(req, res, next){
if(!req.body || !req.body['orderReferenceId']){
exports.subscribe = function(req, res, next){
if(!req.body || !req.body['billingAgreementId']){
return res.json(400, {err: 'Billing Agreement Id not supplied.'});
}

var billingAgreementId = req.body.billingAgreementId
var sub = req.body.subscription ? shared.content.subscriptionBlocks[req.body.subscription] : false;
var coupon = req.body.coupon;
var user = res.locals.user;

if(!sub){
return res.json(400, {err: 'Subscription plan not found.'});
}

async.series({
applyDiscount: function(cb){
if (!sub.discount) return cb();
if (!coupon) return cb(new Error('Please provide a coupon code for this plan.'));
mongoose.model('Coupon').findOne({_id:cc.validate(coupon), event:sub.key}, function(err, coupon){
if(err) return cb(err);
if(!coupon) return cb(new Error('Coupon code not found.'));
cb()
});
},

setBillingAgreementDetails: function(cb){
amzPayment.offAmazonPayments.setBillingAgreementDetails({
AmazonBillingAgreementId: billingAgreementId,
BillingAgreementAttributes: {
SellerNote: 'HabitRPG Subscription',
SellerBillingAgreementAttributes: {
SellerBillingAgreementId: shared.uuid(),
StoreName: 'HabitRPG',
CustomInformation: 'HabitRPG Subscription'
}
}
}, cb);
},

confirmBillingAgreement: function(cb){
amzPayment.offAmazonPayments.confirmBillingAgreement({
AmazonBillingAgreementId: billingAgreementId
}, cb);
},

authorizeOnBillingAgreeement: function(cb){
amzPayment.offAmazonPayments.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'
}
}, cb);
},

createSubscription: function(cb){
payments.createSubscription({
user: user,
customerId: billingAgreementId,
paymentMethod: 'Amazon Payments',
sub: sub
}, cb);
}
}, function(err, results){
if(err) return next(err);

res.send(200);
});
};

exports.subscribeCancel = function(req, res, next){
var user = res.locals.user;
if (!user.purchased.plan.customerId)
return res.json(401, {err: "User does not have a plan subscription"});

var billingAgreementId = user.purchased.plan.customerId;

async.series({
closeBillingAgreement: function(cb){
amzPayment.offAmazonPayments.closeBillingAgreement({
AmazonBillingAgreementId: billingAgreementId
}, cb);
},

cancelSubscription: function(cb){
var data = {
user: user,
// Date of next bill, dateUpdated can be used because it's only updated when the user is billed
nextBill: moment(user.purchased.plan.dateUpdated).add({days: 30}),
paymentMethod: 'Amazon Payments'
};

payments.cancelSubscription(data, cb);
}
}, function(err, results){
if (err) return next(err); // don't json this, let toString() handle errors
res.redirect('/');
user = null;
});
};
2 changes: 2 additions & 0 deletions website/src/controllers/payments/index.js
Expand Up @@ -172,6 +172,8 @@ exports.paypalIPN = paypal.ipn;

exports.amazonVerifyAccessToken = amazon.verifyAccessToken;
exports.amazonCheckout = amazon.checkout;
exports.amazonSubscribe = amazon.subscribe;
exports.amazonSubscribeCancel = amazon.subscribeCancel;

exports.iapAndroidVerify = iap.androidVerify;
exports.iapIosVerify = iap.iosVerify;

0 comments on commit 65f4aac

Please sign in to comment.