working on promisifying amazonPayments

This commit is contained in:
Victor Piousbox
2016-04-14 22:38:41 +00:00
parent 1d5d6e9146
commit 261a5a66b1
14 changed files with 521 additions and 1167 deletions

View File

@@ -170,5 +170,10 @@
"pushDeviceAdded": "Push device added successfully", "pushDeviceAdded": "Push device added successfully",
"pushDeviceAlreadyAdded": "The user already has the push device", "pushDeviceAlreadyAdded": "The user already has the push device",
"resetComplete": "Reset completed", "resetComplete": "Reset completed",
"lvl10ChangeClass": "To change class you must be at least level 10." "lvl10ChangeClass": "To change class you must be at least level 10.",
"resetComplete": "Reset has completed",
"missingAccessToken": "The request is missing a required parameter : access_token",
"missingBillingAgreementId": "Missing billing agreement id",
"missingAttributesFromAmazon": "Missing attributes from Amazon",
"errorFromAmazon": "Error from Amazon"
} }

View File

@@ -358,6 +358,10 @@ gulp.task('test:api-v3:unit', (done) => {
pipe(runner); pipe(runner);
}); });
gulp.task('test:api-v3:unit:watch', () => {
gulp.watch(['website/src/libs/api-v3/*', 'test/api/v3/unit/libs/*'], ['test:api-v3:unit']);
});
gulp.task('test:api-v3:integration', (done) => { gulp.task('test:api-v3:integration', (done) => {
let runner = exec( let runner = exec(
testBin('mocha test/api/v3/integration --recursive'), testBin('mocha test/api/v3/integration --recursive'),

View File

@@ -0,0 +1,21 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
describe('payments : amazon', () => {
let endpoint = '/payments/amazon/verifyAccessToken';
let user;
beforeEach(async () => {
user = await generateUser();
});
it('verify access token', async () => {
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('missingAccessToken'),
});
});
});

View File

@@ -0,0 +1,24 @@
import * as amz from '../../../../../website/src/libs/api-v3/amazonPayments';
describe.only('amazonPayments', () => {
beforeEach(() => {
});
describe('#getTokenInfo', () => {
it('validates access_token parameter', async (done) => {
try {
let result = await amz.getTokenInfo();
} catch (e) {
expect(e.type).to.eql('invalid_request');
done();
}
});
});
describe('#createOrderReferenceId', () => {
it('is sane', () => {
expect(false).to.eql(true); // @TODO
});
});
});

View File

@@ -1,277 +0,0 @@
import amazonPayments from 'amazon-payments';
import async from 'async';
import cc from 'coupon-code';
import mongoose from 'mongoose';
import moment from 'moment';
import nconf from 'nconf';
import payments from './index';
import shared from '../../../../../common';
import { model as User } from '../../models/user';
const IS_PROD = nconf.get('NODE_ENV') === 'production';
let api = {};
let amzPayment = amazonPayments.connect({
environment: amazonPayments.Environment[IS_PROD ? 'Production' : 'Sandbox'],
sellerId: nconf.get('AMAZON_PAYMENTS:SELLER_ID'),
mwsAccessKey: nconf.get('AMAZON_PAYMENTS:MWS_KEY'),
mwsSecretKey: nconf.get('AMAZON_PAYMENTS:MWS_SECRET'),
clientId: nconf.get('AMAZON_PAYMENTS:CLIENT_ID'),
});
api.verifyAccessToken = function verifyAccessToken (req, res) {
if (!req.body || !req.body.access_token) {
return res.status(400).json({err: 'Access token not supplied.'});
}
amzPayment.api.getTokenInfo(req.body.access_token, function getTokenInfo (err) {
if (err) return res.status(400).json({err});
res.sendStatus(200);
});
};
api.createOrderReferenceId = function createOrderReferenceId (req, res, next) {
if (!req.body || !req.body.billingAgreementId) {
return res.status(400).json({err: 'Billing Agreement Id not supplied.'});
}
amzPayment.offAmazonPayments.createOrderReferenceForId({
Id: req.body.billingAgreementId,
IdType: 'BillingAgreement',
ConfirmNow: false,
}, function createOrderReferenceForId (err, response) {
if (err) return next(err);
if (!response.OrderReferenceDetails || !response.OrderReferenceDetails.AmazonOrderReferenceId) {
return next(new Error('Missing attributes in Amazon response.'));
}
res.json({
orderReferenceId: response.OrderReferenceDetails.AmazonOrderReferenceId,
});
});
};
api.checkout = function checkout (req, res, next) {
if (!req.body || !req.body.orderReferenceId) {
return res.status(400).json({err: 'Billing Agreement Id not supplied.'});
}
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;
}
}
async.series({
setOrderReferenceDetails (cb) {
amzPayment.offAmazonPayments.setOrderReferenceDetails({
AmazonOrderReferenceId: orderReferenceId,
OrderReferenceAttributes: {
OrderTotal: {
CurrencyCode: 'USD',
Amount: amount,
},
SellerNote: 'HabitRPG Payment',
SellerOrderAttributes: {
SellerOrderId: shared.uuid(),
StoreName: 'HabitRPG',
},
},
}, cb);
},
confirmOrderReference (cb) {
amzPayment.offAmazonPayments.confirmOrderReference({
AmazonOrderReferenceId: orderReferenceId,
}, cb);
},
authorize (cb) {
amzPayment.offAmazonPayments.authorize({
AmazonOrderReferenceId: orderReferenceId,
AuthorizationReferenceId: shared.uuid().substring(0, 32),
AuthorizationAmount: {
CurrencyCode: 'USD',
Amount: amount,
},
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);
},
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);
},
], 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.'});
}
let billingAgreementId = req.body.billingAgreementId;
let sub = req.body.subscription ? shared.content.subscriptionBlocks[req.body.subscription] : false;
let coupon = req.body.coupon;
let user = res.locals.user;
if (!sub) {
return res.status(400).json({err: 'Subscription plan not found.'});
}
async.series({
applyDiscount (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 couponResult (err) {
if (err) return cb(err);
if (!coupon) return cb(new Error('Coupon code not found.'));
cb();
});
},
setBillingAgreementDetails (cb) {
amzPayment.offAmazonPayments.setBillingAgreementDetails({
AmazonBillingAgreementId: billingAgreementId,
BillingAgreementAttributes: {
SellerNote: 'HabitRPG Subscription',
SellerBillingAgreementAttributes: {
SellerBillingAgreementId: shared.uuid(),
StoreName: 'HabitRPG',
CustomInformation: 'HabitRPG Subscription',
},
},
}, cb);
},
confirmBillingAgreement (cb) {
amzPayment.offAmazonPayments.confirmBillingAgreement({
AmazonBillingAgreementId: billingAgreementId,
}, cb);
},
authorizeOnBillingAgreement (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',
},
}, function billingAgreementResult (err) {
if (err) return cb(err);
if (res.AuthorizationDetails.AuthorizationStatus.State === 'Declined') {
return cb(new Error('The payment was not successful.'));
}
return cb();
});
},
createSubscription (cb) {
payments.createSubscription({
user,
customerId: billingAgreementId,
paymentMethod: 'Amazon Payments',
sub,
}, cb);
},
}, function subscribeResult (err) {
if (err) return next(err);
res.sendStatus(200);
});
};
api.subscribeCancel = function subscribeCancel (req, res, next) {
let user = res.locals.user;
if (!user.purchased.plan.customerId)
return res.status(401).json({err: 'User does not have a plan subscription'});
let billingAgreementId = user.purchased.plan.customerId;
async.series({
closeBillingAgreement (cb) {
amzPayment.offAmazonPayments.closeBillingAgreement({
AmazonBillingAgreementId: billingAgreementId,
}, cb);
},
cancelSubscription (cb) {
let data = {
user,
// Date of next bill
nextBill: moment(user.purchased.plan.lastBillingDate).add({days: 30}),
paymentMethod: 'Amazon Payments',
};
payments.cancelSubscription(data, cb);
},
}, function subscribeCancelResult (err) {
if (err) return next(err); // don't json this, let toString() handle errors
if (req.query.noRedirect) {
res.sendStatus(200);
} else {
res.redirect('/');
}
user = null;
});
};
module.exports = api;

View File

