2020-12-14 14:59:17 +00:00
|
|
|
import nconf from 'nconf';
|
2018-05-19 15:31:26 +00:00
|
|
|
|
|
|
|
|
import { getStripeApi } from './api';
|
2020-12-14 14:59:17 +00:00
|
|
|
import {
|
|
|
|
|
NotAuthorized,
|
|
|
|
|
NotFound,
|
|
|
|
|
} from '../../errors';
|
2019-10-10 18:11:50 +00:00
|
|
|
import { // eslint-disable-line import/no-cycle
|
2018-05-19 15:31:26 +00:00
|
|
|
model as Group,
|
|
|
|
|
basicFields as basicGroupFields,
|
|
|
|
|
} from '../../../models/group';
|
|
|
|
|
import shared from '../../../../common';
|
2020-12-14 14:59:17 +00:00
|
|
|
import { getOneTimePaymentInfo } from './oneTimePayments'; // eslint-disable-line import/no-cycle
|
|
|
|
|
import { checkSubData } from './subscriptions'; // eslint-disable-line import/no-cycle
|
|
|
|
|
import { validateGiftMessage } from '../gems'; // eslint-disable-line import/no-cycle
|
2018-05-19 15:31:26 +00:00
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
const BASE_URL = nconf.get('BASE_URL');
|
2018-05-19 15:31:26 +00:00
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
export async function createCheckoutSession (options, stripeInc) {
|
|
|
|
|
const {
|
|
|
|
|
user,
|
|
|
|
|
gift,
|
|
|
|
|
gemsBlock: gemsBlockKey,
|
|
|
|
|
sub,
|
|
|
|
|
groupId,
|
|
|
|
|
coupon,
|
|
|
|
|
} = options;
|
2018-05-19 15:31:26 +00:00
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
// @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;
|
2018-05-19 15:31:26 +00:00
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
let type = 'gems';
|
Fall Festival Gem Promo (#138)
* content: add gems blocks
* gemsBlocks: include ios and android identifiers
* wip: promo code
* split common constants into multiple files
* add second promo part
* geCurrentEvent, refactor promo
* fix lint
* fix exports, use world state api
* start adding world state tests
* remove console.log
* use gems block for purchases
* remove comments
* fix most unit tests
* restore comment
* fix lint
* prevent apple/google gift tests from breaking other tests when stub is not reset
* fix unit tests, clarify tests names
* iap: use gift object when gifting gems
* allow gift object with less data
* fix iap tests, remove findById stubs
* iap: require less data from the mobile apps
* apply discounts
* add missing worldState file
* fix lint
* add test event
* start removing 20 gems option for web
* start adding support for all gems packages on web
* fix unit tests for apple, stripe and google
* amazon: support all gems blocks
* paypal: support all gems blocks
* fix payments unit tests, add tests for getGemsBlock
* web: add gems plans with discounts, update stripe
* fix amazon and paypal clients, payments success modals
* amazon pay: disabled state
* update icons, start abstracting payments buttons
* begin redesign
* redesign gems modal
* fix buttons
* fix hover color for gems modal close icon
* add key to world state current event
* extend test event length
* implement gems modals designs
* early test fall2020
* fix header banner position
* add missing files
* use iso 8601 for dates, minor ui fixes
* fix time zones
* events: fix ISO8601 format
* fix css indentation
* start abstracting banners
* refactor payments buttons
* test spooky, fix group plans box
* implement gems promo banners, refactor banners, fixes
* fix lint
* fix dates
* remove unused i18n strings
* fix stripe integration test
* fix world state integration tests
* the current active event
* add missing unit tests
* add storybook story for payments buttons component
* fix typo
* fix(stripe): correct label when gifting subscriptions
2020-09-21 14:22:13 +00:00
|
|
|
if (gift) {
|
2020-12-14 14:59:17 +00:00
|
|
|
type = gift.type === 'gems' ? 'gift-gems' : 'gift-sub';
|
|
|
|
|
validateGiftMessage(gift, user);
|
|
|
|
|
} else if (sub) {
|
|
|
|
|
type = 'subscription';
|
2018-05-19 15:31:26 +00:00
|
|
|
}
|
|
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
const metadata = {
|
|
|
|
|
type,
|
|
|
|
|
userId: user._id,
|
|
|
|
|
gift: gift ? JSON.stringify(gift) : undefined,
|
|
|
|
|
sub: sub ? JSON.stringify(sub) : undefined,
|
2018-05-19 15:31:26 +00:00
|
|
|
};
|
|
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
let lineItems;
|
|
|
|
|
|
|
|
|
|
if (type === 'subscription') {
|
|
|
|
|
let quantity = 1;
|
|
|
|
|
|
|
|
|
|
if (groupId) {
|
|
|
|
|
quantity = sub.quantity;
|
|
|
|
|
const groupFields = basicGroupFields.concat(' purchased');
|
|
|
|
|
const group = await Group.getGroup({
|
|
|
|
|
user, groupId, populateLeader: false, groupFields,
|
|
|
|
|
});
|
|
|
|
|
if (!group) {
|
|
|
|
|
throw new NotFound(shared.i18n.t('groupNotFound', user.preferences.language));
|
|
|
|
|
}
|
|
|
|
|
const membersCount = await group.getMemberCount();
|
|
|
|
|
quantity = membersCount + sub.quantity - 1;
|
|
|
|
|
metadata.groupId = groupId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await checkSubData(sub, Boolean(groupId), coupon);
|
|
|
|
|
|
|
|
|
|
lineItems = [{
|
|
|
|
|
price: sub.key,
|
|
|
|
|
quantity,
|
|
|
|
|
}];
|
|
|
|
|
} else {
|
|
|
|
|
const {
|
|
|
|
|
amount,
|
|
|
|
|
gemsBlock,
|
|
|
|
|
subscription,
|
|
|
|
|
} = await getOneTimePaymentInfo(gemsBlockKey, gift, user);
|
|
|
|
|
|
|
|
|
|
metadata.gemsBlock = gemsBlock ? gemsBlock.key : undefined;
|
|
|
|
|
|
|
|
|
|
let productName;
|
|
|
|
|
|
|
|
|
|
if (gift) {
|
|
|
|
|
if (gift.type === 'subscription') {
|
|
|
|
|
productName = shared.i18n.t('nMonthsSubscriptionGift', { nMonths: subscription.months }, user.preferences.language);
|
|
|
|
|
} else {
|
|
|
|
|
productName = shared.i18n.t('nGemsGift', { nGems: gift.gems.amount }, user.preferences.language);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
productName = shared.i18n.t('nGems', { nGems: gemsBlock.gems }, user.preferences.language);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lineItems = [{
|
|
|
|
|
price_data: {
|
|
|
|
|
product_data: {
|
|
|
|
|
name: productName,
|
|
|
|
|
},
|
|
|
|
|
unit_amount: amount,
|
|
|
|
|
currency: 'usd',
|
|
|
|
|
},
|
|
|
|
|
quantity: 1,
|
|
|
|
|
}];
|
2018-05-19 15:31:26 +00:00
|
|
|
}
|
|
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
const session = await stripeApi.checkout.sessions.create({
|
|
|
|
|
payment_method_types: ['card'],
|
|
|
|
|
metadata,
|
|
|
|
|
line_items: lineItems,
|
|
|
|
|
mode: type === 'subscription' ? 'subscription' : 'payment',
|
|
|
|
|
success_url: `${BASE_URL}/redirect/stripe-success-checkout`,
|
|
|
|
|
cancel_url: `${BASE_URL}/redirect/stripe-error-checkout`,
|
|
|
|
|
});
|
2018-05-19 15:31:26 +00:00
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
return session;
|
2018-05-19 15:31:26 +00:00
|
|
|
}
|
|
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
export async function createEditCardCheckoutSession (options, stripeInc) {
|
2019-10-08 14:57:10 +00:00
|
|
|
const {
|
2018-05-19 15:31:26 +00:00
|
|
|
user,
|
|
|
|
|
groupId,
|
|
|
|
|
} = options;
|
|
|
|
|
|
2019-10-10 18:11:50 +00:00
|
|
|
// @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?
|
2018-05-19 15:31:26 +00:00
|
|
|
let stripeApi = getStripeApi();
|
|
|
|
|
if (stripeInc) stripeApi = stripeInc;
|
|
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
const type = groupId ? 'edit-card-group' : 'edit-card-user';
|
2018-05-19 15:31:26 +00:00
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
const metadata = {
|
|
|
|
|
type,
|
|
|
|
|
userId: user._id,
|
|
|
|
|
};
|
2018-05-19 15:31:26 +00:00
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
let customerId;
|
|
|
|
|
let subscriptionId;
|
Fall Festival Gem Promo (#138)
* content: add gems blocks
* gemsBlocks: include ios and android identifiers
* wip: promo code
* split common constants into multiple files
* add second promo part
* geCurrentEvent, refactor promo
* fix lint
* fix exports, use world state api
* start adding world state tests
* remove console.log
* use gems block for purchases
* remove comments
* fix most unit tests
* restore comment
* fix lint
* prevent apple/google gift tests from breaking other tests when stub is not reset
* fix unit tests, clarify tests names
* iap: use gift object when gifting gems
* allow gift object with less data
* fix iap tests, remove findById stubs
* iap: require less data from the mobile apps
* apply discounts
* add missing worldState file
* fix lint
* add test event
* start removing 20 gems option for web
* start adding support for all gems packages on web
* fix unit tests for apple, stripe and google
* amazon: support all gems blocks
* paypal: support all gems blocks
* fix payments unit tests, add tests for getGemsBlock
* web: add gems plans with discounts, update stripe
* fix amazon and paypal clients, payments success modals
* amazon pay: disabled state
* update icons, start abstracting payments buttons
* begin redesign
* redesign gems modal
* fix buttons
* fix hover color for gems modal close icon
* add key to world state current event
* extend test event length
* implement gems modals designs
* early test fall2020
* fix header banner position
* add missing files
* use iso 8601 for dates, minor ui fixes
* fix time zones
* events: fix ISO8601 format
* fix css indentation
* start abstracting banners
* refactor payments buttons
* test spooky, fix group plans box
* implement gems promo banners, refactor banners, fixes
* fix lint
* fix dates
* remove unused i18n strings
* fix stripe integration test
* fix world state integration tests
* the current active event
* add missing unit tests
* add storybook story for payments buttons component
* fix typo
* fix(stripe): correct label when gifting subscriptions
2020-09-21 14:22:13 +00:00
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
if (type === 'edit-card-group') {
|
|
|
|
|
const groupFields = basicGroupFields.concat(' purchased');
|
|
|
|
|
const group = await Group.getGroup({
|
|
|
|
|
user, groupId, populateLeader: false, groupFields,
|
|
|
|
|
});
|
|
|
|
|
if (!group) {
|
|
|
|
|
throw new NotFound(shared.i18n.t('groupNotFound', user.preferences.language));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const allowedManagers = [group.leader, group.purchased.plan.owner];
|
|
|
|
|
|
|
|
|
|
if (allowedManagers.indexOf(user._id) === -1) {
|
|
|
|
|
throw new NotAuthorized(shared.i18n.t('onlyGroupLeaderCanManageSubscription', user.preferences.language));
|
|
|
|
|
}
|
|
|
|
|
metadata.groupId = groupId;
|
|
|
|
|
customerId = group.purchased.plan.customerId;
|
|
|
|
|
subscriptionId = group.purchased.plan.subscriptionId;
|
2018-05-19 15:31:26 +00:00
|
|
|
} else {
|
2020-12-14 14:59:17 +00:00
|
|
|
customerId = user.purchased.plan.customerId;
|
|
|
|
|
subscriptionId = user.purchased.plan.subscriptionId;
|
2018-05-19 15:31:26 +00:00
|
|
|
}
|
|
|
|
|
|
2020-12-14 14:59:17 +00:00
|
|
|
if (!customerId) throw new NotAuthorized(shared.i18n.t('missingSubscription', user.preferences.language));
|
|
|
|
|
|
|
|
|
|
if (!subscriptionId) {
|
|
|
|
|
const subscriptions = await stripeApi.subscriptions.list({ customer: customerId });
|
|
|
|
|
subscriptionId = subscriptions.data[0] && subscriptions.data[0].id;
|
2018-05-19 15:31:26 +00:00
|
|
|
}
|
2020-12-14 14:59:17 +00:00
|
|
|
|
|
|
|
|
if (!subscriptionId) throw new NotAuthorized(shared.i18n.t('missingSubscription', user.preferences.language));
|
|
|
|
|
|
|
|
|
|
const session = await stripeApi.checkout.sessions.create({
|
|
|
|
|
mode: 'setup',
|
|
|
|
|
payment_method_types: ['card'],
|
|
|
|
|
metadata,
|
|
|
|
|
customer: customerId,
|
|
|
|
|
setup_intent_data: {
|
|
|
|
|
metadata: {
|
|
|
|
|
customer_id: customerId,
|
|
|
|
|
subscription_id: subscriptionId,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
success_url: `${BASE_URL}/redirect/stripe-success-checkout`,
|
|
|
|
|
cancel_url: `${BASE_URL}/redirect/stripe-error-checkout`,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return session;
|
2018-05-19 15:31:26 +00:00
|
|
|
}
|