Headless WooCommerce & Next.js: Set up Stripe and Create Checkout

Leo Chan
Geek Culture
Published in
9 min readAug 4, 2021
Photo by Dan Smedley on Unsplash

The last piece of this jigsaw puzzle is to set up a Stripe account and use it to handle card payments in our checkout.

Create Stripe Account

First thing’s first — you’ll need to register for a Stripe account, which is free to do, and thereafter you will have access to development API keys for test purposes. The Stripe dashboard is pretty handy for developers as it lets you see a log of activity so you can check if things are working or why it isn’t working. Stripe also provides a comprehensive list of test cards you can use to test a variety of scenarios (e.g. insufficient funds, fraudulent card).

Back to the development API keys. You are looking for the ‘publishable key’ and the ‘secret key’. If you can’t see it on the dashboard then browse the menu option: Developers -> API Keys. I store these keys as environment variables within my app. The publishable key doesn’t have to be secret so I prefix this with NEXT_PUBLIC_ but I make sure the secret key is only used server-side.

Install Stripe for WooCommerce Plugin

WooCommerce has a Stripe plugin called ‘WooCommerce Stripe Gateway’ and this integrates Stripe for you. Go to your WordPress site and download this plugin and activate it.

After installing and activating the plugin, you’ll find Stripe settings under WooCommerce -> Settings -> Payments. Here we want to ensure the checkboxes are checked for Enable Stripe and Enable Test Mode. Then enter your Test Publishable Key and your Test Secret Key. Click Save Changes at the bottom of the screen!

If you have a WordPress site that is served over HTTPS and not your http://localhost then follow the instructions for adding webhook endpoints to your Stripe account. This is really useful as it allows Stripe to communicate with your WooCommerce orders. We can omit this for testing but it’s definitely worth configuring for production.

Photo by Meghan Rodgers on Unsplash

Use the Stripe SDKs

Stripe has a React specific SDK and a tutorial for how to set it up. If that tutorial is not enough then Stripe has another in-depth tutorial for how to set up custom payment flows, including what you need to include server-side and client-side.

If you want to follow along with me instead then these are the packages I used:

yarn add @stripe/react-stripe-js --dev
yarn add @stripe/stripe-js -- dev
yarn add stripe --dev

Stripe Elements Provider

In order to capture card details securely we will be relying on Stripe Elements. Stripe Elements are components that ‘provide a flexible way to securely collect payment information in your React app’. The bottom line is you can rely on Stripe to handle the sensitive data securely without it ever touching your servers.

We will eventually have a checkout form component where we want to use the Stripe Elements. To enable us to use the Stripe Elements we need to wrap the checkout form component with the Elements provider.

Notice we also use the loadStripe method to create a Stripe object with our publishable key. This Stripe object gets passed into our provider so we know which Stripe account we are associated with.

Stripe Elements Components

There are a number of different Stripe Elements components we can use and a popular one is CardElement which provides an all-in-one component to capture all necessary card details. Easy.

However, I want to show you how you can use other elements to create different UI. For this, I want to use CardNumberElement, CardExpiryElement and CardCvcElement so I can still capture these essential card details but I have flexibility over how to display it.

To give you extra flexibility in the UI you can give a className to the components and style it using regular CSS. This is useful because the Stripe style object is limited. Do check the Stripe docs to see what you can play with in their style object.

We now have a way to capture card details securely so the next thing to tackle is actually using the card to pay for something.

The Checkout Flow

We are in a scenario where a user has something in their cart and they would like to checkout and buy it using a card.

1. Create Stripe Payment Intent

The first thing we need to do is create a Stripe Payment Intent. Fundamentally, all we need to create a Payment Intent is the amount and the currency. We then use a Stripe method to create a Payment Intent that successfully returns an object that includes the client_secret. This client_secret is what we need to complete the payment later.

Crucially, we want to calculate the amount server-side. The reason for doing this is so the amount we pass into the Stripe Payment Intent cannot easily be tampered with. Our cart already contains the WooCommerce product IDs and quantity so I use this information to fetch the price from WooCommerce on the server side and calculate the grand total there. I created create-payment-intent.ts in the \pages\api folder in order to create a custom API route. This method of creating API routes is unique to Next.js (but you can see how easy it is).

IMPORTANT NOTE: Whilst it’s not immediately noticeable in the code above, the amount is in a specific format where £1.00 is represented as 100. This is common when dealing with monetary values. With Javascript there’s an annoying issue with floating point rounding errors which sometimes leads to incorrect maths when making calculations on float numbers. In our example it’s likely to mean you could be a penny out in some instances, which is a small amount, but there shouldn’t be any discrepancies. Converting float amounts like £1.00 to an integer like 100 avoids these issues.

In the code above I am looking to return the Payment Intent ID and the Client Secret. Even though we will be using these later we want to avoid saving the Client Secret where it could be easily retrieved by a malicious user.