@@ -1,158 +0,0 @@
import {
iap,
inAppPurchase, }
from 'in-app-purchase';
import payments from './index';
import nconf from 'nconf';
inAppPurchase.config({
// this is the path to the directory containing iap-sanbox/iap-live files
googlePublicKeyPath: nconf.get('IAP_GOOGLE_KEYDIR'),
});
// Validation ERROR Codes
const INVALID_PAYLOAD = 6778001;
/* const CONNECTION_FAILED = 6778002;
const PURCHASE_EXPIRED = 6778003; */ // These variables were never used??
let api = {};
api.androidVerify = function androidVerify (req, res) {
let iapBody = req.body;
let user = res.locals.user;
iap.setup(function googleSetupResult (error) {
if (error) {
let resObj = {
ok: false,
data: 'IAP Error',
};
return res.json(resObj);
}
/*
google receipt must be provided as an object
{
"data": "{stringified data object}",
"signature": "signature from google"
}
*/
let testObj = {
data: iapBody.transaction.receipt,
signature: iapBody.transaction.signature,
};
// iap is ready
iap.validate(iap.GOOGLE, testObj, function googleValidateResult (err, googleRes) {
if (err) {
let resObj = {
ok: false,
data: {
code: INVALID_PAYLOAD,
message: err.toString(),
},
};
return res.json(resObj);
}
if (iap.isValidated(googleRes)) {
let resObj = {
ok: true,
data: googleRes,
};
payments.buyGems({user, paymentMethod: 'IAP GooglePlay', amount: 5.25});
return res.json(resObj);
}
});
});
};
exports.iosVerify = function iosVerify (req, res) {
let iapBody = req.body;
let user = res.locals.user;
iap.setup(function iosSetupResult (error) {
if (error) {
let resObj = {
ok: false,
data: 'IAP Error',
};
return res.json(resObj);
}
// iap is ready
iap.validate(iap.APPLE, iapBody.transaction.receipt, function iosValidateResult (err, appleRes) {
if (err) {
let resObj = {
ok: false,
data: {
code: INVALID_PAYLOAD,
message: err.toString(),
},
};
return res.json(resObj);
}
if (iap.isValidated(appleRes)) {
let purchaseDataList = iap.getPurchaseData(appleRes);
if (purchaseDataList.length > 0) {
let correctReceipt = true;
for (let index of purchaseDataList) {
switch (purchaseDataList[index].productId) {
case 'com.habitrpg.ios.Habitica.4gems':
payments.buyGems({user, paymentMethod: 'IAP AppleStore', amount: 1});
break;
case 'com.habitrpg.ios.Habitica.8gems':
payments.buyGems({user, paymentMethod: 'IAP AppleStore', amount: 2});
break;
case 'com.habitrpg.ios.Habitica.20gems':
case 'com.habitrpg.ios.Habitica.21gems':
payments.buyGems({user, paymentMethod: 'IAP AppleStore', amount: 5.25});
break;
case 'com.habitrpg.ios.Habitica.42gems':
payments.buyGems({user, paymentMethod: 'IAP AppleStore', amount: 10.5});
break;
default:
correctReceipt = false;
}
}
if (correctReceipt) {
let resObj = {
ok: true,
data: appleRes,
};
// yay good!
return res.json(resObj);
}
}
// wrong receipt content
let resObj = {
ok: false,
data: {
code: INVALID_PAYLOAD,
message: 'Incorrect receipt content',
},
};
return res.json(resObj);
}
// invalid receipt
let resObj = {
ok: false,
data: {
code: INVALID_PAYLOAD,
message: 'Invalid receipt',
},
};
return res.json(resObj);
});
});
};
module.exports = api;

View File

@@ -1,232 +0,0 @@
import _ from 'lodash' ;
import analytics from '../../../libs/api-v3/analyticsService';
import async from 'async';
import cc from 'coupon-code';
import {
getUserInfo,
sendTxn as txnEmail,
} from '../../../libs/api-v3/email';
import members from '../members';
import moment from 'moment';
import mongoose from 'mongoose';
import nconf from 'nconf';
import pushNotify from '../../../libs/api-v3/pushNotifications';
import shared from '../../../../../common' ;
import amazon from './amazon';
import iap from './iap';
import paypal from './paypal';
import stripe from './stripe';
const IS_PROD = nconf.get('NODE_ENV') === 'production';
let api = {};
function revealMysteryItems (user) {
_.each(shared.content.gear.flat, function findMysteryItems (item) {
if (
item.klass === 'mystery' &&
moment().isAfter(shared.content.mystery[item.mystery].start) &&
moment().isBefore(shared.content.mystery[item.mystery].end) &&
!user.items.gear.owned[item.key] &&
user.purchased.plan.mysteryItems.indexOf(item.key) !== -1
) {
user.purchased.plan.mysteryItems.push(item.key);
}
});
}
api.createSubscription = function createSubscription (data, cb) {
let recipient = data.gift ? data.gift.member : data.user;
let plan = recipient.purchased.plan;
let block = shared.content.subscriptionBlocks[data.gift ? data.gift.subscription.key : data.sub.key];
let months = Number(block.months);
if (data.gift) {
if (plan.customerId && !plan.dateTerminated) { // User has active plan
plan.extraMonths += months;
} else {
plan.dateTerminated = moment(plan.dateTerminated).add({months}).toDate();
if (!plan.dateUpdated) plan.dateUpdated = new Date();
}
if (!plan.customerId) plan.customerId = 'Gift'; // don't override existing customer, but all sub need a customerId
} else {
_(plan).merge({ // override with these values
planId: block.key,
customerId: data.customerId,
dateUpdated: new Date(),
gemsBought: 0,
paymentMethod: data.paymentMethod,
extraMonths: Number(plan.extraMonths) +
Number(plan.dateTerminated ? moment(plan.dateTerminated).diff(new Date(), 'months', true) : 0),
dateTerminated: null,
// Specify a lastBillingDate just for Amazon Payments
// Resetted every time the subscription restarts
lastBillingDate: data.paymentMethod === 'Amazon Payments' ? new Date() : undefined,
}).defaults({ // allow non-override if a plan was previously used
dateCreated: new Date(),
mysteryItems: [],
}).value();
}
// Block sub perks
let perks = Math.floor(months / 3);
if (perks) {
plan.consecutive.offset += months;
plan.consecutive.gemCapExtra += perks * 5;
if (plan.consecutive.gemCapExtra > 25) plan.consecutive.gemCapExtra = 25;
plan.consecutive.trinkets += perks;
}
revealMysteryItems(recipient);
if (IS_PROD) {
if (!data.gift) txnEmail(data.user, 'subscription-begins');
let analyticsData = {
uuid: data.user._id,
itemPurchased: 'Subscription',
sku: `${data.paymentMethod.toLowerCase()}-subscription`,
purchaseType: 'subscribe',
paymentMethod: data.paymentMethod,
quantity: 1,
gift: Boolean(data.gift),
purchaseValue: block.price,
};
analytics.trackPurchase(analyticsData);
}
data.user.purchased.txnCount++;
if (data.gift) {
members.sendMessage(data.user, data.gift.member, data.gift);
let byUserName = getUserInfo(data.user, ['name']).name;
if (data.gift.member.preferences.emailNotifications.giftedSubscription !== false) {
txnEmail(data.gift.member, 'gifted-subscription', [
{name: 'GIFTER', content: byUserName},
{name: 'X_MONTHS_SUBSCRIPTION', content: months},
]);
}
if (data.gift.member._id !== data.user._id) { // Only send push notifications if sending to a user other than yourself
pushNotify.sendNotify(data.gift.member, shared.i18n.t('giftedSubscription'), `${months} months - by ${byUserName}`);
}
}
async.parallel([
function saveGiftingUserData (cb2) {
data.user.save(cb2);
},
function saveRecipientUserData (cb2) {
if (data.gift) {
data.gift.member.save(cb2);
} else {
cb2(null);
}
},
], cb);
};
/**
* Sets their subscription to be cancelled later
*/
api.cancelSubscription = function cancelSubscription (data, cb) {
let plan = data.user.purchased.plan;
let now = moment();
let remaining = data.nextBill ? moment(data.nextBill).diff(new Date(), 'days') : 30;
plan.dateTerminated =
moment(`${now.format('MM')}/${moment(plan.dateUpdated).format('DD')}/${now.format('YYYY')}`)
.add({days: remaining}) // end their subscription 1mo from their last payment
.add({months: Math.ceil(plan.extraMonths)})// plus any extra time (carry-over, gifted subscription, etc) they have. FIXME: moment can't add months in fractions...
.toDate();
plan.extraMonths = 0; // clear extra time. If they subscribe again, it'll be recalculated from p.dateTerminated
data.user.save(cb);
txnEmail(data.user, 'cancel-subscription');
let analyticsData = {
uuid: data.user._id,
gaCategory: 'commerce',
gaLabel: data.paymentMethod,
paymentMethod: data.paymentMethod,
};
analytics.track('unsubscribe', analyticsData);
};
api.buyGems = function buyGems (data, cb) {
let amt = data.amount || 5;
amt = data.gift ? data.gift.gems.amount / 4 : amt;
(data.gift ? data.gift.member : data.user).balance += amt;
data.user.purchased.txnCount++;
if (IS_PROD) {
if (!data.gift) txnEmail(data.user, 'donation');
let analyticsData = {
uuid: data.user._id,
itemPurchased: 'Gems',
sku: `${data.paymentMethod.toLowerCase()}-checkout`,
purchaseType: 'checkout',
paymentMethod: data.paymentMethod,
quantity: 1,
gift: Boolean(data.gift),
purchaseValue: amt,
};
analytics.trackPurchase(analyticsData);
}
if (data.gift) {
let byUsername = getUserInfo(data.user, ['name']).name;
let gemAmount = data.gift.gems.amount || 20;
members.sendMessage(data.user, data.gift.member, data.gift);
if (data.gift.member.preferences.emailNotifications.giftedGems !== false) {
txnEmail(data.gift.member, 'gifted-gems', [
{name: 'GIFTER', content: byUsername},
{name: 'X_GEMS_GIFTED', content: gemAmount},
]);
}
if (data.gift.member._id !== data.user._id) { // Only send push notifications if sending to a user other than yourself
pushNotify.sendNotify(data.gift.member, shared.i18n.t('giftedGems'), `${gemAmount} Gems - by ${byUsername}`);
}
}
async.parallel([
function saveGiftingUserData (cb2) {
data.user.save(cb2);
},
function saveRecipientUserData (cb2) {
if (data.gift) {
data.gift.member.save(cb2);
} else {
cb2(null);
}
},
], cb);
};
api.validCoupon = function validCoupon (req, res, next) {
mongoose.model('Coupon').findOne({_id: cc.validate(req.params.code), event: 'google_6mo'}, function couponErrorCheck (err, coupon) {
if (err) return next(err);
if (!coupon) return res.status(401).json({err: 'Invalid coupon code'});
return res.sendStatus(200);
});
};
api.stripeCheckout = stripe.checkout;
api.stripeSubscribeCancel = stripe.subscribeCancel;
api.stripeSubscribeEdit = stripe.subscribeEdit;
api.paypalSubscribe = paypal.createBillingAgreement;
api.paypalSubscribeSuccess = paypal.executeBillingAgreement;
api.paypalSubscribeCancel = paypal.cancelSubscription;
api.paypalCheckout = paypal.createPayment;
api.paypalCheckoutSuccess = paypal.executePayment;
api.paypalIPN = paypal.ipn;
api.amazonVerifyAccessToken = amazon.verifyAccessToken;
api.amazonCreateOrderReferenceId = amazon.createOrderReferenceId;
api.amazonCheckout = amazon.checkout;
api.amazonSubscribe = amazon.subscribe;
api.amazonSubscribeCancel = amazon.subscribeCancel;
api.iapAndroidVerify = iap.androidVerify;
api.iapIosVerify = iap.iosVerify;
module.exports = api;

