Stripe: upgrade module and API, switch to Checkout (#12785)

* upgrade stripe module

* switch stripe api to latest version

* fix api version in tests

* start upgrading client and server

* client: switch to redirect

* implement checkout session creation for gems, start implementing webhooks

* stripe: start refactoring one time payments

* working gems and gift payments

* start adding support for subscriptions

* stripe: migrate subscriptions and fix cancelling sub

* allow upgrading group plans

* remove console.log statements

* group plans: upgrade from static page / create new one

* fix #11885, correct group plan modal title

* silence more stripe webhooks

* fix group plans redirects

* implement editing payment method

* start cleaning up code

* fix(stripe): update in-code docs, fix eslint issues

* subscriptions tests

* remove and skip old tests

* skip integration tests

* fix client build

* stripe webhooks: throw error if request fails

* subscriptions: correctly pass groupId

* remove console.log

* stripe: add unit tests for one time payments

* wip: stripe checkout tests

* stripe createCheckoutSession unit tests

* stripe createCheckoutSession unit tests

* stripe createCheckoutSession unit tests (editing card)

* fix existing webhooks tests

* add new webhooks tests

* add more webhooks tests

* fix lint

* stripe integration tests

* better error handling when retrieving customer from stripe

* client: remove unused strings and improve error handling

* payments: limit gift message length (server)

* payments: limit gift message length (client)

* fix redirects when payment is cancelled

* add back "subUpdateCard" string

* fix redirects when editing a sub card, use proper names for products, check subs when gifting
This commit is contained in:
Matteo Pagliazzi
2020-12-14 15:59:17 +01:00
parent 5daf96bbf5
commit 2d091fc667
53 changed files with 2457 additions and 1661 deletions
@@ -0,0 +1,45 @@
import {
generateUser,
} from '../../../../../helpers/api-integration/v3';
import stripePayments from '../../../../../../website/server/libs/payments/stripe';
import common from '../../../../../../website/common';
describe('payments - stripe - #createCheckoutSession', () => {
const endpoint = '/stripe/checkout-session';
let user; const groupId = 'groupId';
const gift = {}; const subKey = 'basic_3mo';
const gemsBlock = '21gems'; const coupon = 'coupon';
let stripeCreateCheckoutSessionStub; const sessionId = 'sessionId';
beforeEach(async () => {
user = await generateUser();
stripeCreateCheckoutSessionStub = sinon
.stub(stripePayments, 'createCheckoutSession')
.resolves({ id: sessionId });
});
afterEach(() => {
stripePayments.createCheckoutSession.restore();
});
it('works', async () => {
const res = await user.post(endpoint, {
groupId,
gift,
sub: subKey,
gemsBlock,
coupon,
});
expect(res.sessionId).to.equal(sessionId);
expect(stripeCreateCheckoutSessionStub).to.be.calledOnce;
expect(stripeCreateCheckoutSessionStub.args[0][0].user._id).to.eql(user._id);
expect(stripeCreateCheckoutSessionStub.args[0][0].groupId).to.eql(groupId);
expect(stripeCreateCheckoutSessionStub.args[0][0].gift).to.eql(gift);
expect(stripeCreateCheckoutSessionStub.args[0][0].sub)
.to.eql(common.content.subscriptionBlocks[subKey]);
expect(stripeCreateCheckoutSessionStub.args[0][0].gemsBlock).to.eql(gemsBlock);
expect(stripeCreateCheckoutSessionStub.args[0][0].coupon).to.eql(coupon);
});
});
@@ -1,79 +0,0 @@
import {
generateUser,
generateGroup,
} from '../../../../../helpers/api-integration/v3';
import stripePayments from '../../../../../../website/server/libs/payments/stripe';
describe('payments - stripe - #checkout', () => {
const endpoint = '/stripe/checkout';
let user; let
group;
beforeEach(async () => {
user = await generateUser();
});
it('verifies credentials', async () => {
await expect(user.post(
`${endpoint}?gemsBlock=4gems`,
{ id: 123 },
)).to.eventually.be.rejected.and.include({
code: 401,
error: 'Error',
// message: 'Invalid API Key provided: aaaabbbb********************1111',
});
});
describe('success', () => {
let stripeCheckoutSubscriptionStub;
beforeEach(async () => {
stripeCheckoutSubscriptionStub = sinon.stub(stripePayments, 'checkout').resolves({});
});
afterEach(() => {
stripePayments.checkout.restore();
});
it('creates a user subscription', async () => {
user = await generateUser({
'profile.name': 'sender',
'purchased.plan.customerId': 'customer-id',
'purchased.plan.planId': 'basic_3mo',
'purchased.plan.lastBillingDate': new Date(),
balance: 2,
});
await user.post(endpoint);
expect(stripeCheckoutSubscriptionStub).to.be.calledOnce;
expect(stripeCheckoutSubscriptionStub.args[0][0].user._id).to.eql(user._id);
expect(stripeCheckoutSubscriptionStub.args[0][0].groupId).to.eql(undefined);
});
it('creates a group subscription', async () => {
user = await generateUser({
'profile.name': 'sender',
'purchased.plan.customerId': 'customer-id',
'purchased.plan.planId': 'basic_3mo',
'purchased.plan.lastBillingDate': new Date(),
balance: 2,
});
group = await generateGroup(user, {
name: 'test group',
type: 'guild',
privacy: 'public',
'purchased.plan.customerId': 'customer-id',
'purchased.plan.planId': 'basic_3mo',
'purchased.plan.lastBillingDate': new Date(),
});
await user.post(`${endpoint}?groupId=${group._id}`);
expect(stripeCheckoutSubscriptionStub).to.be.calledOnce;
expect(stripeCheckoutSubscriptionStub.args[0][0].user._id).to.eql(user._id);
expect(stripeCheckoutSubscriptionStub.args[0][0].groupId).to.eql(group._id);
});
});
});
@@ -1,79 +1,31 @@
import {
generateUser,
generateGroup,
translate as t,
} from '../../../../../helpers/api-integration/v3';
import stripePayments from '../../../../../../website/server/libs/payments/stripe';
describe('payments - stripe - #subscribeEdit', () => {
const endpoint = '/stripe/subscribe/edit';
let user; let
group;
let user; const groupId = 'groupId';
let stripeEditSubscriptionStub;
const sessionId = 'sessionId';
beforeEach(async () => {
user = await generateUser();
stripeEditSubscriptionStub = sinon
.stub(stripePayments, 'createEditCardCheckoutSession')
.resolves({ id: sessionId });
});
it('verifies credentials', async () => {
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('missingSubscription'),
});
afterEach(() => {
stripePayments.createEditCardCheckoutSession.restore();
});
describe('success', () => {
let stripeEditSubscriptionStub;
it('works', async () => {
const res = await user.post(endpoint, { groupId });
expect(res.sessionId).to.equal(sessionId);
beforeEach(async () => {
stripeEditSubscriptionStub = sinon.stub(stripePayments, 'editSubscription').resolves({});
});
afterEach(() => {
stripePayments.editSubscription.restore();
});
it('cancels a user subscription', async () => {
user = await generateUser({
'profile.name': 'sender',
'purchased.plan.customerId': 'customer-id',
'purchased.plan.planId': 'basic_3mo',
'purchased.plan.lastBillingDate': new Date(),
balance: 2,
});
await user.post(endpoint);
expect(stripeEditSubscriptionStub).to.be.calledOnce;
expect(stripeEditSubscriptionStub.args[0][0].user._id).to.eql(user._id);
expect(stripeEditSubscriptionStub.args[0][0].groupId).to.eql(undefined);
});
it('cancels a group subscription', async () => {
user = await generateUser({
'profile.name': 'sender',
'purchased.plan.customerId': 'customer-id',
'purchased.plan.planId': 'basic_3mo',
'purchased.plan.lastBillingDate': new Date(),
balance: 2,
});
group = await generateGroup(user, {
name: 'test group',
type: 'guild',
privacy: 'public',
'purchased.plan.customerId': 'customer-id',
'purchased.plan.planId': 'basic_3mo',
'purchased.plan.lastBillingDate': new Date(),
});
await user.post(endpoint, {
groupId: group._id,
});
expect(stripeEditSubscriptionStub).to.be.calledOnce;
expect(stripeEditSubscriptionStub.args[0][0].user._id).to.eql(user._id);
expect(stripeEditSubscriptionStub.args[0][0].groupId).to.eql(group._id);
});
expect(stripeEditSubscriptionStub).to.be.calledOnce;
expect(stripeEditSubscriptionStub.args[0][0].user._id).to.eql(user._id);
expect(stripeEditSubscriptionStub.args[0][0].groupId).to.eql(groupId);
});
});
@@ -0,0 +1,30 @@
import {
generateUser,
} from '../../../../../helpers/api-integration/v3';
import stripePayments from '../../../../../../website/server/libs/payments/stripe';
describe('payments - stripe - #handleWebhooks', () => {
const endpoint = '/stripe/webhooks';
let user; const body = '{"key": "val"}';
let stripeHandleWebhooksStub;
beforeEach(async () => {
user = await generateUser();
stripeHandleWebhooksStub = sinon
.stub(stripePayments, 'handleWebhooks')
.resolves({});
});
afterEach(() => {
stripePayments.handleWebhooks.restore();
});
it('works', async () => {
const res = await user.post(endpoint, body);
expect(res).to.eql({});
expect(stripeHandleWebhooksStub).to.be.calledOnce;
expect(stripeHandleWebhooksStub.args[0][0].body).to.exist;
expect(stripeHandleWebhooksStub.args[0][0].headers).to.exist;
});
});