mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
working on promisifying amazonPayments
This commit is contained in:
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
24
test/api/v3/unit/libs/amazonPayments.test.js
Normal file
24
test/api/v3/unit/libs/amazonPayments.test.js
Normal 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
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
38
website/src/libs/api-v3/amazonPayments.js
Normal file
38
website/src/libs/api-v3/amazonPayments.js
Normal 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;
|
||||||
Reference in New Issue
Block a user