View File

@@ -1,135 +0,0 @@
import nconf from 'nconf';
import stripeModule from 'stripe';
import async from 'async';
import payments from './index';
import { model as User } from '../../models/user';
import shared from '../../../../../common';
import mongoose from 'mongoose';
import cc from 'coupon-code';
const stripe = stripeModule(nconf.get('STRIPE_API_KEY'));
let api = {};
/*
Setup Stripe response when posting payment
*/
api.checkout = function checkout (req, res) {
let token = req.body.id;
let user = res.locals.user;
let gift = req.query.gift ? JSON.parse(req.query.gift) : undefined;
let sub = req.query.sub ? shared.content.subscriptionBlocks[req.query.sub] : false;
async.waterfall([
function stripeCharge (cb) {
if (sub) {
async.waterfall([
function handleCoupon (cb2) {
if (!sub.discount) return cb2(null, null);
if (!req.query.coupon) return cb2('Please provide a coupon code for this plan.');
mongoose.model('Coupon').findOne({_id: cc.validate(req.query.coupon), event: sub.key}, cb2);
},
function createCustomer (coupon, cb2) {
if (sub.discount && !coupon) return cb2('Invalid coupon code.');
let customer = {
email: req.body.email,
metadata: {uuid: user._id},
card: token,
plan: sub.key,
};
stripe.customers.create(customer, cb2);
},
], cb);
} else {
let amount;
if (!gift) {
amount = '500';
} else if (gift.type === 'subscription') {
amount = `${shared.content.subscriptionBlocks[gift.subscription.key].price * 100}`;
} else {
amount = `${gift.gems.amount / 4 * 100}`;
}
stripe.charges.create({
amount,
currency: 'usd',
card: token,
}, cb);
}
},
function saveUserData (response, cb) {
if (sub) return payments.createSubscription({user, customerId: response.id, paymentMethod: 'Stripe', sub}, cb);
async.waterfall([
function findUser (cb2) {
User.findById(gift ? gift.uuid : undefined, cb2);
},
function prepData (member, cb2) {
let data = {user, customerId: response.id, paymentMethod: 'Stripe', gift};
let method = 'buyGems';
if (gift) {
gift.member = member;
if (gift.type === 'subscription') method = 'createSubscription';
data.paymentMethod = 'Gift';
}
payments[method](data, cb2);
},
], cb);
},
], function handleResponse (err) {
if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors
res.sendStatus(200);
user = token = null;
});
};
api.subscribeCancel = function subscribeCancel (req, res) {
let user = res.locals.user;
if (!user.purchased.plan.customerId) {
return res.status(401).json({err: 'User does not have a plan subscription'});
}
async.auto({
getCustomer: function getCustomer (cb) {
stripe.customers.retrieve(user.purchased.plan.customerId, cb);
},
deleteCustomer: ['getCustomer', function deleteCustomer (cb) {
stripe.customers.del(user.purchased.plan.customerId, cb);
}],
cancelSubscription: ['getCustomer', function cancelSubscription (cb, results) {
let data = {
user,
nextBill: results.get_cus.subscription.current_period_end * 1000, // timestamp is in seconds
paymentMethod: 'Stripe',
};
payments.cancelSubscription(data, cb);
}],
}, function handleResponse (err) {
if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors
res.redirect('/');
user = null;
});
};
api.subscribeEdit = function subscribeEdit (req, res) {
let token = req.body.id;
let user = res.locals.user;
let userId = user.purchased.plan.customerId;
let subscriptionId;
async.waterfall([
function listSubscriptions (cb) {
stripe.customers.listSubscriptions(userId, cb);
},
function updateSubscription (response, cb) {
subscriptionId = response.data[0].id;
stripe.customers.updateSubscription(userId, subscriptionId, { card: token }, cb);
},
function saveUser (response, cb) {
user.save(cb);
},
], function handleResponse (err) {
if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors
res.sendStatus(200);
token = user = userId = subscriptionId;
});
};
module.exports = api;

View File