2. Create WooCommerce Order

We covered how to create WooCommerce orders previously and there is no material difference here except we have the Stripe Payment Intent ID which we can pass into the meta_data field. The reason for doing this is so there is a connection between this specific order and a specific Stripe payment intent. When webhooks are setup and you are using a production version of WordPress/WooCommerce you can refund payments directly from the WooCommerce dashboard rather than the Stripe dashboard because the order is linked to a specific Payment Intent and the WooCommerce Stripe Gateway enables you to process the refund.

const data: Order = {
payment_method: "stripe",
payment_method_title: "Card",
set_paid: false,
line_items: lineItems,
meta_data: [
{
key: "_stripe_intent_id",
value: paymentIntentId,
},
],
};

The code above is an example of the format you must adhere to when including the Payment Intent ID as meta_data. Note it is an object in an array. The key _stripe_intent_id is specific and the value is the Payment Intent ID you received earlier.

Now we have actually associated our WooCommerce order with our Stripe Payment Intent.

3. Confirm Stripe Payment

We have created a Stripe Payment Intent and specified the amount of money we want to process. The next thing to do is capture the card details and confirm the payment against the Payment Intent.

We have our Stripe Element components in place to capture the card details so how do we actually obtain the data it has collected? To do this we use the useElements hook. Instantiate it with const elements = useElements().

If you are using the all-in-one Stripe Element component CardElement then you can get the card details with const cardElement = elements.getElement(CardElement). In our example, I have used three different components: CardNumberElement, CardExpiryElement and CardCvcElement. How then do we get the card details?

Actually, it’s as easy as it is with CardElement. Instead of passing in CardElement we pass in CardNumberElement and Stripe knows to get the data from the other components.

We have the client_secret from the Payment Intent. We have the cardElement containing our card details. Now we’re ready to confirm the payment.

To do this we use the useStripe hook. Import it and instantiate it with const stripe = useStripe() and then we can use the confirmCardPayment method.

confirmCardPayment takes two arguments: client secret and payment method. We have the client secret but we do not have the Stripe PaymentMethod object fully yet.

The basics of the payment method object is to pass the cardElement we obtained earlier. This is the basic payment method object we can pass into confirmCardPayment.

{
payment_method: {
card: cardElement,
},
}

Of course, you can add more detail as necessary (receipt_email, billing_details and shipping are often handy to include).

// create Stripe confirm payment method data
// TODO add more data for Stripe to hold if necessary. Receipt email is usually a good idea
const paymentMethod = {
payment_method: {
card: elements.getElement(CardNumberElement)!,
// billing_details: {},
// shipping: {},
// receipt_email: ''
},
};
// use Stripe client secret to process card payment method
try {
const result = await stripe.confirmCardPayment(clientSecret, paymentMethod);
if (result.error) {
throw new Error(result.error.message);
}
return result;
} catch (error) {
throw new Error(error);
}

One you’ve successfully called stripe.confirmCardPayment(clientSecret, paymentMethod) then that’s it! The card has been charged and you have already created a WooCommerce order linked to the Stripe Payment Intent.

Next Steps

There are quite a few next steps you can consider. These are some that I have included in my code and you’re welcome to check out how I did it on GitHub.

Card Payment Form Validation — take advantage of Stripe’s error handling to get real-time feedback on whether the card details are incorrect.

Loading Indicator — creating the Stripe Payment Intent, WooCommerce order and confirming the payment can take a moment so it’s a good idea to lock out the form with some sort of loading indicator.

Reset Redux Cart — after successfully completing a transaction, reset the Redux cart state.

Stripe Webhooks — remember you can only use this when your WordPress/WooCommerce site is served over HTTPS. However, it is worth doing because you can be notified when a card payment is successful and use the webhook to trigger a function to update the status of your WooCommerce order.

GraphQl — instead of using WooCommerce REST API you can use GraphQl and it is well worth it. You can get the data you need across multiple API endpoints in one query and be efficient in what you need and omit what you don’t.

WooCommerce Products — so far we explored simple products. However, you can try out variations in products and see how you can use this instead. You will get a variation_id as well as a product_id and you may also need to use the /variations endpoint with the WooCommerce REST API in order to retrieve variations.

Responsive Design — the code I’ve presented only looks good on mobile right now. We want to add breakpoints and media queries to create a responsive app.

Final Words

I hope you enjoyed this series of articles introducing you to Headless WooCommerce and Next.js. I have tried to show you the pared back version of how to achieve a basic e-commerce platform and there are many layers you can add on top. As with everything, there will inevitably be frustrations along the way and I might have overlooked some important details in these articles. I’d be happy to help however I can so look me up and let’s connect.

For those of you who have persevered this far: here is a link to my public GitHub with the code for this woocommerce-nextjs example.

--

--

Leo Chan
Geek Culture

Lockdown coder: transformation from non-coder to coder in under 12 months…complete-ish.