Dashiell Rose Bark-Huss
Dashiell Rose Bark-Huss

Reputation: 2975

How to handle the success URL on Stripe considering GET requests should be safe?

When we make a stripe checkout session we include a success url:

session = await this.stripe.checkout.sessions.create({
    payment_method_types: ['card'],
    line_items: lineItems,
    payment_intent_data: {
        transfer_data: {
            amount: 9999999,
            destination: someaccountId,
        },
    },
    success_url: `http://localhost:4000/api/checkout/success?true&session_id={CHECKOUT_SESSION_ID}&alias_id=${aliasId}`, 
    cancel_url: `http://localhost:4000/api/checkout/canceled?session_id={CHECKOUT_SESSION_ID}`,
});

The success URL is where stripe sends the user after a successful payment. It's a GET request since stripe is redirecting the user. Many apps using stripe will need to take actions after a successful checkout- sending an email receipt, notifications, sending paid content, updating the order in the database etc. But it's suggested not to do these actions in GET requests because GET requests are supposed to be idempotent and safe.

For example an unsubscribe link in an email should not unsubscribe a user but instead the "proper approach for unsubscribe links is to lead to a page where the user can click a button to unsubscribe (where the button click triggers a POST request)."src This is because "Many, many, many tools, utilities, web crawlers and other thingamajiggies assume that GET will never be a destructive action (rightly so, since it's specified this way). If you now break your application by breaking that specification, you'll get to keep both parts of your application." src

So I was wondering what is the proper way to handle the stripe success url? If we follow the suggested advice above, then the success url would link to a page where the user clicks a button that updates the order, emails a receipt, etc. But then we are relying on customer to finish the order that has already been paid for. If they don't press that button then important actions aren't completed. What is the proper way to do this? Or does the suggestion to not change the database on a GET request not apply for some reason to these type of actions?

Upvotes: 9

Views: 18278

Answers (3)

matshidis
matshidis

Reputation: 558

The best way to manage taking action after a successful checkout is NOT on the HTTPS client side but in on the server side by listening to webhook events and verifying that the webhook payload does indeed come from Stripe - by checking the web-hook secret, signature etc.

If you handle checkout confirmation on the client side - your website can be compromised -- technically I can redirect myself to any URL and activate a button, even bots can do that.

You can register a webhook on the dashboard and use that for order verifications. Read more here: https://stripe.com/docs/webhooks

Upvotes: 2

Omkar Joshi
Omkar Joshi

Reputation: 131

If you are using a stripe template then you need to follow this step

Stripe Pricing Table -> edit -> Click on Don't show confirmation page -> add your web url (https://test.com/success?session_id={CHECKOUT_SESSION_ID}.

Stripe will update the checkout session-id for you

Upvotes: 2

floatingLomas
floatingLomas

Reputation: 8747

Make the part of that page that handles the Checkout Session code idempotent - i.e. have it check first to see if its steps have already been processed (and in that case skip), or else make it so whatever processing it does could be repeated multiple times without having any additional effect after the first time it runs.

For "tools, utilities, web crawlers and other thingamajiggies" to hit your URL with a valid Checkout Session ID would be pretty close to impossible, so whatever code you use to handle a 'bad session ID' would handle that just fine.

You should also have a webhook for this - which would get a POST request. https://stripe.com/docs/payments/checkout/fulfill-orders#handle-the---event

Upvotes: 3

Related Questions