@@ -1,112 +1,128 @@
var amazonPayments = require('amazon-payments'); import async from 'async';
var mongoose = require('mongoose'); import cc from 'coupon-code';
var moment = require('moment'); import mongoose from 'mongoose';
var nconf = require('nconf'); import moment from 'moment';
var async = require('async'); import payments from './index';
var User = require('mongoose').model('User'); import shared from '../../../../../common';
var shared = require('../../../../common'); import { model as User } from '../../../models/user';
var payments = require('./index'); import {
var cc = require('coupon-code'); NotFound,
var isProd = nconf.get('NODE_ENV') === 'production'; NotAuthorized,
BadRequest,
} from '../../../libs/api-v3/errors';
import amz from '../../../libs/api-v3/amazonPayments';
var amzPayment = amazonPayments.connect({ let api = {};
environment: amazonPayments.Environment[isProd ? 'Production' : 'Sandbox'],
sellerId: nconf.get('AMAZON_PAYMENTS:SELLER_ID'),
mwsAccessKey: nconf.get('AMAZON_PAYMENTS:MWS_KEY'),
mwsSecretKey: nconf.get('AMAZON_PAYMENTS:MWS_SECRET'),
clientId: nconf.get('AMAZON_PAYMENTS:CLIENT_ID')
});
exports.verifyAccessToken = function(req, res, next){ /**
if(!req.body || !req.body['access_token']){ * @api {post} /api/v3/payments/amazon/verifyAccessToken verify access token
return res.status(400).json({err: 'Access token not supplied.'}); * @apiVersion 3.0.0
} * @apiName AmazonVerifyAccessToken
* @apiGroup Payments
amzPayment.api.getTokenInfo(req.body['access_token'], function(err, tokenInfo){ * @apiParam {string} access_token the access token
if(err) return res.status(400).json({err:err}); * @apiSuccess {} empty
**/
res.sendStatus(200); api.verifyAccessToken = {
}); method: 'POST',
url: '/payments/amazon/verifyAccessToken',
async handler (req, res) {
await amz.getTokenInfo(req.body.access_token)
.then(() => {
res.respond(200, {});
}).catch( (error) => {
throw new BadRequest(error.body.error_description);
});
},
}; };
exports.createOrderReferenceId = function(req, res, next){ /**
if(!req.body || !req.body.billingAgreementId){ * @api {post} /api/v3/payments/amazon/createOrderReferenceId create order reference id
return res.status(400).json({err: 'Billing Agreement Id not supplied.'}); * @apiVersion 3.0.0
} * @apiName AmazonCreateOrderReferenceId
* @apiGroup Payments
amzPayment.offAmazonPayments.createOrderReferenceForId({ * @apiParam {string} billingAgreementId billing agreement id
Id: req.body.billingAgreementId, * @apiSuccess {object} object containing { orderReferenceId }
IdType: 'BillingAgreement', **/
ConfirmNow: false api.createOrderReferenceId = {
}, function(err, response){ method: 'POST',
if(err) return next(err); url: '/payments/amazon/createOrderReferenceId',
if(!response.OrderReferenceDetails || !response.OrderReferenceDetails.AmazonOrderReferenceId){ async handler (req, res) {
return next(new Error('Missing attributes in Amazon response.')); if (!req.body.billingAgreementId) {
throw new BadRequest(res.t('missingBillingAgreementId'));
} }
res.json({ let response = await amz.createOrderReferenceId({
orderReferenceId: response.OrderReferenceDetails.AmazonOrderReferenceId Id: req.body.billingAgreementId,
IdType: 'BillingAgreement',
ConfirmNow: false,
}).then(() => {
res.respond(200, {
orderReferenceId: response.OrderReferenceDetails.AmazonOrderReferenceId,
});
}).catch(errStr => {
throw new BadRequest(res.t(errStr));
}); });
}); },
}; };
exports.checkout = function(req, res, next){ /*
if(!req.body || !req.body.orderReferenceId){ api.checkout = function checkout (req, res, next) {
if (!req.body || !req.body.orderReferenceId) {
return res.status(400).json({err: 'Billing Agreement Id not supplied.'}); return res.status(400).json({err: 'Billing Agreement Id not supplied.'});
} }
var gift = req.body.gift; let gift = req.body.gift;
var user = res.locals.user; let user = res.locals.user;
var orderReferenceId = req.body.orderReferenceId; let orderReferenceId = req.body.orderReferenceId;
var amount = 5; let amount = 5;
if(gift){ if (gift) {
if(gift.type === 'gems'){ if (gift.type === 'gems') {
amount = gift.gems.amount/4; amount = gift.gems.amount / 4;
}else if(gift.type === 'subscription'){ } else if (gift.type === 'subscription') {
amount = shared.content.subscriptionBlocks[gift.subscription.key].price; amount = shared.content.subscriptionBlocks[gift.subscription.key].price;
} }
} }
async.series({ async.series({
setOrderReferenceDetails: function(cb){ setOrderReferenceDetails (cb) {
amzPayment.offAmazonPayments.setOrderReferenceDetails({ amzPayment.offAmazonPayments.setOrderReferenceDetails({
AmazonOrderReferenceId: orderReferenceId, AmazonOrderReferenceId: orderReferenceId,
OrderReferenceAttributes: { OrderReferenceAttributes: {
OrderTotal: { OrderTotal: {
CurrencyCode: 'USD', CurrencyCode: 'USD',
Amount: amount Amount: amount,
}, },
SellerNote: 'HabitRPG Payment', SellerNote: 'HabitRPG Payment',
SellerOrderAttributes: { SellerOrderAttributes: {
SellerOrderId: shared.uuid(), SellerOrderId: shared.uuid(),
StoreName: 'HabitRPG' StoreName: 'HabitRPG',
} },
} },
}, cb); }, cb);
}, },
confirmOrderReference: function(cb){ confirmOrderReference (cb) {
amzPayment.offAmazonPayments.confirmOrderReference({ amzPayment.offAmazonPayments.confirmOrderReference({
AmazonOrderReferenceId: orderReferenceId AmazonOrderReferenceId: orderReferenceId,
}, cb); }, cb);
}, },
authorize: function(cb){ authorize (cb) {
amzPayment.offAmazonPayments.authorize({ amzPayment.offAmazonPayments.authorize({
AmazonOrderReferenceId: orderReferenceId, AmazonOrderReferenceId: orderReferenceId,
AuthorizationReferenceId: shared.uuid().substring(0, 32), AuthorizationReferenceId: shared.uuid().substring(0, 32),
AuthorizationAmount: { AuthorizationAmount: {
CurrencyCode: 'USD', CurrencyCode: 'USD',
Amount: amount Amount: amount,
}, },
SellerAuthorizationNote: 'HabitRPG Payment', SellerAuthorizationNote: 'HabitRPG Payment',
TransactionTimeout: 0, TransactionTimeout: 0,
CaptureNow: true CaptureNow: true,
}, function(err, res){ }, function checkAuthorizationStatus (err) {
if(err) return cb(err); if (err) return cb(err);
if(res.AuthorizationDetails.AuthorizationStatus.State === 'Declined'){ if (res.AuthorizationDetails.AuthorizationStatus.State === 'Declined') {
return cb(new Error('The payment was not successfull.')); return cb(new Error('The payment was not successfull.'));
} }
@@ -114,64 +130,65 @@ exports.checkout = function(req, res, next){
}); });
}, },
closeOrderReference: function(cb){ closeOrderReference (cb) {
amzPayment.offAmazonPayments.closeOrderReference({ amzPayment.offAmazonPayments.closeOrderReference({
AmazonOrderReferenceId: orderReferenceId AmazonOrderReferenceId: orderReferenceId,
}, cb); }, cb);
}, },
executePayment: function(cb){ executePayment (cb) {
async.waterfall([ async.waterfall([
function(cb2){ User.findById(gift ? gift.uuid : undefined, cb2); }, function findUser (cb2) {
function(member, cb2){ User.findById(gift ? gift.uuid : undefined, cb2);
var data = {user:user, paymentMethod:'Amazon Payments'}; },
var method = 'buyGems'; function executeAmazonPayment (member, cb2) {
let data = {user, paymentMethod: 'Amazon Payments'};
let method = 'buyGems';
if (gift){ if (gift) {
if (gift.type == 'subscription') method = 'createSubscription'; if (gift.type === 'subscription') method = 'createSubscription';
gift.member = member; gift.member = member;
data.gift = gift; data.gift = gift;
data.paymentMethod = 'Gift'; data.paymentMethod = 'Gift';
} }
payments[method](data, cb2); payments[method](data, cb2);
} },
], cb); ], cb);
} },
}, function(err, results){ }, function result (err) {
if(err) return next(err); if (err) return next(err);
res.sendStatus(200); res.sendStatus(200);
}); });
}; };
exports.subscribe = function(req, res, next){ api.subscribe = function subscribe (req, res, next) {
if(!req.body || !req.body['billingAgreementId']){ if (!req.body || !req.body.billingAgreementId) {
return res.status(400).json({err: 'Billing Agreement Id not supplied.'}); return res.status(400).json({err: 'Billing Agreement Id not supplied.'});
} }
var billingAgreementId = req.body.billingAgreementId; let billingAgreementId = req.body.billingAgreementId;
var sub = req.body.subscription ? shared.content.subscriptionBlocks[req.body.subscription] : false; let sub = req.body.subscription ? shared.content.subscriptionBlocks[req.body.subscription] : false;
var coupon = req.body.coupon; let coupon = req.body.coupon;
var user = res.locals.user; let user = res.locals.user;
if(!sub){ if (!sub) {
return res.status(400).json({err: 'Subscription plan not found.'}); return res.status(400).json({err: 'Subscription plan not found.'});
} }
async.series({ async.series({
applyDiscount: function(cb){ applyDiscount (cb) {
if (!sub.discount) return cb(); if (!sub.discount) return cb();
if (!coupon) return cb(new Error('Please provide a coupon code for this plan.')); 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){ mongoose.model('Coupon').findOne({_id: cc.validate(coupon), event: sub.key}, function couponResult (err) {
if(err) return cb(err); if (err) return cb(err);
if(!coupon) return cb(new Error('Coupon code not found.')); if (!coupon) return cb(new Error('Coupon code not found.'));
cb(); cb();
}); });
}, },
setBillingAgreementDetails: function(cb){ setBillingAgreementDetails (cb) {
amzPayment.offAmazonPayments.setBillingAgreementDetails({ amzPayment.offAmazonPayments.setBillingAgreementDetails({
AmazonBillingAgreementId: billingAgreementId, AmazonBillingAgreementId: billingAgreementId,
BillingAgreementAttributes: { BillingAgreementAttributes: {
@@ -179,25 +196,25 @@ exports.subscribe = function(req, res, next){
SellerBillingAgreementAttributes: { SellerBillingAgreementAttributes: {
SellerBillingAgreementId: shared.uuid(), SellerBillingAgreementId: shared.uuid(),
StoreName: 'HabitRPG', StoreName: 'HabitRPG',
CustomInformation: 'HabitRPG Subscription' CustomInformation: 'HabitRPG Subscription',
} },
} },
}, cb); }, cb);
}, },
confirmBillingAgreement: function(cb){ confirmBillingAgreement (cb) {
amzPayment.offAmazonPayments.confirmBillingAgreement({ amzPayment.offAmazonPayments.confirmBillingAgreement({
AmazonBillingAgreementId: billingAgreementId AmazonBillingAgreementId: billingAgreementId,
}, cb); }, cb);
}, },
authorizeOnBillingAgreeement: function(cb){ authorizeOnBillingAgreement (cb) {
amzPayment.offAmazonPayments.authorizeOnBillingAgreement({ amzPayment.offAmazonPayments.authorizeOnBillingAgreement({
AmazonBillingAgreementId: billingAgreementId, AmazonBillingAgreementId: billingAgreementId,
AuthorizationReferenceId: shared.uuid().substring(0, 32), AuthorizationReferenceId: shared.uuid().substring(0, 32),
AuthorizationAmount: { AuthorizationAmount: {
CurrencyCode: 'USD', CurrencyCode: 'USD',
Amount: sub.price Amount: sub.price,
}, },
SellerAuthorizationNote: 'HabitRPG Subscription Payment', SellerAuthorizationNote: 'HabitRPG Subscription Payment',
TransactionTimeout: 0, TransactionTimeout: 0,
@@ -205,67 +222,70 @@ exports.subscribe = function(req, res, next){
SellerNote: 'HabitRPG Subscription Payment', SellerNote: 'HabitRPG Subscription Payment',
SellerOrderAttributes: { SellerOrderAttributes: {
SellerOrderId: shared.uuid(), SellerOrderId: shared.uuid(),
StoreName: 'HabitRPG' StoreName: 'HabitRPG',
} },
}, function(err, res){ }, function billingAgreementResult (err) {
if(err) return cb(err); if (err) return cb(err);
if(res.AuthorizationDetails.AuthorizationStatus.State === 'Declined'){ if (res.AuthorizationDetails.AuthorizationStatus.State === 'Declined') {
return cb(new Error('The payment was not successfull.')); return cb(new Error('The payment was not successful.'));
} }
return cb(); return cb();
}); });
}, },
createSubscription: function(cb){ createSubscription (cb) {
payments.createSubscription({ payments.createSubscription({
user: user, user,
customerId: billingAgreementId, customerId: billingAgreementId,
paymentMethod: 'Amazon Payments', paymentMethod: 'Amazon Payments',
sub: sub sub,
}, cb); }, cb);
} },
}, function(err, results){ }, function subscribeResult (err) {
if(err) return next(err); if (err) return next(err);
res.sendStatus(200); res.sendStatus(200);
}); });
}; };
exports.subscribeCancel = function(req, res, next){ api.subscribeCancel = function subscribeCancel (req, res, next) {
var user = res.locals.user; let user = res.locals.user;
if (!user.purchased.plan.customerId) if (!user.purchased.plan.customerId)
return res.status(401).json({err: 'User does not have a plan subscription'}); return res.status(401).json({err: 'User does not have a plan subscription'});
var billingAgreementId = user.purchased.plan.customerId; let billingAgreementId = user.purchased.plan.customerId;
async.series({ async.series({
closeBillingAgreement: function(cb){ closeBillingAgreement (cb) {
amzPayment.offAmazonPayments.closeBillingAgreement({ amzPayment.offAmazonPayments.closeBillingAgreement({
AmazonBillingAgreementId: billingAgreementId AmazonBillingAgreementId: billingAgreementId,
}, cb); }, cb);
}, },
cancelSubscription: function(cb){ cancelSubscription (cb) {
var data = { let data = {
user: user, user,
// Date of next bill // Date of next bill
nextBill: moment(user.purchased.plan.lastBillingDate).add({days: 30}), nextBill: moment(user.purchased.plan.lastBillingDate).add({days: 30}),
paymentMethod: 'Amazon Payments' paymentMethod: 'Amazon Payments',
}; };
payments.cancelSubscription(data, cb); payments.cancelSubscription(data, cb);
} },
}, function(err, results){ }, function subscribeCancelResult (err) {
if (err) return next(err); // don't json this, let toString() handle errors if (err) return next(err); // don't json this, let toString() handle errors
if(req.query.noRedirect){ if (req.query.noRedirect) {
res.sendStatus(200); res.sendStatus(200);
}else{ } else {
res.redirect('/'); res.redirect('/');
} }
user = null; user = null;
}); });
}; };
*/
module.exports = api;

View File

@@ -1,67 +1,66 @@
var iap = require('in-app-purchase'); import iap from 'in-app-purchase';
var async = require('async'); import whatThis from 'in-app-purchase';
var payments = require('./index'); import payments from './index';
var nconf = require('nconf'); import nconf from 'nconf';
var inAppPurchase = require('in-app-purchase'); iap.config({
inAppPurchase.config({
// this is the path to the directory containing iap-sanbox/iap-live files // this is the path to the directory containing iap-sanbox/iap-live files
googlePublicKeyPath: nconf.get('IAP_GOOGLE_KEYDIR') googlePublicKeyPath: nconf.get('IAP_GOOGLE_KEYDIR'),
}); });
// Validation ERROR Codes // Validation ERROR Codes
var INVALID_PAYLOAD = 6778001; const INVALID_PAYLOAD = 6778001;
var CONNECTION_FAILED = 6778002; /* const CONNECTION_FAILED = 6778002;
var PURCHASE_EXPIRED = 6778003; const PURCHASE_EXPIRED = 6778003; */ // These variables were never used??
exports.androidVerify = function(req, res, next) { let api = {};
var iapBody = req.body;
var user = res.locals.user;
iap.setup(function (error) { /*
api.androidVerify = function androidVerify (req, res) {
let iapBody = req.body;
let user = res.locals.user;
iap.setup(function googleSetupResult (error) {
if (error) { if (error) {
var resObj = { let resObj = {
ok: false, ok: false,
data: 'IAP Error' data: 'IAP Error',
}; };
return res.json(resObj); return res.json(resObj);
} }
/* // google receipt must be provided as an object
google receipt must be provided as an object // {
{ // "data": "{stringified data object}",
"data": "{stringified data object}", // "signature": "signature from google"
"signature": "signature from google" // }
} let testObj = {
*/
var testObj = {
data: iapBody.transaction.receipt, data: iapBody.transaction.receipt,
signature: iapBody.transaction.signature signature: iapBody.transaction.signature,
}; };
// iap is ready // iap is ready
iap.validate(iap.GOOGLE, testObj, function (err, googleRes) { iap.validate(iap.GOOGLE, testObj, function googleValidateResult (err, googleRes) {
if (err) { if (err) {
var resObj = { let resObj = {
ok: false, ok: false,
data: { data: {
code: INVALID_PAYLOAD, code: INVALID_PAYLOAD,
message: err.toString() message: err.toString(),
} },
}; };
return res.json(resObj); return res.json(resObj);
} }
if (iap.isValidated(googleRes)) { if (iap.isValidated(googleRes)) {
var resObj = { let resObj = {
ok: true, ok: true,
data: googleRes data: googleRes,
}; };
payments.buyGems({user:user, paymentMethod:'IAP GooglePlay', amount: 5.25}); payments.buyGems({user, paymentMethod: 'IAP GooglePlay', amount: 5.25});
return res.json(resObj); return res.json(resObj);
} }
@@ -69,87 +68,89 @@ exports.androidVerify = function(req, res, next) {
}); });
}; };
exports.iosVerify = function(req, res, next) { exports.iosVerify = function iosVerify (req, res) {
var iapBody = req.body; let iapBody = req.body;
var user = res.locals.user; let user = res.locals.user;
iap.setup(function (error) { iap.setup(function iosSetupResult (error) {
if (error) { if (error) {
var resObj = { let resObj = {
ok: false, ok: false,
data: 'IAP Error' data: 'IAP Error',
}; };
return res.json(resObj); return res.json(resObj);
} }
//iap is ready // iap is ready
iap.validate(iap.APPLE, iapBody.transaction.receipt, function (err, appleRes) { iap.validate(iap.APPLE, iapBody.transaction.receipt, function iosValidateResult (err, appleRes) {
if (err) { if (err) {
var resObj = { let resObj = {
ok: false, ok: false,
data: { data: {
code: INVALID_PAYLOAD, code: INVALID_PAYLOAD,
message: err.toString() message: err.toString(),
} },
}; };
return res.json(resObj); return res.json(resObj);
} }
if (iap.isValidated(appleRes)) { if (iap.isValidated(appleRes)) {
var purchaseDataList = iap.getPurchaseData(appleRes); let purchaseDataList = iap.getPurchaseData(appleRes);
if (purchaseDataList.length > 0) { if (purchaseDataList.length > 0) {
var correctReceipt = true; let correctReceipt = true;
for (var index in purchaseDataList) { for (let index of purchaseDataList) {
switch (purchaseDataList[index].productId) { switch (purchaseDataList[index].productId) {
case 'com.habitrpg.ios.Habitica.4gems': case 'com.habitrpg.ios.Habitica.4gems':
payments.buyGems({user:user, paymentMethod:'IAP AppleStore', amount: 1}); payments.buyGems({user, paymentMethod: 'IAP AppleStore', amount: 1});
break; break;
case 'com.habitrpg.ios.Habitica.8gems': case 'com.habitrpg.ios.Habitica.8gems':
payments.buyGems({user:user, paymentMethod:'IAP AppleStore', amount: 2}); payments.buyGems({user, paymentMethod: 'IAP AppleStore', amount: 2});
break; break;
case 'com.habitrpg.ios.Habitica.20gems': case 'com.habitrpg.ios.Habitica.20gems':
case 'com.habitrpg.ios.Habitica.21gems': case 'com.habitrpg.ios.Habitica.21gems':
payments.buyGems({user:user, paymentMethod:'IAP AppleStore', amount: 5.25}); payments.buyGems({user, paymentMethod: 'IAP AppleStore', amount: 5.25});
break; break;
case 'com.habitrpg.ios.Habitica.42gems': case 'com.habitrpg.ios.Habitica.42gems':
payments.buyGems({user:user, paymentMethod:'IAP AppleStore', amount: 10.5}); payments.buyGems({user, paymentMethod: 'IAP AppleStore', amount: 10.5});
break; break;
default: default:
correctReceipt = false; correctReceipt = false;
} }
} }
if (correctReceipt) { if (correctReceipt) {
var resObj = { let resObj = {
ok: true, ok: true,
data: appleRes data: appleRes,
}; };
// yay good! // yay good!
return res.json(resObj); return res.json(resObj);
} }
} }
//wrong receipt content // wrong receipt content
var resObj = { let resObj = {
ok: false, ok: false,
data: { data: {
code: INVALID_PAYLOAD, code: INVALID_PAYLOAD,
message: 'Incorrect receipt content' message: 'Incorrect receipt content',
} },
}; };
return res.json(resObj); return res.json(resObj);
} }
//invalid receipt // invalid receipt
var resObj = { let resObj = {
ok: false, ok: false,
data: { data: {
code: INVALID_PAYLOAD, code: INVALID_PAYLOAD,
message: 'Invalid receipt' message: 'Invalid receipt',
} },
}; };
return res.json(resObj); return res.json(resObj);
}); });
}); });
}; };
*/
module.exports = api;

View File

@@ -1,207 +1,233 @@
var _ = require('lodash'); import _ from 'lodash' ;
var shared = require('../../../../common'); import analytics from '../../../libs/api-v3/analyticsService';
var nconf = require('nconf'); import async from 'async';
var utils = require('./../../libs/api-v2/utils'); import cc from 'coupon-code';
var moment = require('moment'); import {
var isProduction = nconf.get("NODE_ENV") === "production"; getUserInfo,
var stripe = require('./stripe'); sendTxn as txnEmail,
var paypal = require('./paypal'); } from '../../../libs/api-v3/email';
var amazon = require('./amazon'); import members from '../members';
var members = require('../api-v2/members') import moment from 'moment';
var async = require('async'); import mongoose from 'mongoose';
var iap = require('./iap'); import nconf from 'nconf';
var mongoose= require('mongoose'); import pushNotify from '../../../libs/api-v3/pushNotifications';
var cc = require('coupon-code'); import shared from '../../../../../common' ;
var pushNotify = require('./../api-v2/pushNotifications');
function revealMysteryItems(user) { import amazon from './amazon';
_.each(shared.content.gear.flat, function(item) { import iap from './iap';
import paypal from './paypal';
import stripe from './stripe';
const IS_PROD = nconf.get('NODE_ENV') === 'production';
let api = {};
function revealMysteryItems (user) {
_.each(shared.content.gear.flat, function findMysteryItems (item) {
if ( if (
item.klass === 'mystery' && item.klass === 'mystery' &&
moment().isAfter(shared.content.mystery[item.mystery].start) && moment().isAfter(shared.content.mystery[item.mystery].start) &&
moment().isBefore(shared.content.mystery[item.mystery].end) && moment().isBefore(shared.content.mystery[item.mystery].end) &&
!user.items.gear.owned[item.key] && !user.items.gear.owned[item.key] &&
!~user.purchased.plan.mysteryItems.indexOf(item.key) user.purchased.plan.mysteryItems.indexOf(item.key) !== -1
) { ) {
user.purchased.plan.mysteryItems.push(item.key); user.purchased.plan.mysteryItems.push(item.key);
} }
}); });
} }
exports.createSubscription = function(data, cb) { api.createSubscription = function createSubscription (data, cb) {
var recipient = data.gift ? data.gift.member : data.user; let recipient = data.gift ? data.gift.member : data.user;
//if (!recipient.purchased.plan) recipient.purchased.plan = {}; // TODO double-check, this should never be the case let plan = recipient.purchased.plan;
var p = recipient.purchased.plan; let block = shared.content.subscriptionBlocks[data.gift ? data.gift.subscription.key : data.sub.key];
var block = shared.content.subscriptionBlocks[data.gift ? data.gift.subscription.key : data.sub.key]; let months = Number(block.months);
var months = +block.months;
if (data.gift) { if (data.gift) {
if (p.customerId && !p.dateTerminated) { // User has active plan if (plan.customerId && !plan.dateTerminated) { // User has active plan
p.extraMonths += months; plan.extraMonths += months;
} else { } else {
p.dateTerminated = moment(p.dateTerminated).add({months: months}).toDate(); plan.dateTerminated = moment(plan.dateTerminated).add({months}).toDate();
if (!p.dateUpdated) p.dateUpdated = new Date(); if (!plan.dateUpdated) plan.dateUpdated = new Date();
} }
if (!p.customerId) p.customerId = 'Gift'; // don't override existing customer, but all sub need a customerId if (!plan.customerId) plan.customerId = 'Gift'; // don't override existing customer, but all sub need a customerId
} else { } else {
_(p).merge({ // override with these values _(plan).merge({ // override with these values
planId: block.key, planId: block.key,
customerId: data.customerId, customerId: data.customerId,
dateUpdated: new Date(), dateUpdated: new Date(),
gemsBought: 0, gemsBought: 0,
paymentMethod: data.paymentMethod, paymentMethod: data.paymentMethod,
extraMonths: +p.extraMonths extraMonths: Number(plan.extraMonths) +
+ +(p.dateTerminated ? moment(p.dateTerminated).diff(new Date(),'months',true) : 0), Number(plan.dateTerminated ? moment(plan.dateTerminated).diff(new Date(), 'months', true) : 0),
dateTerminated: null, dateTerminated: null,
// Specify a lastBillingDate just for Amazon Payments // Specify a lastBillingDate just for Amazon Payments
// Resetted every time the subscription restarts // Resetted every time the subscription restarts
lastBillingDate: data.paymentMethod === 'Amazon Payments' ? new Date() : undefined lastBillingDate: data.paymentMethod === 'Amazon Payments' ? new Date() : undefined,
}).defaults({ // allow non-override if a plan was previously used }).defaults({ // allow non-override if a plan was previously used
dateCreated: new Date(), dateCreated: new Date(),
mysteryItems: [] mysteryItems: [],
}).value(); }).value();
} }
// Block sub perks // Block sub perks
var perks = Math.floor(months/3); let perks = Math.floor(months / 3);
if (perks) { if (perks) {
p.consecutive.offset += months; plan.consecutive.offset += months;
p.consecutive.gemCapExtra += perks*5; plan.consecutive.gemCapExtra += perks * 5;
if (p.consecutive.gemCapExtra > 25) p.consecutive.gemCapExtra = 25; if (plan.consecutive.gemCapExtra > 25) plan.consecutive.gemCapExtra = 25;
p.consecutive.trinkets += perks; plan.consecutive.trinkets += perks;
} }
revealMysteryItems(recipient); revealMysteryItems(recipient);
if(isProduction) { if (IS_PROD) {
if (!data.gift) utils.txnEmail(data.user, 'subscription-begins'); if (!data.gift) txnEmail(data.user, 'subscription-begins');
var analyticsData = { let analyticsData = {
uuid: data.user._id, uuid: data.user._id,
itemPurchased: 'Subscription', itemPurchased: 'Subscription',
sku: data.paymentMethod.toLowerCase() + '-subscription', sku: `${data.paymentMethod.toLowerCase()}-subscription`,
purchaseType: 'subscribe', purchaseType: 'subscribe',
paymentMethod: data.paymentMethod, paymentMethod: data.paymentMethod,
quantity: 1, quantity: 1,
gift: !!data.gift, // coerced into a boolean gift: Boolean(data.gift),
purchaseValue: block.price purchaseValue: block.price,
} };
utils.analytics.trackPurchase(analyticsData); analytics.trackPurchase(analyticsData);
} }
data.user.purchased.txnCount++; data.user.purchased.txnCount++;
if (data.gift){ if (data.gift) {
members.sendMessage(data.user, data.gift.member, data.gift); members.sendMessage(data.user, data.gift.member, data.gift);
var byUserName = utils.getUserInfo(data.user, ['name']).name; let byUserName = getUserInfo(data.user, ['name']).name;
if(data.gift.member.preferences.emailNotifications.giftedSubscription !== false){ if (data.gift.member.preferences.emailNotifications.giftedSubscription !== false) {
utils.txnEmail(data.gift.member, 'gifted-subscription', [ txnEmail(data.gift.member, 'gifted-subscription', [
{name: 'GIFTER', content: byUserName}, {name: 'GIFTER', content: byUserName},
{name: 'X_MONTHS_SUBSCRIPTION', content: months} {name: 'X_MONTHS_SUBSCRIPTION', content: months},
]); ]);
} }
if (data.gift.member._id != data.user._id) { // Only send push notifications if sending to a user other than yourself if (data.gift.member._id !== data.user._id) { // Only send push notifications if sending to a user other than yourself
pushNotify.sendNotify(data.gift.member, shared.i18n.t('giftedSubscription'), months + " months - by "+ byUserName); pushNotify.sendNotify(data.gift.member, shared.i18n.t('giftedSubscription'), `${months} months - by ${byUserName}`);
} }
} }
async.parallel([ async.parallel([
function(cb2){data.user.save(cb2)}, function saveGiftingUserData (cb2) {
function(cb2){data.gift ? data.gift.member.save(cb2) : cb2(null);} data.user.save(cb2);
},
function saveRecipientUserData (cb2) {
if (data.gift) {
data.gift.member.save(cb2);
} else {
cb2(null);
}
},
], cb); ], cb);
} };
/** /**
* Sets their subscription to be cancelled later * Sets their subscription to be cancelled later
*/ */
exports.cancelSubscription = function(data, cb) { api.cancelSubscription = function cancelSubscription (data, cb) {
var p = data.user.purchased.plan, let plan = data.user.purchased.plan;
now = moment(), let now = moment();
remaining = data.nextBill ? moment(data.nextBill).diff(new Date, 'days') : 30; let remaining = data.nextBill ? moment(data.nextBill).diff(new Date(), 'days') : 30;
p.dateTerminated = plan.dateTerminated =
moment( now.format('MM') + '/' + moment(p.dateUpdated).format('DD') + '/' + now.format('YYYY') ) moment(`${now.format('MM')}/${moment(plan.dateUpdated).format('DD')}/${now.format('YYYY')}`)
.add({days: remaining}) // end their subscription 1mo from their last payment .add({days: remaining}) // end their subscription 1mo from their last payment
.add({months: Math.ceil(p.extraMonths)})// plus any extra time (carry-over, gifted subscription, etc) they have. TODO: moment can't add months in fractions... .add({months: Math.ceil(plan.extraMonths)})// plus any extra time (carry-over, gifted subscription, etc) they have. FIXME: moment can't add months in fractions...
.toDate(); .toDate();
p.extraMonths = 0; // clear extra time. If they subscribe again, it'll be recalculated from p.dateTerminated plan.extraMonths = 0; // clear extra time. If they subscribe again, it'll be recalculated from p.dateTerminated
data.user.save(cb); data.user.save(cb);
utils.txnEmail(data.user, 'cancel-subscription'); txnEmail(data.user, 'cancel-subscription');
var analyticsData = { let analyticsData = {
uuid: data.user._id, uuid: data.user._id,
gaCategory: 'commerce', gaCategory: 'commerce',
gaLabel: data.paymentMethod, gaLabel: data.paymentMethod,
paymentMethod: data.paymentMethod paymentMethod: data.paymentMethod,
} };
utils.analytics.track('unsubscribe', analyticsData); analytics.track('unsubscribe', analyticsData);
} };
exports.buyGems = function(data, cb) { api.buyGems = function buyGems (data, cb) {
var amt = data.amount || 5; let amt = data.amount || 5;
amt = data.gift ? data.gift.gems.amount/4 : amt; amt = data.gift ? data.gift.gems.amount / 4 : amt;
(data.gift ? data.gift.member : data.user).balance += amt; (data.gift ? data.gift.member : data.user).balance += amt;
data.user.purchased.txnCount++; data.user.purchased.txnCount++;
if(isProduction) { if (IS_PROD) {
if (!data.gift) utils.txnEmail(data.user, 'donation'); if (!data.gift) txnEmail(data.user, 'donation');
var analyticsData = { let analyticsData = {
uuid: data.user._id, uuid: data.user._id,
itemPurchased: 'Gems', itemPurchased: 'Gems',
sku: data.paymentMethod.toLowerCase() + '-checkout', sku: `${data.paymentMethod.toLowerCase()}-checkout`,
purchaseType: 'checkout', purchaseType: 'checkout',
paymentMethod: data.paymentMethod, paymentMethod: data.paymentMethod,
quantity: 1, quantity: 1,
gift: !!data.gift, // coerced into a boolean gift: Boolean(data.gift),
purchaseValue: amt purchaseValue: amt,
} };
utils.analytics.trackPurchase(analyticsData); analytics.trackPurchase(analyticsData);
} }
if (data.gift){ if (data.gift) {
var byUsername = utils.getUserInfo(data.user, ['name']).name; let byUsername = getUserInfo(data.user, ['name']).name;
var gemAmount = data.gift.gems.amount || 20; let gemAmount = data.gift.gems.amount || 20;
members.sendMessage(data.user, data.gift.member, data.gift); members.sendMessage(data.user, data.gift.member, data.gift);
if(data.gift.member.preferences.emailNotifications.giftedGems !== false){ if (data.gift.member.preferences.emailNotifications.giftedGems !== false) {
utils.txnEmail(data.gift.member, 'gifted-gems', [ txnEmail(data.gift.member, 'gifted-gems', [
{name: 'GIFTER', content: byUsername}, {name: 'GIFTER', content: byUsername},
{name: 'X_GEMS_GIFTED', content: gemAmount} {name: 'X_GEMS_GIFTED', content: gemAmount},
]); ]);
} }
if (data.gift.member._id != data.user._id) { // Only send push notifications if sending to a user other than yourself if (data.gift.member._id !== data.user._id) { // Only send push notifications if sending to a user other than yourself
pushNotify.sendNotify(data.gift.member, shared.i18n.t('giftedGems'), gemAmount + ' Gems - by '+byUsername); pushNotify.sendNotify(data.gift.member, shared.i18n.t('giftedGems'), `${gemAmount} Gems - by ${byUsername}`);
} }
} }
async.parallel([ async.parallel([
function(cb2){data.user.save(cb2)}, function saveGiftingUserData (cb2) {
function(cb2){data.gift ? data.gift.member.save(cb2) : cb2(null);} data.user.save(cb2);
},
function saveRecipientUserData (cb2) {
if (data.gift) {
data.gift.member.save(cb2);
} else {
cb2(null);
}
},
], cb); ], cb);
} };
exports.validCoupon = function(req, res, next){ api.validCoupon = function validCoupon (req, res, next) {
mongoose.model('Coupon').findOne({_id:cc.validate(req.params.code), event:'google_6mo'}, function(err, coupon){ mongoose.model('Coupon').findOne({_id: cc.validate(req.params.code), event: 'google_6mo'}, function couponErrorCheck (err, coupon) {
if (err) return next(err); if (err) return next(err);
if (!coupon) return res.status(401).json({err:"Invalid coupon code"}); if (!coupon) return res.status(401).json({err: 'Invalid coupon code'});
return res.sendStatus(200); return res.sendStatus(200);
}); });
} };
exports.stripeCheckout = stripe.checkout; api.stripeCheckout = stripe.checkout;
exports.stripeSubscribeCancel = stripe.subscribeCancel; api.stripeSubscribeCancel = stripe.subscribeCancel;
exports.stripeSubscribeEdit = stripe.subscribeEdit; api.stripeSubscribeEdit = stripe.subscribeEdit;
exports.paypalSubscribe = paypal.createBillingAgreement; api.paypalSubscribe = paypal.createBillingAgreement;
exports.paypalSubscribeSuccess = paypal.executeBillingAgreement; api.paypalSubscribeSuccess = paypal.executeBillingAgreement;
exports.paypalSubscribeCancel = paypal.cancelSubscription; api.paypalSubscribeCancel = paypal.cancelSubscription;
exports.paypalCheckout = paypal.createPayment; api.paypalCheckout = paypal.createPayment;
exports.paypalCheckoutSuccess = paypal.executePayment; api.paypalCheckoutSuccess = paypal.executePayment;
exports.paypalIPN = paypal.ipn; api.paypalIPN = paypal.ipn;
exports.amazonVerifyAccessToken = amazon.verifyAccessToken; api.amazonVerifyAccessToken = amazon.verifyAccessToken;
exports.amazonCreateOrderReferenceId = amazon.createOrderReferenceId; api.amazonCreateOrderReferenceId = amazon.createOrderReferenceId;
exports.amazonCheckout = amazon.checkout; api.amazonCheckout = amazon.checkout;
exports.amazonSubscribe = amazon.subscribe; api.amazonSubscribe = amazon.subscribe;
exports.amazonSubscribeCancel = amazon.subscribeCancel; api.amazonSubscribeCancel = amazon.subscribeCancel;
exports.iapAndroidVerify = iap.androidVerify; api.iapAndroidVerify = iap.androidVerify;
exports.iapIosVerify = iap.iosVerify; api.iapIosVerify = iap.iosVerify;
// module.exports = api;
module.exports = {}; // @TODO HEREHERE

