Files
habitica/website/server/libs/payments/stripe/checkout.js
2019-10-02 19:45:27 +02:00

147 lines
3.7 KiB
JavaScript

import cc from 'coupon-code';
import { getStripeApi } from './api';
import { model as User } from '../../../models/user';
import { model as Coupon } from '../../../models/coupon';
import {
model as Group,
basicFields as basicGroupFields,
} from '../../../models/group';
import shared from '../../../../common';
import {
BadRequest,
NotAuthorized,
} from '../../errors';
import payments from './../payments';
import stripeConstants from './constants';
function getGiftAmount (gift) {
if (gift.type === 'subscription') {
return `${shared.content.subscriptionBlocks[gift.subscription.key].price * 100}`;
}
if (gift.gems.amount <= 0) {
throw new BadRequest(shared.i18n.t('badAmountOfGemsToPurchase'));
}
return `${gift.gems.amount / 4 * 100}`;
}
async function buyGems (gift, user, token, stripeApi) {
let amount = 500; // $5
if (gift) amount = getGiftAmount(gift);
if (!gift || gift.type === 'gems') {
const receiver = gift ? gift.member : user;
const receiverCanGetGems = await receiver.canGetGems();
if (!receiverCanGetGems) throw new NotAuthorized(shared.i18n.t('groupPolicyCannotGetGems', receiver.preferences.language));
}
const response = await stripeApi.charges.create({
amount,
currency: 'usd',
card: token,
});
return response;
}
async function buySubscription (sub, coupon, email, user, token, groupId, stripeApi) {
if (sub.discount) {
if (!coupon) throw new BadRequest(shared.i18n.t('couponCodeRequired'));
coupon = await Coupon.findOne({_id: cc.validate(coupon), event: sub.key}).exec();
if (!coupon) throw new BadRequest(shared.i18n.t('invalidCoupon'));
}
let customerObject = {
email,
metadata: { uuid: user._id },
card: token,
plan: sub.key,
};
if (groupId) {
customerObject.quantity = sub.quantity;
const groupFields = basicGroupFields.concat(' purchased');
const group = await Group.getGroup({user, groupId, populateLeader: false, groupFields});
const membersCount = await group.getMemberCount();
customerObject.quantity = membersCount + sub.quantity - 1;
}
const response = await stripeApi.customers.create(customerObject);
let subscriptionId;
if (groupId) subscriptionId = response.subscriptions.data[0].id;
return { subResponse: response, subId: subscriptionId };
}
async function applyGemPayment (user, response, gift) {
let method = 'buyGems';
const data = {
user,
customerId: response.id,
paymentMethod: stripeConstants.PAYMENT_METHOD,
gift,
};
if (gift) {
if (gift.type === 'subscription') method = 'createSubscription';
data.paymentMethod = 'Gift';
}
await payments[method](data);
}
async function checkout (options, stripeInc) {
let {
token,
user,
gift,
sub,
groupId,
email,
headers,
coupon,
} = options;
let response;
let subscriptionId;
// @TODO: We need to mock this, but curently we don't have correct Dependency Injection. And the Stripe Api doesn't seem to be a singleton?
let stripeApi = getStripeApi();
if (stripeInc) stripeApi = stripeInc;
if (!token) throw new BadRequest('Missing req.body.id');
if (gift) {
const member = await User.findById(gift.uuid).exec();
gift.member = member;
}
if (sub) {
const { subId, subResponse } = await buySubscription(sub, coupon, email, user, token, groupId, stripeApi);
subscriptionId = subId;
response = subResponse;
} else {
response = await buyGems(gift, user, token, stripeApi);
}
if (sub) {
await payments.createSubscription({
user,
customerId: response.id,
paymentMethod: this.constants.PAYMENT_METHOD,
sub,
headers,
groupId,
subscriptionId,
});
return;
}
await applyGemPayment(user, response, gift);
}
export { checkout };