web #stripe #javascript #fetch #node.js

Redirecting After a Stripe Payment - Thanks for your order!

Jan 5 '19 · 10 min read · 1952

A problem I came across myself recently, which requires a deeper understanding of POST requests and the Fetch API.

The Stripe API

T

aking payments used to be notoriously difficult, but these days there are a multitude of easy to use payment gateways with extensive APIs for developers - web and mobile - to take advantage of.

Stripe is one of the more popular payment gateways. It is considered one of the best platforms for running an e-commerce platform or custom e-commerce application. Founded in 2011 with seed money from Y Combinator, Stripe now handles billions of dollars every year. Their customers include; Kickstarter, Instacart, Pinterest, Lyft, Shopify, Slack etc.

I think one of the reasons why they are so successful is their really easy to follow API documentation, which makes getting up and running and start accepting your first payments a breeze.

Example thanks for your order page

Stripe Custom Checkout

Checkout is pre-built UI for accepting secure payments, which is integrated at the most basic level using a form. To have more programmatic control over how the application sends the payment token to the server, Stripe has a tutorial of how to create a custom checkout using the W3C Fetch API using JSON data instead of form encoding. I'll assume that because you're reading this you will have already implemented the code on the Stripe website.

A problem arises when you would like to redirect the user to a page with a "Thanks for your order!" message instead of sending data back with the Fetch POST request. See this SO discussion for more. For example, you might want to modify the Stripe code to something like below:

app.post("/charge", (req, res) => {
  const {amount} = req.body;

  stripe.customers.create({
    email: req.body.email,
    card: req.body.id
  })
  .then(customer =>
    stripe.charges.create({
      amount,
      description: req.body.description,
      currency: "usd",
      customer: customer.id
    }))
    .then(() => {
      res.redirect(`/success/${req.body.email}`);
    }).catch(err => {
    console.log("Error:", err);
    res.status(500).send({error: "Purchase Failed"});
  });
});

app.get('/success/:email', (req, res) => {
  res.render('success.hbs', {
    email: req.params.email
  })
});

The issue comes from how POST requests using AJAX and the Fetch API work in comparison to form submissions. When using a form submission to a specific end-point, browsers have default ways of handling the response, such as redirecting to a specified endpoint after submission.

The res.redirect('/some/url') sends the URL to redirect to back to the browser as res.url. If the original request to the server was made using a form submission, then the browser knows to navigate to this new URL that it receives, likewise with GET requests using anchor links - the browser is expecting to navigate to a new page.

AJAX and Fetch requests, on the other hand, don't have a default implementation of what to do with the response, and it is up to you to provide that information in the 'then' block of your fetch request. This is why if you do not tell the browser what you want to do with the redirect response, it won't do anything.

To make the browser redirect, your POST /charge fetch request might look like the following, which uses window.location.replace() to manually navigate the browser to the redirect URL set on res.url:

fetch("/charge", {
    method: "POST",
    headers: {"Content-Type": "application/json"},
    body: JSON.stringify(token)
  }).then(res => {
    window.location.replace(res.url);
  });