2

QUESTION:

I am currently using a webhook: when the checkout.session.completed event is received, the user is redirected to the success_url by default even if I try to redirect him to another page with res.redirect("/page") (Node.js). Is there truly no way to redirect him to another page in case my fulfilment code fails ? (That is: the payment succeeds, but the fulfilment code fails, so redirecting to success_url is not appropriate)


REFERENCE:

https://stripe.com/docs/payments/checkout/one-time

https://stripe.com/docs/payments/checkout/fulfillment#webhooks

"The checkout.session.completed webhook is sent to your server before your customer is redirected. Your webhook acknowledgement (any 2xx status code) will trigger the customer’s redirect to the success_url"


WHAT I TRIED:

Chaining Express.js 4's res.status(401) to a redirect

So if I were to send a 4xx status code, shouldn't that prevent the success_url redirect ? That is indeed what I observe.

But I can't seem to redirect to another page though. I tried: 

res.status(401).location('/submit/wait/').end();
res.redirect(401, '/submit/wait/');
res.set('Content-Type', 'text/html');
res.status(401).send('<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=/submit/wait"></head></html>`);Is there really no way to then just redirect to another page ?  

CODE

router.post('/webhook', bodyParser.raw({type: 'application/json'}), (req, res) => {

    const sig = req.headers['stripe-signature'];
    let event;

    try {
        event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret);
    } catch (err) {
        return res.status(400).send(`Webhook Error: ${err.message}`);
    }

    if (event.type === 'checkout.session.completed') {

        const session = event.data.object;

        (async () => {

            try {
                //FULFIL ORDER CODE

            } catch(e) {
                console.log("ERROR FULFILLING ORDER: "+e);


                res.status(401).location('/submit/wait/').end();

            }
        })();
    }
    else {
        console.log("DEFAULT RESPONSE");

        // Return a response to acknowledge receipt of the event
        res.json({received: true});
    }

});
TheProgrammer
  • 1,409
  • 4
  • 24
  • 53

1 Answers1

6

The checkout.session.completed event sent to your webhook endpoint is only there to let you know the Checkout Session completed. You cannot use the event notification to alter the Checkout Session in any way beyond responding with success or failure.

The reason the client redirect is delayed until you respond to the checkout.session.completed event is to allow you to perform fulfillment tasks on your server before the user is redirected to the success_url (like updating your database, making other API calls, etc.). This ensures the customer lands on a page that reflects their successful purchase.

The success_url itself cannot be changed at this point in the process, but you can set up a page on your server that will redirect the user to whatever page you wish based on the Checkout Session ID and set the success_url to that page. You can include the Checkout Session ID in your success_url using the {CHECKOUT_SESSION_ID} placeholder.

Justin Michael
  • 5,634
  • 1
  • 29
  • 32
  • That would mean I need to save the sessionID in my database in a collection "Failed Fulfillments" for example in case the fulfilment failed so that when my successURL loads with the checkout session ID parameter, I can then call my database to check if that session ID has been tied to a failed fulfilment. That would work. It's not a very elegant solution imo, but it works. I feel like Stripe should have a better way to handle this :/ – TheProgrammer Feb 24 '20 at 23:40
  • There's no need to save every Checkout Session ID in your database. You can just save the successful ones. Here's the flow: 1. Customer completes Checkout successfully. 2. Stripe sends your server the `checkout.session.completed` event. 3. You save it to your DB and respond with a 2xx status. 4. Stripe redirects. 5. You can now look for a matching Checkout Session ID when they land on your `success_url` page. This flow is why Stripe waits to redirect; to give you a chance to save everything and whatnot. – Justin Michael Feb 25 '20 at 17:32
  • That's what I did (sorry if it wasn't clear) : the checkout.session.completed event is received only if the payment was successful. – TheProgrammer Feb 25 '20 at 21:24