Zero
Zero
Back

The Quickest Way to Set Up Stripe in a Web App

This article shows how to get started using Stripe to accept payments in a Next.js application, using the Zero TypeScript SDK to fetch the Stripe API key.

Sam Magura

Sam Magura

Side of a minimalist building

This article shows how to get started using Stripe to accept payments in a Next.js application, using the Zero TypeScript SDK  to fetch the Stripe API key. Zero is a secrets manager which allows you to retrieve API keys and other secrets from the cloud using a single secure token, called a Zero token. Zero makes it so the Next.js application only needs to be configured with the Zero token — the Stripe API key is not stored in the repository or an environment variable.

🔗 The full code for this example is available in the zerosecrets/examples  GitHub repository.

Sign Up for Stripe

To complete this example, you will need an activated Stripe account. Sign up at Stripe.com  and follow the wizard for activating your account so that you can accept payments. We won't be doing any real transactions in this example, but it is still necessary to provide the name of your business and your bank account information. You can use your first & last name as the business name, and leave a note in the description field that says you are simply evaluating the Stripe API.

Once your account is activated, make sure to switch to test mode using the toggle in the upper right:

Enable test mode in Stripe

The Stripe publisable key and secret key will be stored in Zero, so please sign up for Zero if you have not done so already. Once you are logged in to the Zero web console, create a new Zero token, and click "Add secret" and select Stripe from the dropdown. Then copy-paste the Stripe publishable and secret keys into Zero. These keys can be found on the homepage of the Stripe dashboard:

Copying the Stripe secret key

To test a payment, we'll need to create a product in Stripe. Click the "Products" link in the navigation bar and then press the "Add product" button. Enter a name for the product and set the cost to a one time payment of $20.00. Once the product is created, you'll be able to copy its price ID which should look similar to price_1LYuqlBzNL28s33DQYwQxFx7. This price ID is not a secret and can safely be committed to the codebase, which we'll set up in the next section.

Creating the Next.js Web App

Let's create a new Next.js application using TypeScript by running

1
npx create-next-app nextjs-stripe --ts --use-npm
shell

Start by deleting the boilerplate JSX, CSS, and API route that is part of the default Next.js template so that we can begin from a clean slate. Replace the contents of globals.css with this stylesheet  which is adapted from this example project  from the Stripe docs.

Then update index.tsx to display a product with a checkout link:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// See https://github.com/zerosecrets/examples/tree/main/nextjs-stripe
// for the full source code.

const ProductDisplay: React.FC = () => (
  <section>
    (product description here)
    <form action="/api/createCheckoutSession" method="POST">
      <button type="submit">Checkout</button>
    </form>
  </section>
)

interface HomeProps {
  message: string | null
}

export const getServerSideProps: GetServerSideProps<HomeProps> = async (context) => {
  let message: string | null = null

  // Check to see if this is a redirect back from Checkout
  if (context.query['success']) {
    message = 'Order placed! You will receive an email confirmation.'
  }

  if (context.query['canceled']) {
    message = "Order canceled -- continue to shop around and checkout when you're ready."
  }

  return {props: {message}}
}

const Home: NextPage<HomeProps> = ({message}) => {
  return (
    <div>
      <Head>
        <title>Next.js + Stripe + Zero</title>
      </Head>

      <h1>Next.js + Stripe + Zero</h1>

      {message ? <p>{message}</p> : <ProductDisplay />}
    </div>
  )
}

export default Home
typescript

There's a fair amount going on here, so let's break it down. The ProductDisplay component renders the product name, price, and thumbnail image and has a submit button which will post to the API route /api/createCheckoutSession. This API route doesn't exist yet, but we will create it in a moment. Once complete, the API route should redirect the user to a prebuilt Stripe checkout page.

The other complex part of the code is the getServerSideProps function. This function inspects the URL search params and sets the message prop accordingly. Depending on the search params, message may be set to a "payment successful" message, a "payment canceled" message, or undefined, which is the default before the user has attempted to make a purchase.

Run the site with npm run dev and you'll see our simple product page:

Product page

Creating a Checkout Session in Stripe

Currently, clicking the "Checkout" button will result in a 404 error. Let's remedy this by creating a new Next.js API route :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const DOMAIN = process.env.DOMAIN

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const stripe = await getStripeClient()

  const session = await stripe.checkout.sessions.create({
    line_items: [
      {
        // This is a price ID copied from the Stripe Dashboard
        price: 'price_1LYuqlBzNL28s33DQYwQxFx7',
        quantity: 1,
      },
    ],
    mode: 'payment',
    success_url: `${MY_DOMAIN}?success=true`,
    cancel_url: `${MY_DOMAIN}?canceled=true`,
  })

  if (!session.url) {
    throw new Error('session.url is null.')
  }

  res.redirect(303, session.url)
}
typescript

This API route is relatively straightforward — it uses a Stripe API client (which we'll create next) to create a new checkout session using the price ID for the product we created earlier in the Stripe Dashboard. It then redirects the user to Stripe, which will render a prebuilt checkout page. If the checkout succeeds, Stripe will redirect back to our site with success=true in the URL.

Creating the Stripe API Client

We still need to fill in the getStripeClient function. We'll need the official Stripe npm package as well as the Zero SDK, since the Zero SDK will be used to fetch the Stripe secret key. These packages can be installed with:

1
npm install stripe @zerosecrets/zero
shell

getStripeClient could be used by multiple parts of our application, so we'll place it in a new directory named util. The code looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import Stripe from 'stripe'

import {zero} from '@zerosecrets/zero'

let stripe: Stripe | undefined

export async function getStripeClient(): Promise<Stripe> {
  // Reuse the same Stripe client if one has already been created, so that we
  // don't call Zero on every request
  if (stripe) {
    return stripe
  }

  if (!process.env.ZERO_TOKEN) {
    throw new Error('Did you forget to set the ZERO_TOKEN environment variable?')
  }

  const secrets = await zero({
    token: process.env.ZERO_TOKEN,
    pick: ['stripe'],
  }).fetch()

  if (!secrets.stripe) {
    throw new Error('Did not receive an API key for SendGrid.')
  }

  stripe = new Stripe(secrets.stripe.secret_api_key, {
    apiVersion: '2022-08-01',
  })

  return stripe
}
typescript

First, we call Zero with our Zero token to get the Stripe secret key. Then, we instantiate a new instance of the Stripe API client and return it.

You can test the website by locally by running

1
ZERO_TOKEN=<YOUR_ZERO_TOKEN> npm run dev
shell

This syntax sets your Zero token as an environment variable which can then be accessed from JavaScript as process.env.ZERO_TOKEN. Don't commit your Zero token to the git repository — your Zero token must remain secret since it provides access to your Stripe account.

If everything is set up correctly, you will be able to "purchase" the mock product. When redirected to the Stripe checkout page, enter 4242 4242 4242 4242 as the card number to simulate a successful payment. The expiration date can be any future date, and the security code can be any three digit number. To test a canceled payment, click the small back button in the upper left of the Stripe checkout page.

Deployment and Next Steps

This article showed how to quickly integrate a Next.js web application with the Stripe API, using the Zero secrets manager to store the Stripe private key. If this were a production application, the next step would be to deploy the app to the cloud. The easiest way to deploy a Next.js application is Vercel  — Vercel is the company that makes Next. Vercel has a generous free plan and makes deployment a breeze, especially for Next.js. The application we coded should work on Vercel with no changes, except that the Zero token and your site's domain name must be defined as environment variables  in the Vercel dashboard. Happy coding!