2022-02-09 16:00:39 +03:00
|
|
|
const sinon = require('sinon');
|
|
|
|
const should = require('should');
|
|
|
|
const rewire = require('rewire');
|
|
|
|
const StripeAPI = rewire('../../../lib/StripeAPI');
|
|
|
|
|
|
|
|
describe('StripeAPI', function () {
|
2023-07-13 14:20:54 +03:00
|
|
|
const mockCustomerEmail = 'foo@example.com';
|
|
|
|
const mockCustomerId = 'cust_mock_123456';
|
|
|
|
const mockCustomerName = 'Example Customer';
|
2024-04-04 17:17:14 +03:00
|
|
|
let mockLabs = {
|
|
|
|
isSet() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const api = new StripeAPI({labs: mockLabs});
|
2023-07-13 14:20:54 +03:00
|
|
|
|
2022-02-09 16:00:39 +03:00
|
|
|
let mockStripe;
|
2023-07-13 14:20:54 +03:00
|
|
|
|
|
|
|
describe('createCheckoutSession', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
mockStripe = {
|
|
|
|
checkout: {
|
|
|
|
sessions: {
|
|
|
|
create: sinon.stub().resolves()
|
|
|
|
}
|
2022-02-09 16:00:39 +03:00
|
|
|
}
|
2023-07-13 14:20:54 +03:00
|
|
|
};
|
2024-04-04 17:17:14 +03:00
|
|
|
sinon.stub(mockLabs, 'isSet');
|
2023-07-13 14:20:54 +03:00
|
|
|
const mockStripeConstructor = sinon.stub().returns(mockStripe);
|
|
|
|
StripeAPI.__set__('Stripe', mockStripeConstructor);
|
|
|
|
api.configure({
|
|
|
|
checkoutSessionSuccessUrl: '/success',
|
|
|
|
checkoutSessionCancelUrl: '/cancel',
|
|
|
|
checkoutSetupSessionSuccessUrl: '/setup-success',
|
|
|
|
checkoutSetupSessionCancelUrl: '/setup-cancel',
|
|
|
|
secretKey: ''
|
|
|
|
});
|
2022-02-09 16:00:39 +03:00
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
afterEach(function () {
|
|
|
|
sinon.restore();
|
|
|
|
});
|
2022-02-09 16:00:39 +03:00
|
|
|
|
2024-04-04 17:17:14 +03:00
|
|
|
it('Sends card as payment method if labs flag not enabled', async function () {
|
|
|
|
await api.createCheckoutSession('priceId', null, {});
|
|
|
|
|
|
|
|
should.deepEqual(mockStripe.checkout.sessions.create.firstCall.firstArg.payment_method_types, ['card']);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Sends no payment methods if labs flag is enabled', async function () {
|
|
|
|
mockLabs.isSet.withArgs('additionalPaymentMethods').returns(true);
|
|
|
|
await api.createCheckoutSession('priceId', null, {});
|
|
|
|
|
|
|
|
should.deepEqual(mockStripe.checkout.sessions.create.firstCall.firstArg.payment_method_types, undefined);
|
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
it('sends success_url and cancel_url', async function () {
|
|
|
|
await api.createCheckoutSession('priceId', null, {});
|
2022-02-09 16:00:39 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.success_url);
|
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.cancel_url);
|
|
|
|
});
|
2022-02-09 16:00:39 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
it('sets valid trialDays', async function () {
|
|
|
|
await api.createCheckoutSession('priceId', null, {
|
|
|
|
trialDays: 12
|
|
|
|
});
|
|
|
|
|
|
|
|
should.not.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_from_plan);
|
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_period_days);
|
|
|
|
should.equal(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_period_days, 12);
|
2022-08-05 12:58:01 +03:00
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
it('uses trial_from_plan without trialDays', async function () {
|
|
|
|
await api.createCheckoutSession('priceId', null, {});
|
2022-08-05 12:58:01 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_from_plan);
|
|
|
|
should.equal(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_from_plan, true);
|
|
|
|
should.not.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_period_days);
|
|
|
|
});
|
2022-08-05 12:58:01 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
it('ignores 0 trialDays', async function () {
|
|
|
|
await api.createCheckoutSession('priceId', null, {
|
|
|
|
trialDays: 0
|
|
|
|
});
|
2022-08-09 06:37:16 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_from_plan);
|
|
|
|
should.equal(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_from_plan, true);
|
|
|
|
should.not.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_period_days);
|
2022-08-09 06:37:16 +03:00
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
it('ignores null trialDays', async function () {
|
|
|
|
await api.createCheckoutSession('priceId', null, {
|
|
|
|
trialDays: null
|
|
|
|
});
|
2022-08-09 06:37:16 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_from_plan);
|
|
|
|
should.equal(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_from_plan, true);
|
|
|
|
should.not.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.subscription_data.trial_period_days);
|
2022-08-09 06:37:16 +03:00
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
it('passes customer ID successfully to Stripe', async function () {
|
|
|
|
const mockCustomer = {
|
|
|
|
id: mockCustomerId,
|
|
|
|
customer_email: mockCustomerEmail,
|
|
|
|
name: 'Example Customer'
|
|
|
|
};
|
2023-01-06 07:44:56 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
await api.createCheckoutSession('priceId', mockCustomer, {
|
|
|
|
trialDays: null
|
|
|
|
});
|
2023-01-06 07:44:56 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.customer);
|
|
|
|
should.equal(mockStripe.checkout.sessions.create.firstCall.firstArg.customer, 'cust_mock_123456');
|
2023-01-06 07:44:56 +03:00
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
it('passes email if no customer object provided', async function () {
|
|
|
|
await api.createCheckoutSession('priceId', undefined, {
|
|
|
|
customerEmail: mockCustomerEmail,
|
|
|
|
trialDays: null
|
|
|
|
});
|
2023-01-06 07:44:56 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.customer_email);
|
|
|
|
should.equal(mockStripe.checkout.sessions.create.firstCall.firstArg.customer_email, 'foo@example.com');
|
2023-01-06 07:44:56 +03:00
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
it('passes email if customer object provided w/o ID', async function () {
|
|
|
|
const mockCustomer = {
|
|
|
|
email: mockCustomerEmail,
|
|
|
|
name: mockCustomerName
|
|
|
|
};
|
2023-01-06 07:44:56 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
await api.createCheckoutSession('priceId', mockCustomer, {
|
|
|
|
trialDays: null
|
|
|
|
});
|
2023-01-06 07:44:56 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.customer_email);
|
|
|
|
should.equal(mockStripe.checkout.sessions.create.firstCall.firstArg.customer_email, 'foo@example.com');
|
2023-01-06 07:44:56 +03:00
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
it('passes only one of customer ID and email', async function () {
|
|
|
|
const mockCustomer = {
|
|
|
|
id: mockCustomerId,
|
|
|
|
email: mockCustomerEmail,
|
|
|
|
name: mockCustomerName
|
|
|
|
};
|
|
|
|
|
|
|
|
await api.createCheckoutSession('priceId', mockCustomer, {
|
|
|
|
trialDays: null
|
|
|
|
});
|
|
|
|
|
|
|
|
should.not.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.customer_email);
|
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.customer);
|
|
|
|
should.equal(mockStripe.checkout.sessions.create.firstCall.firstArg.customer, 'cust_mock_123456');
|
|
|
|
});
|
2023-01-06 07:44:56 +03:00
|
|
|
});
|
|
|
|
|
2024-05-08 21:56:17 +03:00
|
|
|
describe('createCheckoutSetupSession', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
mockStripe = {
|
|
|
|
checkout: {
|
|
|
|
sessions: {
|
|
|
|
create: sinon.stub().resolves()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
sinon.stub(mockLabs, 'isSet');
|
|
|
|
const mockStripeConstructor = sinon.stub().returns(mockStripe);
|
|
|
|
StripeAPI.__set__('Stripe', mockStripeConstructor);
|
|
|
|
api.configure({
|
|
|
|
checkoutSessionSuccessUrl: '/success',
|
|
|
|
checkoutSessionCancelUrl: '/cancel',
|
|
|
|
checkoutSetupSessionSuccessUrl: '/setup-success',
|
|
|
|
checkoutSetupSessionCancelUrl: '/setup-cancel',
|
|
|
|
secretKey: ''
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(function () {
|
|
|
|
sinon.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('createCheckoutSetupSession sends success_url and cancel_url', async function () {
|
|
|
|
await api.createCheckoutSetupSession('priceId', {});
|
|
|
|
|
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.success_url);
|
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.cancel_url);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('createCheckoutSetupSession does not send currency if additionalPaymentMethods flag is off', async function () {
|
|
|
|
mockLabs.isSet.withArgs('additionalPaymentMethods').returns(false);
|
|
|
|
await api.createCheckoutSetupSession('priceId', {currency: 'usd'});
|
|
|
|
|
|
|
|
should.not.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.currency);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('createCheckoutSetupSession sends currency if additionalPaymentMethods flag is on', async function () {
|
|
|
|
mockLabs.isSet.withArgs('additionalPaymentMethods').returns(true);
|
|
|
|
await api.createCheckoutSetupSession('priceId', {currency: 'usd'});
|
|
|
|
|
|
|
|
should.equal(mockStripe.checkout.sessions.create.firstCall.firstArg.currency, 'usd');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
describe('getCustomerIdByEmail', function () {
|
|
|
|
describe('when no customer is found', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
mockStripe = {
|
|
|
|
customers: {
|
|
|
|
search: sinon.stub().resolves({
|
|
|
|
data: []
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const mockStripeConstructor = sinon.stub().returns(mockStripe);
|
|
|
|
StripeAPI.__set__('Stripe', mockStripeConstructor);
|
|
|
|
api.configure({
|
|
|
|
secretKey: ''
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(function () {
|
|
|
|
sinon.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns null if customer exists', async function () {
|
|
|
|
const stripeCustomerId = await api.getCustomerIdByEmail(mockCustomerEmail);
|
|
|
|
|
|
|
|
should.equal(stripeCustomerId, null);
|
|
|
|
});
|
|
|
|
});
|
2023-01-06 07:44:56 +03:00
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
describe('when only one customer is found', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
mockStripe = {
|
|
|
|
customers: {
|
|
|
|
search: sinon.stub().resolves({
|
|
|
|
data: [{
|
|
|
|
id: mockCustomerId
|
|
|
|
}]
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const mockStripeConstructor = sinon.stub().returns(mockStripe);
|
|
|
|
StripeAPI.__set__('Stripe', mockStripeConstructor);
|
|
|
|
api.configure({
|
|
|
|
secretKey: ''
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(function () {
|
|
|
|
sinon.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns customer ID if customer exists', async function () {
|
|
|
|
const stripeCustomerId = await api.getCustomerIdByEmail(mockCustomerEmail);
|
|
|
|
|
|
|
|
should.equal(stripeCustomerId, mockCustomerId);
|
|
|
|
});
|
2023-01-06 07:44:56 +03:00
|
|
|
});
|
|
|
|
|
2023-07-13 14:20:54 +03:00
|
|
|
describe('when multiple customers are found', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
mockStripe = {
|
|
|
|
customers: {
|
|
|
|
search: sinon.stub().resolves({
|
|
|
|
data: [{
|
|
|
|
id: 'recent_customer_id',
|
|
|
|
subscriptions: {
|
|
|
|
data: [
|
2023-07-18 12:41:42 +03:00
|
|
|
{current_period_end: 1000},
|
|
|
|
{current_period_end: 9000}
|
2023-07-13 14:20:54 +03:00
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'customer_with_no_sub_id',
|
|
|
|
subscriptions: {
|
|
|
|
data: []
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'old_customer_id',
|
|
|
|
subscriptions: {
|
|
|
|
data: [
|
2023-07-18 12:41:42 +03:00
|
|
|
{current_period_end: 5000}
|
2023-07-13 14:20:54 +03:00
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const mockStripeConstructor = sinon.stub().returns(mockStripe);
|
|
|
|
StripeAPI.__set__('Stripe', mockStripeConstructor);
|
|
|
|
api.configure({
|
|
|
|
secretKey: ''
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(function () {
|
|
|
|
sinon.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns the customer with the most recent subscription', async function () {
|
|
|
|
const stripeCustomerId = await api.getCustomerIdByEmail(mockCustomerEmail);
|
|
|
|
|
|
|
|
should.equal(stripeCustomerId, 'recent_customer_id');
|
|
|
|
});
|
|
|
|
});
|
2023-01-06 07:44:56 +03:00
|
|
|
});
|
2023-10-20 10:52:08 +03:00
|
|
|
|
|
|
|
describe('cancelSubscriptionTrial', function () {
|
|
|
|
const mockSubscription = {
|
|
|
|
id: 'sub_123'
|
|
|
|
};
|
|
|
|
beforeEach(function () {
|
|
|
|
mockStripe = {
|
|
|
|
subscriptions: {
|
|
|
|
update: sinon.stub().resolves(mockSubscription)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const mockStripeConstructor = sinon.stub().returns(mockStripe);
|
|
|
|
StripeAPI.__set__('Stripe', mockStripeConstructor);
|
|
|
|
api.configure({
|
|
|
|
secretKey: ''
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(function () {
|
|
|
|
sinon.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('cancels a subscription trial', async function () {
|
|
|
|
const result = await api.cancelSubscriptionTrial(mockSubscription.id);
|
|
|
|
|
|
|
|
should.equal(mockStripe.subscriptions.update.callCount, 1);
|
|
|
|
|
|
|
|
should.equal(mockStripe.subscriptions.update.args[0][0], mockSubscription.id);
|
|
|
|
should.deepEqual(mockStripe.subscriptions.update.args[0][1], {trial_end: 'now'});
|
|
|
|
|
|
|
|
should.deepEqual(result, mockSubscription);
|
|
|
|
});
|
2024-05-16 03:47:23 +03:00
|
|
|
|
|
|
|
describe('createCheckoutSetupSession automatic tax flag', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
mockStripe = {
|
|
|
|
checkout: {
|
|
|
|
sessions: {
|
|
|
|
create: sinon.stub().resolves()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
customers: {
|
|
|
|
create: sinon.stub().resolves()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
sinon.stub(mockLabs, 'isSet');
|
|
|
|
mockLabs.isSet.withArgs('stripeAutomaticTax').returns(true);
|
|
|
|
const mockStripeConstructor = sinon.stub().returns(mockStripe);
|
|
|
|
StripeAPI.__set__('Stripe', mockStripeConstructor);
|
|
|
|
api.configure({
|
|
|
|
checkoutSessionSuccessUrl: '/success',
|
|
|
|
checkoutSessionCancelUrl: '/cancel',
|
|
|
|
checkoutSetupSessionSuccessUrl: '/setup-success',
|
|
|
|
checkoutSetupSessionCancelUrl: '/setup-cancel',
|
|
|
|
secretKey: '',
|
|
|
|
enableAutomaticTax: true
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(function () {
|
|
|
|
sinon.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('createCheckoutSession adds customer_update if automatic tax flag is enabled and customer is not undefined', async function () {
|
|
|
|
const mockCustomer = {
|
|
|
|
id: mockCustomerId,
|
|
|
|
customer_email: mockCustomerEmail,
|
|
|
|
name: 'Example Customer'
|
|
|
|
};
|
|
|
|
|
|
|
|
await api.createCheckoutSession('priceId', mockCustomer, {
|
|
|
|
trialDays: null
|
|
|
|
});
|
|
|
|
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.customer_update);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('createCheckoutSession does not add customer_update if automatic tax flag is enabled and customer is undefined', async function () {
|
|
|
|
await api.createCheckoutSession('priceId', undefined, {
|
|
|
|
trialDays: null
|
|
|
|
});
|
|
|
|
should.not.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.customer_update);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('createCheckoutSession does not add customer_update if automatic tax flag is disabled', async function () {
|
|
|
|
const mockCustomer = {
|
|
|
|
id: mockCustomerId,
|
|
|
|
customer_email: mockCustomerEmail,
|
|
|
|
name: 'Example Customer'
|
|
|
|
};
|
|
|
|
// set enableAutomaticTax: false
|
|
|
|
api.configure({
|
|
|
|
checkoutSessionSuccessUrl: '/success',
|
|
|
|
checkoutSessionCancelUrl: '/cancel',
|
|
|
|
checkoutSetupSessionSuccessUrl: '/setup-success',
|
|
|
|
checkoutSetupSessionCancelUrl: '/setup-cancel',
|
|
|
|
secretKey: '',
|
|
|
|
enableAutomaticTax: false
|
|
|
|
});
|
|
|
|
await api.createCheckoutSession('priceId', mockCustomer, {
|
|
|
|
trialDays: null
|
|
|
|
});
|
|
|
|
should.not.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.customer_update);
|
|
|
|
});
|
|
|
|
});
|
2023-10-20 10:52:08 +03:00
|
|
|
});
|
2022-02-09 16:00:39 +03:00
|
|
|
});
|