From d751d648c72d22360244fd13c78908d43c7d2038 Mon Sep 17 00:00:00 2001 From: Sag Date: Tue, 4 Jun 2024 12:27:45 +0200 Subject: [PATCH] Fixed offer not found case during Stripe checkout (#20322) fixes https://linear.app/tryghost/issue/SLO-135 - handles edge cases when an invalid `offerId` is provided during Stripe checkout --- ghost/core/test/e2e-frontend/members.test.js | 7 +++--- .../lib/controllers/RouterController.js | 9 ++++++++ .../test/unit/lib/controllers/router.test.js | 23 +++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/ghost/core/test/e2e-frontend/members.test.js b/ghost/core/test/e2e-frontend/members.test.js index ae86c95c70..99d77df355 100644 --- a/ghost/core/test/e2e-frontend/members.test.js +++ b/ghost/core/test/e2e-frontend/members.test.js @@ -125,11 +125,10 @@ describe('Front-end members behavior', function () { .expect(400); }); - //TODO: Remove 500 expect once tests are wired up with Stripe - it('should not throw 400 for using offer id on members create checkout session endpoint', async function () { + it('should error for using an invalid offer id on members create checkout session endpoint', async function () { await request.post('/members/api/create-stripe-checkout-session') .send({ - offerId: '62826b1b6dccb3e3e997ebd4', + offerId: 'invalid', identity: null, metadata: { name: 'Jamie Larsen' @@ -139,7 +138,7 @@ describe('Front-end members behavior', function () { tierId: null, cadence: null }) - .expect(500); + .expect(400); }); it('should error for invalid data on members create update session endpoint', async function () { diff --git a/ghost/members-api/lib/controllers/RouterController.js b/ghost/members-api/lib/controllers/RouterController.js index a900bb3314..b5af1d8599 100644 --- a/ghost/members-api/lib/controllers/RouterController.js +++ b/ghost/members-api/lib/controllers/RouterController.js @@ -8,6 +8,7 @@ const messages = { emailRequired: 'Email is required.', badRequest: 'Bad Request.', notFound: 'Not Found.', + offerNotFound: 'This offer does not exist.', offerArchived: 'This offer is archived.', tierArchived: 'This tier is archived.', existingSubscription: 'A subscription exists for this Member.', @@ -247,6 +248,14 @@ module.exports = class RouterController { // Fetch tier and offer if (offerId) { offer = await this._offersAPI.getOffer({id: offerId}); + + if (!offer) { + throw new BadRequestError({ + message: tpl(messages.offerNotFound), + context: 'Offer with id "' + offerId + '" not found' + }); + } + tier = await this._tiersService.api.read(offer.tier.id); cadence = offer.cadence; } else { diff --git a/ghost/members-api/test/unit/lib/controllers/router.test.js b/ghost/members-api/test/unit/lib/controllers/router.test.js index 4b54322721..ef1d5dd7fb 100644 --- a/ghost/members-api/test/unit/lib/controllers/router.test.js +++ b/ghost/members-api/test/unit/lib/controllers/router.test.js @@ -161,6 +161,29 @@ describe('RouterController', function () { assert.equal(error.context, 'Expected cadence to be "month" or "year", received "day"'); } }); + + it('returns a BadRequestError if offer is not found', async function () { + offersAPI = { + getOffer: sinon.stub().resolves(null) + }; + + const routerController = new RouterController({ + tiersService, + paymentsService, + offersAPI, + stripeAPIService, + labsService + }); + + try { + await routerController._getSubscriptionCheckoutData({offerId: 'invalid'}); + + assert.fail('Expected function to throw BadRequestError'); + } catch (error) { + assert(error instanceof errors.BadRequestError, 'Error should be an instance of BadRequestError'); + assert.equal(error.context, 'Offer with id "invalid" not found'); + } + }); }); afterEach(function () {