View File

@@ -5,10 +5,10 @@ var _ = require('lodash');
var url = require('url'); var url = require('url');
var User = require('mongoose').model('User'); var User = require('mongoose').model('User');
var payments = require('./index'); var payments = require('./index');
var logger = require('../../libs/api-v2/logging'); var logger = require('../../../libs/api-v2/logging');
var ipn = require('paypal-ipn'); var ipn = require('paypal-ipn');
var paypal = require('paypal-rest-sdk'); var paypal = require('paypal-rest-sdk');
var shared = require('../../../../common'); var shared = require('../../../../../common');
var mongoose = require('mongoose'); var mongoose = require('mongoose');
var cc = require('coupon-code'); var cc = require('coupon-code');
@@ -31,6 +31,7 @@ var parseErr = function(res, err){
return res.status(400).json({err:error}); return res.status(400).json({err:error});
} }
/*
exports.createBillingAgreement = function(req,res,next){ exports.createBillingAgreement = function(req,res,next){
var sub = shared.content.subscriptionBlocks[req.query.sub]; var sub = shared.content.subscriptionBlocks[req.query.sub];
async.waterfall([ async.waterfall([
@@ -190,11 +191,13 @@ exports.cancelSubscription = function(req, res, next){
user = null; user = null;
}); });
} }
*/
/** /**
* General IPN handler. We catch cancelled HabitRPG subscriptions for users who manually cancel their * General IPN handler. We catch cancelled HabitRPG subscriptions for users who manually cancel their
* recurring paypal payments in their paypal dashboard. Remove this when we can move to webhooks or some other solution * recurring paypal payments in their paypal dashboard. Remove this when we can move to webhooks or some other solution
*/ */
/*
exports.ipn = function(req, res, next) { exports.ipn = function(req, res, next) {
console.log('IPN Called'); console.log('IPN Called');
res.sendStatus(200); // Must respond to PayPal IPN request with an empty 200 first res.sendStatus(200); // Must respond to PayPal IPN request with an empty 200 first
@@ -213,4 +216,4 @@ exports.ipn = function(req, res, next) {
} }
}); });
}; };
*/

