mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
V3 payments 7 stripe (#7124)
* payments api: cancelSubscription * some more tests for amazon payments * promisifying amazon payments * somehow payment stub is not working * cleaning up tests * renaming tests in api/v3/integration/payments * improvements * cleanup, lint * fixes as per comments * moment.zone() is back in. * basic controller for stripe payments * authWithUrl is in * stripe cleanup * making tests pass * stripe bug fixes * 400 error is right * cleanup of sinon spy for fakeSend * paypal payments * lint of paypal * require -> import
This commit is contained in:
committed by
Matteo Pagliazzi
parent
fa21577c46
commit
a567476bb7
@@ -19,7 +19,6 @@ website/src/routes/payments.js
|
||||
website/src/routes/pages.js
|
||||
website/src/middlewares/apiThrottle.js
|
||||
website/src/middlewares/forceRefresh.js
|
||||
website/src/controllers/top-level/payments/paypal.js
|
||||
|
||||
debug-scripts/*
|
||||
tasks/*.js
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"missingAuthHeaders": "Missing authentication headers.",
|
||||
"missingAuthParams": "Missing authentication parameters.",
|
||||
"missingUsernameEmail": "Missing username or email.",
|
||||
"missingEmail": "Missing email.",
|
||||
"missingUsername": "Missing username.",
|
||||
@@ -175,5 +176,7 @@
|
||||
"equipmentAlreadyOwned": "You already own that piece of equipment",
|
||||
"missingAccessToken": "The request is missing a required parameter : access_token",
|
||||
"missingBillingAgreementId": "Missing billing agreement id",
|
||||
"paymentNotSuccessful": "The payment was not successful"
|
||||
"paymentNotSuccessful": "The payment was not successful",
|
||||
"planNotActive": "The plan hasn't activated yet (due to a PayPal bug). It will begin <%= nextBillingDate %>, after which you can cancel to retain your full benefits",
|
||||
"cancelingSubscription": "Canceling the subscription"
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ gulp.task('test:api-v3:unit', (done) => {
|
||||
});
|
||||
|
||||
gulp.task('test:api-v3:unit:watch', () => {
|
||||
gulp.watch(['website/src/libs/api-v3/*', 'test/api/v3/unit/libs/*', 'website/src/controllers/**/*'], ['test:api-v3:unit']);
|
||||
gulp.watch(['website/src/libs/api-v3/*', 'test/api/v3/unit/**/*', 'website/src/controllers/**/*'], ['test:api-v3:unit']);
|
||||
});
|
||||
|
||||
gulp.task('test:api-v3:integration', (done) => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : amazon #subscribeCancel', () => {
|
||||
let endpoint = '/payments/amazon/subscribeCancel';
|
||||
let endpoint = '/payments/amazon/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -13,9 +13,9 @@ describe('payments : amazon #subscribeCancel', () => {
|
||||
|
||||
it('verifies subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingSubscription'),
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingAuthParams'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : paypal #checkout', () => {
|
||||
let endpoint = '/payments/paypal/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingAuthParams'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : paypal #checkoutSuccess', () => {
|
||||
let endpoint = '/payments/paypal/checkout/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('invalidCredentials'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : paypal #subscribe', () => {
|
||||
let endpoint = '/payments/paypal/subscribe';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingAuthParams'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : paypal #subscribeCancel', () => {
|
||||
let endpoint = '/payments/paypal/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingAuthParams'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : paypal #subscribeSuccess', () => {
|
||||
let endpoint = '/payments/paypal/subscribe/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('invalidCredentials'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - stripe - #subscribeCancel', () => {
|
||||
let endpoint = '/payments/stripe/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingAuthParams'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - paypal - #ipn', () => {
|
||||
let endpoint = '/payments/paypal/ipn';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
let result = await user.post(endpoint);
|
||||
expect(result).to.eql({});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - stripe - #checkout', () => {
|
||||
let endpoint = '/payments/stripe/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'Error',
|
||||
message: 'Invalid API Key provided: ****************************1111',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - stripe - #subscribeEdit', () => {
|
||||
let endpoint = '/payments/stripe/subscribe/edit';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -66,10 +66,7 @@ describe('payments/index', () => {
|
||||
|
||||
it('sends a text', async () => {
|
||||
await api.cancelSubscription(data);
|
||||
sinon.assert.calledOnce(fakeSend);
|
||||
sinon.assert.called(fakeSend);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#buyGems', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,9 @@ import validator from 'validator';
|
||||
import moment from 'moment';
|
||||
import passport from 'passport';
|
||||
import nconf from 'nconf';
|
||||
import setupNconf from '../../libs/api-v3/setupNconf';
|
||||
setupNconf();
|
||||
|
||||
import {
|
||||
authWithHeaders,
|
||||
} from '../../middlewares/api-v3/auth';
|
||||
|
||||
@@ -12,6 +12,8 @@ import _ from 'lodash';
|
||||
import { removeFromArray } from '../../libs/api-v3/collectionManipulators';
|
||||
import { sendTxn } from '../../libs/api-v3/email';
|
||||
import nconf from 'nconf';
|
||||
import setupNconf from '../../libs/api-v3/setupNconf';
|
||||
setupNconf();
|
||||
|
||||
const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => {
|
||||
return { email, canSend: true };
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
import mongoose from 'mongoose';
|
||||
import { model as User } from '../../../models/user'; */
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../../../libs/api-v3/errors';
|
||||
import amzLib from '../../../libs/api-v3/amazonPayments';
|
||||
import { authWithHeaders } from '../../../middlewares/api-v3/auth';
|
||||
import {
|
||||
authWithHeaders,
|
||||
authWithUrl,
|
||||
} from '../../../middlewares/api-v3/auth';
|
||||
import shared from '../../../../../common';
|
||||
import payments from '../../../libs/api-v3/payments';
|
||||
import moment from 'moment';
|
||||
@@ -16,7 +16,7 @@ import cc from 'coupon-code';
|
||||
let api = {};
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/payments/amazon/verifyAccessToken verify access token
|
||||
* @api {post} /amazon/verifyAccessToken verify access token
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName AmazonVerifyAccessToken
|
||||
* @apiGroup Payments
|
||||
@@ -40,7 +40,7 @@ api.verifyAccessToken = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/payments/amazon/createOrderReferenceId create order reference id
|
||||
* @api {post} /amazon/createOrderReferenceId create order reference id
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName AmazonCreateOrderReferenceId
|
||||
* @apiGroup Payments
|
||||
@@ -70,7 +70,7 @@ api.createOrderReferenceId = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/payments/amazon/checkout do checkout
|
||||
* @api {post} /amazon/checkout do checkout
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName AmazonCheckout
|
||||
* @apiGroup Payments
|
||||
@@ -148,7 +148,7 @@ api.checkout = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/payments/amazon/subscribe Subscribe
|
||||
* @api {post} /amazon/subscribe Subscribe
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName AmazonSubscribe
|
||||
* @apiGroup Payments
|
||||
@@ -228,7 +228,7 @@ api.subscribe = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {get} /api/v3/payments/amazon/subscribe/cancel SubscribeCancel
|
||||
* @api {get} /amazon/subscribe/cancel SubscribeCancel
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName AmazonSubscribe
|
||||
* @apiGroup Payments
|
||||
@@ -238,7 +238,7 @@ api.subscribe = {
|
||||
api.subscribeCancel = {
|
||||
method: 'GET',
|
||||
url: '/payments/amazon/subscribe/cancel',
|
||||
middlewares: [authWithHeaders()],
|
||||
middlewares: [authWithUrl],
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
let billingAgreementId = user.purchased.plan.customerId;
|
||||
|
||||
@@ -1,218 +1,296 @@
|
||||
var nconf = require('nconf');
|
||||
var moment = require('moment');
|
||||
var async = require('async');
|
||||
var _ = require('lodash');
|
||||
var url = require('url');
|
||||
var User = require('mongoose').model('User');
|
||||
var payments = require('../../../libs/api-v3/payments');
|
||||
var logger = require('../../../libs/api-v2/logging');
|
||||
var ipn = require('paypal-ipn');
|
||||
var paypal = require('paypal-rest-sdk');
|
||||
var shared = require('../../../../../common');
|
||||
var mongoose = require('mongoose');
|
||||
var cc = require('coupon-code');
|
||||
import nconf from 'nconf';
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import payments from '../../../libs/api-v3/payments';
|
||||
import ipn from 'paypal-ipn';
|
||||
import paypal from 'paypal-rest-sdk';
|
||||
import shared from '../../../../../common';
|
||||
import cc from 'coupon-code';
|
||||
import { model as Coupon } from '../../../models/coupon';
|
||||
import { model as User } from '../../../models/user';
|
||||
import {
|
||||
authWithUrl,
|
||||
authWithSession,
|
||||
} from '../../../middlewares/api-v3/auth';
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../../../libs/api-v3/errors';
|
||||
import * as logger from '../../../libs/api-v3/logger';
|
||||
|
||||
// This is the plan.id for paypal subscriptions. You have to set up billing plans via their REST sdk (they don't have
|
||||
// a web interface for billing-plan creation), see ./paypalBillingSetup.js for how. After the billing plan is created
|
||||
// there, get it's plan.id and store it in config.json
|
||||
_.each(shared.content.subscriptionBlocks, function(block){
|
||||
block.paypalKey = nconf.get("PAYPAL:billing_plans:"+block.key);
|
||||
_.each(shared.content.subscriptionBlocks, (block) => {
|
||||
block.paypalKey = nconf.get(`PAYPAL:billing_plans:${block.key}`);
|
||||
});
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
paypal.configure({
|
||||
'mode': nconf.get("PAYPAL:mode"), //sandbox or live
|
||||
'client_id': nconf.get("PAYPAL:client_id"),
|
||||
'client_secret': nconf.get("PAYPAL:client_secret")
|
||||
mode: nconf.get('PAYPAL:mode'), // sandbox or live
|
||||
client_id: nconf.get('PAYPAL:client_id'),
|
||||
client_secret: nconf.get('PAYPAL:client_secret'),
|
||||
});
|
||||
|
||||
var parseErr = function (res, err) {
|
||||
//var error = err.response ? err.response.message || err.response.details[0].issue : err;
|
||||
var error = JSON.stringify(err);
|
||||
return res.status(400).json({err:error});
|
||||
}
|
||||
|
||||
/*
|
||||
exports.createBillingAgreement = function(req,res,next){
|
||||
var sub = shared.content.subscriptionBlocks[req.query.sub];
|
||||
async.waterfall([
|
||||
function(cb){
|
||||
if (!sub.discount) return cb(null, null);
|
||||
if (!req.query.coupon) return cb('Please provide a coupon code for this plan.');
|
||||
mongoose.model('Coupon').findOne({_id:cc.validate(req.query.coupon), event:sub.key}, cb);
|
||||
},
|
||||
function(coupon, cb){
|
||||
if (sub.discount && !coupon) return cb('Invalid coupon code.');
|
||||
var billingPlanTitle = "HabitRPG Subscription" + ' ($'+sub.price+' every '+sub.months+' months, recurring)';
|
||||
var billingAgreementAttributes = {
|
||||
"name": billingPlanTitle,
|
||||
"description": billingPlanTitle,
|
||||
"start_date": moment().add({minutes:5}).format(),
|
||||
"plan": {
|
||||
"id": sub.paypalKey
|
||||
},
|
||||
"payer": {
|
||||
"payment_method": "paypal"
|
||||
}
|
||||
};
|
||||
paypal.billingAgreement.create(billingAgreementAttributes, cb);
|
||||
}
|
||||
], function(err, billingAgreement){
|
||||
if (err) return parseErr(res, err);
|
||||
// For approving subscription via Paypal, first redirect user to: approval_url
|
||||
req.session.paypalBlock = req.query.sub;
|
||||
var approval_url = _.find(billingAgreement.links, {rel:'approval_url'}).href;
|
||||
res.redirect(approval_url);
|
||||
});
|
||||
}
|
||||
|
||||
exports.executeBillingAgreement = function(req,res,next){
|
||||
var block = shared.content.subscriptionBlocks[req.session.paypalBlock];
|
||||
delete req.session.paypalBlock;
|
||||
async.auto({
|
||||
exec: function (cb) {
|
||||
paypal.billingAgreement.execute(req.query.token, {}, cb);
|
||||
},
|
||||
get_user: function (cb) {
|
||||
User.findById(req.session.userId, cb);
|
||||
},
|
||||
create_sub: ['exec', 'get_user', function (cb, results) {
|
||||
payments.createSubscription({
|
||||
user: results.get_user,
|
||||
customerId: results.exec.id,
|
||||
paymentMethod: 'Paypal',
|
||||
sub: block
|
||||
}, cb);
|
||||
}]
|
||||
},function(err){
|
||||
if (err) return parseErr(res, err);
|
||||
res.redirect('/');
|
||||
})
|
||||
}
|
||||
|
||||
exports.createPayment = function(req, res) {
|
||||
// if we're gifting to a user, put it in session for the `execute()`
|
||||
req.session.gift = req.query.gift || undefined;
|
||||
var gift = req.query.gift ? JSON.parse(req.query.gift) : undefined;
|
||||
var price = !gift ? 5.00
|
||||
: gift.type=='gems' ? Number(gift.gems.amount/4).toFixed(2)
|
||||
: Number(shared.content.subscriptionBlocks[gift.subscription.key].price).toFixed(2);
|
||||
var description = !gift ? "HabitRPG Gems"
|
||||
: gift.type=='gems' ? "HabitRPG Gems (Gift)"
|
||||
: shared.content.subscriptionBlocks[gift.subscription.key].months + "mo. HabitRPG Subscription (Gift)";
|
||||
var create_payment = {
|
||||
"intent": "sale",
|
||||
"payer": {
|
||||
"payment_method": "paypal"
|
||||
},
|
||||
"redirect_urls": {
|
||||
"return_url": nconf.get('BASE_URL') + '/paypal/checkout/success',
|
||||
"cancel_url": nconf.get('BASE_URL')
|
||||
},
|
||||
"transactions": [{
|
||||
"item_list": {
|
||||
"items": [{
|
||||
"name": description,
|
||||
//"sku": "1",
|
||||
"price": price,
|
||||
"currency": "USD",
|
||||
"quantity": 1
|
||||
}]
|
||||
},
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"total": price
|
||||
},
|
||||
"description": description
|
||||
}]
|
||||
};
|
||||
paypal.payment.create(create_payment, function (err, payment) {
|
||||
if (err) return parseErr(res, err);
|
||||
var link = _.find(payment.links, {rel: 'approval_url'}).href;
|
||||
res.redirect(link);
|
||||
});
|
||||
}
|
||||
|
||||
exports.executePayment = function(req, res) {
|
||||
var paymentId = req.query.paymentId,
|
||||
PayerID = req.query.PayerID,
|
||||
gift = req.session.gift ? JSON.parse(req.session.gift) : undefined;
|
||||
delete req.session.gift;
|
||||
async.waterfall([
|
||||
function(cb){
|
||||
paypal.payment.execute(paymentId, {payer_id: PayerID}, cb);
|
||||
},
|
||||
function(payment, cb){
|
||||
async.parallel([
|
||||
function(cb2){ User.findById(req.session.userId, cb2); },
|
||||
function(cb2){ User.findById(gift ? gift.uuid : undefined, cb2); }
|
||||
], cb);
|
||||
},
|
||||
function(results, cb){
|
||||
if (_.isEmpty(results[0])) return cb("User not found when completing paypal transaction");
|
||||
var data = {user:results[0], customerId:PayerID, paymentMethod:'Paypal', gift:gift}
|
||||
var method = 'buyGems';
|
||||
if (gift) {
|
||||
gift.member = results[1];
|
||||
if (gift.type=='subscription') method = 'createSubscription';
|
||||
data.paymentMethod = 'Gift';
|
||||
}
|
||||
payments[method](data, cb);
|
||||
}
|
||||
],function(err){
|
||||
if (err) return parseErr(res, err);
|
||||
res.redirect('/');
|
||||
})
|
||||
}
|
||||
|
||||
exports.cancelSubscription = function(req, res, next){
|
||||
var user = res.locals.user;
|
||||
if (!user.purchased.plan.customerId)
|
||||
return res.status(401).json({err: "User does not have a plan subscription"});
|
||||
async.auto({
|
||||
get_cus: function(cb){
|
||||
paypal.billingAgreement.get(user.purchased.plan.customerId, cb);
|
||||
},
|
||||
verify_cus: ['get_cus', function(cb, results){
|
||||
var hasntBilledYet = results.get_cus.agreement_details.cycles_completed == "0";
|
||||
if (hasntBilledYet)
|
||||
return cb("The plan hasn't activated yet (due to a PayPal bug). It will begin "+results.get_cus.agreement_details.next_billing_date+", after which you can cancel to retain your full benefits");
|
||||
cb();
|
||||
}],
|
||||
del_cus: ['verify_cus', function(cb, results){
|
||||
paypal.billingAgreement.cancel(user.purchased.plan.customerId, {note: "Canceling the subscription"}, cb);
|
||||
}],
|
||||
cancel_sub: ['get_cus', 'verify_cus', function(cb, results){
|
||||
var data = {user: user, paymentMethod: 'Paypal', nextBill: results.get_cus.agreement_details.next_billing_date};
|
||||
payments.cancelSubscription(data, cb)
|
||||
}]
|
||||
}, function(err){
|
||||
if (err) return parseErr(res, err);
|
||||
res.redirect('/');
|
||||
user = null;
|
||||
});
|
||||
} // */
|
||||
let api = {};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
/*
|
||||
exports.ipn = function(req, res, next) {
|
||||
console.log('IPN Called');
|
||||
res.sendStatus(200); // Must respond to PayPal IPN request with an empty 200 first
|
||||
ipn.verify(req.body, function(err, msg) {
|
||||
if (err) return logger.error(msg);
|
||||
switch (req.body.txn_type) {
|
||||
// TODO what's the diff b/w the two data.txn_types below? The docs recommend subscr_cancel, but I'm getting the other one instead...
|
||||
case 'recurring_payment_profile_cancel':
|
||||
case 'subscr_cancel':
|
||||
User.findOne({'purchased.plan.customerId':req.body.recurring_payment_id},function(err, user){
|
||||
if (err) return logger.error(err);
|
||||
if (_.isEmpty(user)) return; // looks like the cancellation was already handled properly above (see api.paypalSubscribeCancel)
|
||||
payments.cancelSubscription({user:user, paymentMethod: 'Paypal'});
|
||||
});
|
||||
break;
|
||||
* @api {get} /paypal/checkout checkout
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName PaypalCheckout
|
||||
* @apiGroup Payments
|
||||
*
|
||||
* @apiParam {string} gift The stringified object representing the user, the gift recepient.
|
||||
*
|
||||
* @apiSuccess {} redirect
|
||||
**/
|
||||
api.checkout = {
|
||||
method: 'GET',
|
||||
url: '/payments/paypal/checkout',
|
||||
middlewares: [authWithUrl],
|
||||
async handler (req, res) {
|
||||
let gift = req.query.gift ? JSON.parse(req.query.gift) : undefined;
|
||||
req.session.gift = req.query.gift;
|
||||
|
||||
let amount = 5.00;
|
||||
let description = 'HabitRPG gems';
|
||||
if (gift) {
|
||||
if (gift.type === 'gems') {
|
||||
amount = Number(gift.gems.amount / 4).toFixed(2);
|
||||
description = `${description} (Gift)`;
|
||||
} else {
|
||||
amount = Number(shared.content.subscriptionBlocks[gift.subscription.key].price).toFixed(2);
|
||||
description = 'monthly HabitRPG Subscription (Gift)';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let createPayment = {
|
||||
intent: 'sale',
|
||||
payer: { payment_method: 'Paypal' },
|
||||
redirect_urls: {
|
||||
return_url: `${nconf.get('BASE_URL')}/paypal/checkout/success`,
|
||||
cancel_url: `${nconf.get('BASE_URL')}`,
|
||||
},
|
||||
transactions: [{
|
||||
item_list: {
|
||||
items: [{
|
||||
name: description,
|
||||
price: amount,
|
||||
currency: 'USD',
|
||||
quality: 1,
|
||||
}],
|
||||
},
|
||||
amount: {
|
||||
currency: 'USD',
|
||||
total: amount,
|
||||
},
|
||||
description,
|
||||
}],
|
||||
};
|
||||
try {
|
||||
let result = await paypal.payment.create(createPayment);
|
||||
let link = _.find(result.links, { rel: 'approval_url' }).href;
|
||||
res.redirect(link);
|
||||
} catch (e) {
|
||||
throw new BadRequest(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* @api {get} /paypal/checkout/success Paypal checkout success
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName PaypalCheckoutSuccess
|
||||
* @apiGroup Payments
|
||||
*
|
||||
* @apiParam {string} paymentId The payment id
|
||||
* @apiParam {string} payerID The payer id, notice ID not id
|
||||
*
|
||||
* @apiSuccess {} redirect
|
||||
**/
|
||||
api.checkoutSuccess = {
|
||||
method: 'GET',
|
||||
url: '/payments/paypal/checkout/success',
|
||||
middlewares: [authWithSession],
|
||||
async handler (req, res) {
|
||||
let paymentId = req.query.paymentId;
|
||||
let customerId = req.query.payerID;
|
||||
let method = 'buyGems';
|
||||
let data = {
|
||||
user: res.locals.user,
|
||||
customerId,
|
||||
paymentMethod: 'Paypal',
|
||||
};
|
||||
|
||||
try {
|
||||
let gift = req.session.gift ? JSON.parse(req.session.gift) : undefined;
|
||||
delete req.session.gift;
|
||||
if (gift) {
|
||||
gift.member = await User.findById(gift.uuid);
|
||||
if (gift.type === 'subscription') {
|
||||
method = 'createSubscription';
|
||||
data.paymentMethod = 'Gift';
|
||||
}
|
||||
data.gift = gift;
|
||||
}
|
||||
|
||||
await paypal.payment.execute(paymentId, { payer_id: customerId });
|
||||
await payments[method](data);
|
||||
res.redirect('/');
|
||||
} catch (e) {
|
||||
throw new BadRequest(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {get} /paypal/subscribe Paypal subscribe
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName PaypalSubscribe
|
||||
* @apiGroup Payments
|
||||
*
|
||||
* @apiParam {string} sub subscription, possible values are: basic_earned, basic_3mo, basic_6mo, google_6mo, basic_12mo
|
||||
* @apiParam {string} coupon coupon for the matching subscription, required only for certain subscriptions
|
||||
*
|
||||
* @apiSuccess {} empty object
|
||||
**/
|
||||
api.subscribe = {
|
||||
method: 'GET',
|
||||
url: '/payments/paypal/subscribe',
|
||||
middlewares: [authWithUrl],
|
||||
async handler (req, res) {
|
||||
let sub = shared.content.subscriptionBlocks[req.query.sub];
|
||||
if (sub.discount) {
|
||||
if (!req.query.coupon) throw new BadRequest(res.t('couponCodeRequired'));
|
||||
let coupon = await Coupon.findOne({_id: cc.validate(req.query.coupon), event: sub.key});
|
||||
if (!coupon) throw new BadRequest(res.t('invalidCoupon'));
|
||||
}
|
||||
|
||||
let billingPlanTitle = `HabitRPG Subscription ($${sub.price} every ${sub.months} months, recurring)`;
|
||||
let billingAgreementAttributes = {
|
||||
name: billingPlanTitle,
|
||||
description: billingPlanTitle,
|
||||
start_date: moment().add({ minutes: 5}).format(),
|
||||
plan: {
|
||||
id: sub.paypalKey,
|
||||
},
|
||||
payer: {
|
||||
payment_method: 'Paypal',
|
||||
},
|
||||
};
|
||||
try {
|
||||
let billingAgreement = await paypal.billingAgreement.create(billingAgreementAttributes);
|
||||
req.session.paypalBlock = req.query.sub;
|
||||
let link = _.find(billingAgreement.links, { rel: 'approval_url' }).href;
|
||||
res.redirect(link);
|
||||
} catch (e) {
|
||||
throw new BadRequest(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {get} /paypal/subscribe/success Paypal subscribe success
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName PaypalSubscribeSuccess
|
||||
* @apiGroup Payments
|
||||
*
|
||||
* @apiParam {string} token The token in query
|
||||
*
|
||||
* @apiSuccess {} redirect
|
||||
**/
|
||||
api.subscribeSuccess = {
|
||||
method: 'GET',
|
||||
url: '/payments/paypal/subscribe/success',
|
||||
middlewares: [authWithSession],
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
let block = shared.content.subscriptionBlocks[req.session.paypalBlock];
|
||||
delete req.session.paypalBlock;
|
||||
try {
|
||||
let result = await paypal.billingAgreement.execute(req.query.token, {});
|
||||
await payments.createSubscription({
|
||||
user,
|
||||
customerId: result.id,
|
||||
paymentMethod: 'Paypal',
|
||||
sub: block,
|
||||
});
|
||||
res.redirect('/');
|
||||
} catch (e) {
|
||||
throw new BadRequest(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {get} /paypal/subscribe/cancel Paypal subscribe cancel
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName PaypalSubscribeCancel
|
||||
* @apiGroup Payments
|
||||
*
|
||||
* @apiParam {string} token The token in query
|
||||
*
|
||||
* @apiSuccess {} redirect
|
||||
**/
|
||||
api.subscribeCancel = {
|
||||
method: 'GET',
|
||||
url: '/payments/paypal/subscribe/cancel',
|
||||
middlewares: [authWithUrl],
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
let customerId = user.purchased.plan.customerId;
|
||||
if (!user.purchased.plan.customerId) throw new BadRequest(res.t('missingSubscription'));
|
||||
try {
|
||||
let customer = await paypal.billingAgreement.get(customerId);
|
||||
let nextBillingDate = customer.agreement_details.next_billing_date;
|
||||
if (customer.agreement_details.cycles_completed === '0') { // hasn't billed yet
|
||||
throw new BadRequest(res.t('planNotActive', { nextBillingDate }));
|
||||
}
|
||||
await paypal.billingAgreement.cancel(customerId, { note: res.t('cancelingSubscription') });
|
||||
let data = {
|
||||
user,
|
||||
paymentMethod: 'Paypal',
|
||||
nextBill: nextBillingDate,
|
||||
};
|
||||
await payments.cancelSubscription(data);
|
||||
res.redirect('/');
|
||||
} catch (e) {
|
||||
throw new BadRequest(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {post} /paypal/ipn Paypal IPN
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName PaypalIpn
|
||||
* @apiGroup Payments
|
||||
*
|
||||
* @apiParam {string} txn_type txn_type
|
||||
* @apiParam {string} recurring_payment_id recurring_payment_id
|
||||
*
|
||||
* @apiSuccess {} empty object
|
||||
**/
|
||||
api.ipn = {
|
||||
method: 'POST',
|
||||
url: '/payments/paypal/ipn',
|
||||
middlewares: [],
|
||||
async handler (req, res) {
|
||||
res.respond(200);
|
||||
try {
|
||||
await ipn.verify(req.body);
|
||||
if (req.body.txn_type === 'recurring_payment_profile_cancel' || req.body.txn_type === 'subscr_cancel') {
|
||||
let user = await User.findOne({ 'purchased.plan.customerId': req.body.recurring_payment_id });
|
||||
if (user) {
|
||||
payments.cancelSubscriptoin({ user, paymentMethod: 'Paypal' });
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
module.exports = api;
|
||||
|
||||
@@ -38,6 +38,7 @@ let billingPlanAttributes = {
|
||||
cycles: '0',
|
||||
}],
|
||||
};
|
||||
|
||||
_.each(blocks, function defineBlock (block) {
|
||||
block.definition = _.cloneDeep(billingPlanAttributes);
|
||||
_.merge(block.definition.payment_definitions[0], {
|
||||
|
||||
@@ -1,137 +1,167 @@
|
||||
/* import nconf from 'nconf';
|
||||
import stripeModule from 'stripe';
|
||||
import async from 'async';
|
||||
import payments from '../../../libs/api-v3/payments';
|
||||
import { model as User } from '../../../models/user';
|
||||
import shared from '../../../../../common';
|
||||
import mongoose from 'mongoose';
|
||||
import cc from 'coupon-code'; */
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../../../libs/api-v3/errors';
|
||||
import { model as Coupon } from '../../../models/coupon';
|
||||
import payments from '../../../libs/api-v3/payments';
|
||||
import nconf from 'nconf';
|
||||
import { model as User } from '../../../models/user';
|
||||
import cc from 'coupon-code';
|
||||
import {
|
||||
authWithHeaders,
|
||||
authWithUrl,
|
||||
} from '../../../middlewares/api-v3/auth';
|
||||
|
||||
// const stripe = stripeModule(nconf.get('STRIPE_API_KEY'));
|
||||
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') {
|
||||
/**
|
||||
* @api {post} /stripe/checkout Stripe checkout
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName StripeCheckout
|
||||
* @apiGroup Payments
|
||||
*
|
||||
* @apiParam {string} id The token
|
||||
* @apiParam {string} gift stringified json object, gift
|
||||
* @apiParam {string} sub subscription, possible values are: basic_earned, basic_3mo, basic_6mo, google_6mo, basic_12mo
|
||||
* @apiParam {string} coupon coupon for the matching subscription, required only for certain subscriptions
|
||||
* @apiParam {string} email the customer email
|
||||
*
|
||||
* @apiSuccess {} empty object
|
||||
**/
|
||||
api.checkout = {
|
||||
method: 'POST',
|
||||
url: '/payments/stripe/checkout',
|
||||
middlewares: [authWithHeaders()],
|
||||
async handler (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;
|
||||
let coupon;
|
||||
let response;
|
||||
|
||||
if (sub) {
|
||||
if (sub.discount) {
|
||||
if (!req.query.coupon) throw new BadRequest(res.t('couponCodeRequired'));
|
||||
coupon = await Coupon.findOne({_id: cc.validate(req.query.coupon), event: sub.key});
|
||||
if (!coupon) throw new BadRequest(res.t('invalidCoupon'));
|
||||
}
|
||||
let customer = {
|
||||
email: req.body.email,
|
||||
metadata: { uuid: user._id },
|
||||
card: token,
|
||||
plan: sub.key,
|
||||
};
|
||||
response = await stripe.customers.create(customer);
|
||||
} else {
|
||||
let amount = 500; // $5
|
||||
if (gift) {
|
||||
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;
|
||||
});
|
||||
};
|
||||
response = await stripe.charges.create({
|
||||
amount,
|
||||
currency: 'usd',
|
||||
card: token,
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
if (sub) {
|
||||
await payments.createSubscription({
|
||||
user,
|
||||
customerId: response.id,
|
||||
paymentMethod: 'Stripe',
|
||||
sub,
|
||||
});
|
||||
} else {
|
||||
let method = 'buyGems';
|
||||
let data = {
|
||||
user,
|
||||
nextBill: results.get_cus.subscription.current_period_end * 1000, // timestamp is in seconds
|
||||
customerId: response.id,
|
||||
paymentMethod: 'Stripe',
|
||||
gift,
|
||||
};
|
||||
if (gift) {
|
||||
let member = await User.findById(gift.uuid);
|
||||
gift.member = member;
|
||||
if (gift.type === 'subscription') method = 'createSubscription';
|
||||
data.paymentMethod = 'Gift';
|
||||
}
|
||||
await payments[method](data);
|
||||
}
|
||||
res.respond(200, {});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {post} /stripe/subscribe/edit Stripe subscribeEdit
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName StripeSubscribeEdit
|
||||
* @apiGroup Payments
|
||||
*
|
||||
* @apiParam {string} id The token
|
||||
*
|
||||
* @apiSuccess {}
|
||||
**/
|
||||
api.subscribeEdit = {
|
||||
method: 'POST',
|
||||
url: '/payments/stripe/subscribe/edit',
|
||||
middlewares: [authWithHeaders()],
|
||||
async handler (req, res) {
|
||||
let token = req.body.id;
|
||||
let user = res.locals.user;
|
||||
let customerId = user.purchased.plan.customerId;
|
||||
|
||||
if (!customerId) throw new BadRequest(res.t('missingSubscription'));
|
||||
|
||||
try {
|
||||
let subscriptions = await stripe.customers.listSubscriptions(customerId);
|
||||
let subscriptionId = subscriptions.data[0].id;
|
||||
await stripe.customers.updateSubscription(customerId, subscriptionId, { card: token });
|
||||
res.respond(200, {});
|
||||
} catch (error) {
|
||||
throw new BadRequest(error.message);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {get} /stripe/subscribe/cancel Stripe subscribeCancel
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName StripeSubscribeCancel
|
||||
* @apiGroup Payments
|
||||
*
|
||||
* @apiParam
|
||||
*
|
||||
* @apiSuccess {}
|
||||
**/
|
||||
api.subscribeCancel = {
|
||||
method: 'GET',
|
||||
url: '/payments/stripe/subscribe/cancel',
|
||||
middlewares: [authWithUrl],
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
if (!user.purchased.plan.customerId) throw new BadRequest(res.t('missingSubscription'));
|
||||
try {
|
||||
let customer = await stripe.customers.retrieve(user.purchased.plan.customeerId);
|
||||
await stripe.customers.del(user.purchased.plan.customerId);
|
||||
let data = {
|
||||
user,
|
||||
nextBill: customer.subscription.current_period_end * 1000, // timestamp 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;
|
||||
});
|
||||
await payments.cancelSubscriptoin(data);
|
||||
res.respond(200, {});
|
||||
} catch (e) {
|
||||
throw new BadRequest(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
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,13 +1,11 @@
|
||||
import _ from 'lodash' ;
|
||||
import analytics from './analyticsService';
|
||||
import cc from 'coupon-code';
|
||||
import {
|
||||
getUserInfo,
|
||||
sendTxn as txnEmail,
|
||||
} from './email';
|
||||
import members from '../../controllers/api-v3/members';
|
||||
import moment from 'moment';
|
||||
import mongoose from 'mongoose';
|
||||
import nconf from 'nconf';
|
||||
import pushNotify from './pushNotifications';
|
||||
import shared from '../../../../common' ;
|
||||
|
||||
@@ -55,3 +55,21 @@ export function authWithSession (req, res, next) {
|
||||
})
|
||||
.catch(next);
|
||||
}
|
||||
|
||||
export function authWithUrl (req, res, next) {
|
||||
let userId = req.query._id;
|
||||
let apiToken = req.query.apiToken;
|
||||
|
||||
if (!userId || !apiToken) {
|
||||
throw new NotAuthorized(res.t('missingAuthParams'));
|
||||
}
|
||||
|
||||
User.findOne({ _id: userId, apiToken }).exec()
|
||||
.then((user) => {
|
||||
if (!user) throw new NotAuthorized(res.t('invalidCredentials'));
|
||||
|
||||
res.locals.user = user;
|
||||
next();
|
||||
})
|
||||
.catch(next);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user