Skip to content

Commit

Permalink
馃悰 Fixed updating payment method when beta flag is on (#20171)
Browse files Browse the repository at this point in the history
refs https://linear.app/tryghost/issue/ONC-20
refs https://linear.app/tryghost/issue/ENG-867

- when using dynamic payment methods in Stripe, we need to provide a
currency. Stripe uses that parameter to determine which payment methods
to render
- docs: https://docs.stripe.com/api/checkout/sessions/create
  • Loading branch information
sagzy committed May 8, 2024
1 parent 000616a commit 5b69476
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 10 deletions.
6 changes: 5 additions & 1 deletion ghost/members-api/lib/controllers/RouterController.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,14 @@ module.exports = class RouterController {
customer = await this._stripeAPIService.getCustomer(subscription.get('customer_id'));
}

const defaultTier = await this._tiersService.api.readDefaultTier();
const currency = defaultTier?.currency?.toLowerCase() || 'usd';

const session = await this._stripeAPIService.createCheckoutSetupSession(customer, {
successUrl: req.body.successUrl,
cancelUrl: req.body.cancelUrl,
subscription_id: req.body.subscription_id
subscription_id: req.body.subscription_id,
currency
});
const publicKey = this._stripeAPIService.getPublicKey();
const sessionInfo = {
Expand Down
10 changes: 8 additions & 2 deletions ghost/stripe/lib/StripeAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,9 @@ module.exports = class StripeAPI {
/**
* @param {ICustomer} customer
* @param {object} options
*
* @param {string} options.successUrl
* @param {string} options.cancelUrl
* @param {string} options.currency - 3-letter ISO code in lowercase, e.g. `usd`
* @returns {Promise<import('stripe').Stripe.Checkout.Session>}
*/
async createCheckoutSetupSession(customer, options) {
Expand All @@ -571,7 +573,11 @@ module.exports = class StripeAPI {
metadata: {
customer_id: customer.id
}
}
},

// Note: this is required for dynamic payment methods
// https://docs.stripe.com/api/checkout/sessions/create#create_checkout_session-currency
currency: this.labs.isSet('additionalPaymentMethods') ? options.currency : undefined
});

return session;
Expand Down
54 changes: 47 additions & 7 deletions ghost/stripe/test/unit/lib/StripeAPI.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,6 @@ describe('StripeAPI', function () {
should.exist(mockStripe.checkout.sessions.create.firstCall.firstArg.cancel_url);
});

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('sets valid trialDays', async function () {
await api.createCheckoutSession('priceId', null, {
trialDays: 12
Expand Down Expand Up @@ -162,6 +155,53 @@ describe('StripeAPI', function () {
});
});

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');
});
});

describe('getCustomerIdByEmail', function () {
describe('when no customer is found', function () {
beforeEach(function () {
Expand Down

0 comments on commit 5b69476

Please sign in to comment.