View File

@@ -1,123 +1,137 @@
var nconf = require('nconf'); import nconf from 'nconf';
var stripe = require('stripe')(nconf.get('STRIPE_API_KEY')); import stripeModule from 'stripe';
var async = require('async'); import async from 'async';
var payments = require('./index'); import payments from './index';
var User = require('mongoose').model('User'); import { model as User } from '../../../models/user';
var shared = require('../../../../common'); import shared from '../../../../../common';
var mongoose = require('mongoose'); import mongoose from 'mongoose';
var cc = require('coupon-code'); import cc from 'coupon-code';
const stripe = stripeModule(nconf.get('STRIPE_API_KEY'));
let api = {};
/* /*
Setup Stripe response when posting payment Setup Stripe response when posting payment
*/ */
exports.checkout = function(req, res, next) { /*
var token = req.body.id; api.checkout = function checkout (req, res) {
var user = res.locals.user; let token = req.body.id;
var gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; let user = res.locals.user;
var sub = req.query.sub ? shared.content.subscriptionBlocks[req.query.sub] : false; let gift = req.query.gift ? JSON.parse(req.query.gift) : undefined;
let sub = req.query.sub ? shared.content.subscriptionBlocks[req.query.sub] : false;
async.waterfall([ async.waterfall([
function(cb){ function stripeCharge (cb) {
if (sub) { if (sub) {
async.waterfall([ async.waterfall([
function(cb2){ function handleCoupon (cb2) {
if (!sub.discount) return cb2(null, null); if (!sub.discount) return cb2(null, null);
if (!req.query.coupon) return cb2('Please provide a coupon code for this plan.'); if (!req.query.coupon) return cb2('Please provide a coupon code for this plan.');
mongoose.model('Coupon').findOne({_id:cc.validate(req.query.coupon), event:sub.key}, cb2); mongoose.model('Coupon').findOne({_id: cc.validate(req.query.coupon), event: sub.key}, cb2);
}, },
function(coupon, cb2){ function createCustomer (coupon, cb2) {
if (sub.discount && !coupon) return cb2('Invalid coupon code.'); if (sub.discount && !coupon) return cb2('Invalid coupon code.');
var customer = { let customer = {
email: req.body.email, email: req.body.email,
metadata: {uuid: user._id}, metadata: {uuid: user._id},
card: token, card: token,
plan: sub.key plan: sub.key,
}; };
stripe.customers.create(customer, cb2); stripe.customers.create(customer, cb2);
} },
], cb); ], cb);
} else { } else {
let amount;
if (!gift) {
amount = '500';
} else if (gift.type === 'subscription') {
amount = `${shared.content.subscriptionBlocks[gift.subscription.key].price * 100}`;
} else {
amount = `${gift.gems.amount / 4 * 100}`;
}
stripe.charges.create({ stripe.charges.create({
amount: !gift ? '500' //"500" = $5 amount,
: gift.type=='subscription' ? ''+shared.content.subscriptionBlocks[gift.subscription.key].price*100
: ''+gift.gems.amount/4*100,
currency: 'usd', currency: 'usd',
card: token card: token,
}, cb); }, cb);
} }
}, },
function(response, cb) { function saveUserData (response, cb) {
if (sub) return payments.createSubscription({user:user, customerId:response.id, paymentMethod:'Stripe', sub:sub}, cb); if (sub) return payments.createSubscription({user, customerId: response.id, paymentMethod: 'Stripe', sub}, cb);
async.waterfall([ async.waterfall([
function(cb2){ User.findById(gift ? gift.uuid : undefined, cb2); }, function findUser (cb2) {
function(member, cb2){ User.findById(gift ? gift.uuid : undefined, cb2);
var data = {user:user, customerId:response.id, paymentMethod:'Stripe', gift:gift}; },
var method = 'buyGems'; function prepData (member, cb2) {
let data = {user, customerId: response.id, paymentMethod: 'Stripe', gift};
let method = 'buyGems';
if (gift) { if (gift) {
gift.member = member; gift.member = member;
if (gift.type=='subscription') method = 'createSubscription'; if (gift.type === 'subscription') method = 'createSubscription';
data.paymentMethod = 'Gift'; data.paymentMethod = 'Gift';
} }
payments[method](data, cb2); payments[method](data, cb2);
} },
], cb); ], cb);
} },
], function(err){ ], function handleResponse (err) {
if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors
res.sendStatus(200); res.sendStatus(200);
user = token = null; user = token = null;
}); });
}; };
exports.subscribeCancel = function(req, res, next) { api.subscribeCancel = function subscribeCancel (req, res) {
var user = res.locals.user; let user = res.locals.user;
if (!user.purchased.plan.customerId) if (!user.purchased.plan.customerId) {
return res.status(401).json({err: 'User does not have a plan subscription'}); return res.status(401).json({err: 'User does not have a plan subscription'});
}
async.auto({ async.auto({
get_cus: function(cb){ getCustomer: function getCustomer (cb) {
stripe.customers.retrieve(user.purchased.plan.customerId, cb); stripe.customers.retrieve(user.purchased.plan.customerId, cb);
}, },
del_cus: ['get_cus', function(cb, results){ deleteCustomer: ['getCustomer', function deleteCustomer (cb) {
stripe.customers.del(user.purchased.plan.customerId, cb); stripe.customers.del(user.purchased.plan.customerId, cb);
}], }],
cancel_sub: ['get_cus', function(cb, results) { cancelSubscription: ['getCustomer', function cancelSubscription (cb, results) {
var data = { let data = {
user: user, user,
nextBill: results.get_cus.subscription.current_period_end*1000, // timestamp is in seconds nextBill: results.get_cus.subscription.current_period_end * 1000, // timestamp is in seconds
paymentMethod: 'Stripe' paymentMethod: 'Stripe',
}; };
payments.cancelSubscription(data, cb); payments.cancelSubscription(data, cb);
}] }],
}, function(err, results){ }, function handleResponse (err) {
if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors
res.redirect('/'); res.redirect('/');
user = null; user = null;
}); });
}; };
exports.subscribeEdit = function(req, res, next) { api.subscribeEdit = function subscribeEdit (req, res) {
var token = req.body.id; let token = req.body.id;
var user = res.locals.user; let user = res.locals.user;
var user_id = user.purchased.plan.customerId; let userId = user.purchased.plan.customerId;
var sub_id; let subscriptionId;
async.waterfall([ async.waterfall([
function(cb){ function listSubscriptions (cb) {
stripe.customers.listSubscriptions(user_id, cb); stripe.customers.listSubscriptions(userId, cb);
}, },
function(response, cb) { function updateSubscription (response, cb) {
sub_id = response.data[0].id; subscriptionId = response.data[0].id;
console.warn(sub_id); stripe.customers.updateSubscription(userId, subscriptionId, { card: token }, cb);
console.warn([user_id, sub_id, { card: token }]);
stripe.customers.updateSubscription(user_id, sub_id, { card: token }, cb);
}, },
function(response, cb) { function saveUser (response, cb) {
user.save(cb); user.save(cb);
} },
], function(err, saved){ ], function handleResponse (err) {
if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors
res.sendStatus(200); res.sendStatus(200);
token = user = user_id = sub_id; token = user = userId = subscriptionId;
}); });
}; };
*/
module.exports = api;

View File

@@ -0,0 +1,38 @@
import amazonPayments from 'amazon-payments';
import nconf from 'nconf';
import Q from 'q';
const IS_PROD = nconf.get('NODE_ENV') === 'production';
let api = {};
let amzPayment = amazonPayments.connect({
environment: amazonPayments.Environment[IS_PROD ? 'Production' : 'Sandbox'],
sellerId: nconf.get('AMAZON_PAYMENTS:SELLER_ID'),
mwsAccessKey: nconf.get('AMAZON_PAYMENTS:MWS_KEY'),
mwsSecretKey: nconf.get('AMAZON_PAYMENTS:MWS_SECRET'),
clientId: nconf.get('AMAZON_PAYMENTS:CLIENT_ID'),
});
api.getTokenInfo = (token) => {
return new Promise((resolve, reject) => {
amzPayment.api.getTokenInfo(token, (err, tokenInfo) => {
if (err) return reject(err);
return resolve(tokenInfo);
});
});
};
api.createOrderReferenceId = (inputSet) => {
return new Promise((resolve, reject) => {
amzPayment.offAmazonPayments.createOrderReferenceForId(inputSet, (err, response) => {
if (err) return reject(err);
if (!response.OrderReferenceDetails || !response.OrderReferenceDetails.AmazonOrderReferenceId) {
return reject('missingAttributesFromAmazon');
}
return resolve(response);
});
});
};
module.exports = api;