How to use Stripe with Hugo to take payments for an e-book, course or software

Save on fees, offer Google Pay and Apple Pay automatically, and gain valuable Stripe experience.

Ron Erdos
Updated February 13, 2021

I’m very excited about today’s topic—creating your own lightweight e-commerce store with Stripe Checkout and Hugo.

Before we get into the “how to”, let’s take a look at the reasons you might want to do this.

Why Stripe Checkout can be a good option for Hugo sites

Benefit #1: Save on fees

When you use Stripe’s Checkout product—v.s. a third-party checkout solution—you can save a fair bit of money.

For example, as an Australian, Stripe charges me 1.75% + 30c per domestic card transaction, and 2.9% + 30c for purchases made with international credit cards. Pretty fair and reasonable.

However, if I were to use a third-party, Hugo-compatible, shopping cart solution such as Gumroad, I’d pay 8.5% + 30c per transaction (less if I paid a monthly fee). That’s an extra “tax” of almost 7% for domestic purchases.

Other “drop in” shopping carts you could use with Hugo, such as Selz and Sellfy don’t even have transaction-only pricing. You’ll be paying $29 USD per month (less on the yearly plan) just to use their services, even if you don’t sell anything. When you’re starting out, that’s far from ideal.

Benefit #2: Offer Apple Pay and Google Pay out of the box

When you use Stripe Checkout, you don’t need to configure anything to accept Apple Pay and Google Pay, other than to enable these options in your settings (more on this later).

Benefit #3: Learn Stripe

As a developer, it may well be in your interest to get first-hand experience using Stripe.

It’s ubiquitous, and their documentation is as great as everyone says.

You never know when a bit of Stripe experience will come in handy in your career!

In fact, that was part of my motivation in using Stripe directly to take payments for my SEO Theme for Hugo on this site, and in writing this tutorial. After all, teaching is a great way to learn something thoroughly.

Okay, that’s the “why”, now let’s look at the “what”.

What we’ll be creating in this tutorial

In Part 1, we’ll sign up for Stripe and create our product object in their UI, where we set its price, currency and so forth.

In Part 2, we’ll create two Hugo pages, one for successful orders; the other for cancelled or failed orders. The customer will see one of these pages after completing Stripe’s Checkout.

In Part 3, we’ll create the Stripe Checkout button and add it to your Hugo site. When users click it, they’ll be taken to checkout.stripe.com to provide their details and make payment using a credit card, Apple Pay, or Google Pay.

In Part 4, we’ll test our test implementation of the Stripe Checkout button.

In Part 5, we’ll use “internet duct tape” service Zapier to set up an automated email for customers, thanking them for their purchase. This email will also contain a Dropbox link to the digital product, such as an ebook, video file, or zipped software package.

And finally, in Part 6, we’ll test our production implementation of Stripe Checkout.

Physical goods and shopping carts are out of scope for this tutorial

This tutorial doesn’t cover selling physical goods via Stripe, although it could certainly help you get some of the way there.

It also doesn’t cover shopping cart implementations, where you can add and subtract multiple products from a cart.

Stripe Checkout limitations

There are a few limitations to using Stripe Checkout with Hugo; I consider none deal-breakers. That said, here they are:

Limitation #1: Coupons, discounts and promo codes are not supported

One of the limitations of implementing Stripe Checkout into a static site generator like Hugo—as opposed to, say, a Ruby on Rails web app—is that coupons, discounts, and promo codes are not supported.

However, I don’t see this as a deal-breaker.

I can still have sales—for Black Friday, for example—by simply lowering the price in the Stripe UI, and editing my Hugo site to make it clear that the product is discounted for a limited time. I could even add a JavaScript countdown timer showing potential customers how much time is left until the price goes back up. Of course, I’d have to manually increase the price at the end of the sale, but to me, that’s no big deal; a fair price to pay for lower fees and the ability to integrate with Hugo.

Limitation #2: Tax rates are not supported

While you can’t have Stripe automatically add the relevant sales tax when integrating Checkout with Hugo, you can bake it into the price.

In Australia, for example, the sales tax (GST) is 10%.

So instead of charging $100 for a product, I could charge $110 ($100 for the product + $10 for GST).

I could then pay the $10 GST to the Australian Taxation Office. I’m not an accountant and I would have to run this by one, but this could be a workable plan. I’d also have to factor in Stripe’s fees when calculating the exact amount of GST I’d pay.

It goes without saying that this is not financial advice; it’s only a plan I’m considering.

Limitation #3: Only credit card payments (and Apple Pay and Google Pay) are supported

On its website, Stripe lists another limitation of using Checkout with client-only integration:

Only card payments are supported. Other payment methods are not supported.

However—and Stripe could have made this clearer—you can still trivially accept payments via Apple Pay and Google Pay when you use Stripe Checkout on your Hugo site.

So what Stripe actually means is you can’t use things like bank transfer to accept payments with Checkout on a Hugo site. Fine by me!

Limitation #4: Zapier says fufilment emails can take up to 15 minutes to arrive on the free tier

When you use the free tier of Zapier, they note that zaps can take up to 15 minutes to run.

However, you can note this on the thank you page so customers know the fufilment email may not be instantaneous. It’s not ideal, but it’s workable. I’ve never had a customer complain that their fulfilment email took too long to arrive (doesn’t mean it didn’t happen, though).

Other limitations

There are other limitations—see link at the top of this section—but none seem relevant to our goal of selling a single digital product for a one-time payment on a Hugo site.

The process

Okay, let’s get coding!

Part 1: Set up Stripe

1A) If you don’t have a Stripe account, register here.

1B) Once you’re signed in, head to your Stripe Checkout settings and enable the client-only integration.

1C) While in the Stripe Checkout settings area, ensure you have Apple Pay and Google Pay enabled—as above, you won’t pay any more to use these services, nor will you have to add any extra code to your Hugo site.

1D) Still in the Stripe Checkout settings, ensure you have Google Maps autocompletion enabled for customers to enter their physical address(es).

Google Maps address autocompletion can improve the conversion rate (completion rate) of your checkout, since it reduces the amount of typing the customer has to do. It also reduces the chances of an incorrect address submission.

1E) Still in the Stripe Checkout settings, on the lower right of the page, add your domain(s) where you’ll be selling via Stripe Checkout. I added both this domain (moonbooth.com) and a Netlify test site (_____.netlify.app). More on the test site later.

1F) Add your business information in your Stripe account settings. I recommend using an official company email (preferably one on the same domain as the website selling your product). If you don’t have one, you can get email on your domain for free at Zoho.com.

1G) Create your product at the product admin area in Stripe. Make sure test mode is enabled, by enabling the toggle marked View test data in the left-hand side nav bar. (Stripe makes it easy to copy test products to production mode—we’ll do this later).

Set a price and currency for your product. I chose $39 USD for my Hugo SEO theme.

Part 2: Create the ‘success’ and ‘cancelled’ pages in Hugo

We’re going to create two Hugo pages, one for successful Stripe orders; the other for cancelled or failed orders.

We’ll need to provide these URLs to Stripe in the next step, so it can include them in the JavaScript for its Checkout button.

If you’ve created Hugo pages before, this will be pretty straightforward. I created my pages at:

/content/order-successful.md
/content/order-cancelled.md

however you may wish to use a subfolder such as /store/ to help organise your repo:

/content/store/order-successful.md
/content/store/order-cancelled.md

Part 3: Create the Stripe Checkout button

In this part, we’ll generate the Checkout button code in Stripe’s UI, and then add it to our Hugo site.

3A) Visit your product dashboard in Stripe.

3B) Click on your product’s name, not the three dot overflow menu to the right.

3C) Scroll down to the Pricing section, and click on the three dot overflow menu next to your price. Click on the dropdown menu option labelled Get Checkout code snippet.

3D) A modal window named Checkout snippet generator will appear. Add the absolute urls of your Success URL and Cancel URL you created earlier, for example:

example.com/order-successful
example.com/order-cancelled

3E) Copy the JavaScript provided.

3F) If you want your Checkout button to go on a blog post or page rather than in your template, create a Hugo shortcode named something like checkout-button.html. Mine lives at:

/layouts/shortcodes/checkout-button.html

Alternatively, if you want your Checkout button to go in your template, create it as a partial—which could also be named something like checkout-button.html. This would live at:

/layouts/checkout-button.html

3G) Paste the Stripe JavaScript from step 3E into the shortcode or partial file.

3H) Add the shortcode or partial for the Checkout button at the desired place(s) in your site. I’m using a shortcode rather than a partial, and I added my Checkout button shortcode to the blog post where I introduced my SEO Theme for Hugo using this Hugo shortcode syntax:

{{< checkout-button >}}

In the next part, we’ll test the test Checkout button with test credit card numbers that Stripe gives us.

Part 4: Test the Checkout button

Let’s briefly sum up where we are: we have a test product hooked up to a Checkout button on our Hugo site, either on your local machine, or on the public internet but on an obscure page. And because we are using a test product, we can use test credit card numbers to test successful and failed orders.

4A) Stripe provides test credit card numbers; go ahead and use them. You should be redirected to either your “order successful” Hugo page or your “order failed” equivalent.

We obviously haven’t set up product fulfilment via email yet; at this point we’re just testing the redirects to the appropriate Hugo page.

Part 5: Add email fulfilment

You can use an “internet duct tape” service like Zapier or IFTTT to send an email to each customer after their card / Apple Pay / Google Pay has been successfully charged.

Inside this email, we’ll thank the customer for their order and provide them with a link to their product so they can download it.

I use Dropbox to host my product (an SEO theme for Hugo) and set the permissions so anyone with the link can view it.

Let’s dive in.

5A) Upload your digital product (your e-book, video, software etc) to Dropbox or another file sharing service. Set its permissions so that anyone with the link can access the file for download.

Yes, this is “security by obscurity”, but the chances of someone guessing your download link is very slim. And yes, people could share the download link, but they could just as easily share the file itself if bought through another e-commerce solution.

5B) Sign up for Zapier, assuming that’s what you want to use to trigger an automated “thank you” email after each purchase.

5C) Now we need to connect our Stripe account to Zapier. Head to Zapier’s connections page and click Add connection. A modal window with a search box will appear—search for Stripe and click on it. You’ll need to authorise Zapier to access your Stripe account, which can be done by copying your test Stripe secret key from your Stripe dashboard and pasting it in when asked.

5D) Still on Zapier’s connections page, add Gmail or another email service provider that you want to use to send the fufilment emails. You’ll also need to authorise that connection by signing into Gmail or whichever email provider you’ve chosen.

5E) Make a new zap and name it. The name should reflect the fact that this is using Stripe’s test mode, as you’ll be making another zap later using Stripe’s production mode, and you want to be able to easily differentiate between them.

5F) The first part of a zap is the “trigger”. Here, there’s a section called Choose app & event. For the app, choose Stripe. For the trigger event, chose New Charge. Press Continue.

5G) The next section is called Choose account. Here we’ll choose our test Stripe connection we created in step 5C.

5H) We move on to the next section, Set up trigger. You’ll be asked whether you want to “Include Failed Charges?”. Set this to False. The help text explains why:

By default, the Zap will trigger on all charges, including any that failed. Choose “False” to prevent the Zap from triggering on failed charges.

We obviously don’t want to email our product to customers who haven’t paid, hence we need to set this to False.

5I) The last section under “Trigger” is called Test trigger. Zapier/Stripe should be able to find a test charge (which we generated when we tested the Hugo “thank you” and “order cancelled” pages). There’s nothing to do in this step—hit Continue.

5J) In the “Action” step, the first section is called Choose app & event. Here, we’ll choose Gmail (or another email provider) and authorise the connection by signing into Gmail.

5K) The next section is named Choose account. Here you choose the relevant email account. There may be only one, which is fine.

5L) Let’s move on to the next section, Set up action. Here, we’ll construct the email that goes out to customers after their purchase.

First, we need to tell Zapier where to send the email.

We’ll pick Cust Email, which is the field customers fill out when purchasing via Checkout. You can cc or bcc a hard-coded email address if you like.

The sender name will be whatever you’ve set up in Gmail.

In the body field, write your message thanking the customer for their purchase. Include the Dropbox link to your product you created earlier, so the customer can download your product.

5M) It’s time to test the action. Have Zapier send a test email and ensure everything looks good and that the Dropbox link is working.

Part 6: Switching from test to production mode

Now it’s time to go back and repeat the entire process, only this time, in production mode. Here are the steps:

6A) Before you activate production mode in the Stripe UI, head to your Stripe products page. Click on the name of your product (not the three dot overflow menu). On the ensuing page, click the button labelled Copy to live mode at top right.

6B) Now we can deactivate the “Viewing test data” toggle in Stripe’s left-hand nav bar—this will put us in production mode. Suddenly, your test product will say Product not found—this is because it doesn’t exist in live mode. Click View all products and you should see the production mode version of your product.

6C) Click on the name of your product (not the three dot overflow menu). Then scroll down to the Pricing section. Click on the three dot overflow menu next to your price, and select Get Checkout code snippet.

6D) Enter in the absolute urls of your Hugo “order success” and “order cancelled” pages.

6E) Copy the JavaScript provided and paste it into your Hugo checkout button shortcode and/or partial, overwriting the test mode JavaScript. Push it live.

6F) Moving on to Zapier, head to your zaps. Click the dropdown arrow next to your test zap. Click Copy. Rename the copied zap to indicate it’s the production version.

6G) In this new, production zap, go to New Charge in Stripe > Choose account. Change your Stripe test secret to your Stripe live secret. Every other part of the zap can stay the same.

6H) Testing the production version of your Stripe Checkout implementation isn’t straightforward. We’re not supposed to use our own credit cards, as technically this could be money laundering. However, you could have a friend purchase the product for you with their card, and reimburse them the money.

Wrapping up

As you can see, there’s a fair bit to this. However, it’s hugely satisfying building a lightweight e-commerce offering directly into Hugo.

A huge thank you to Josh Thompson who taught me all of this. If you want a video version of the process, or annotated screenshots, I recommend Josh’s course How to Take Payment On A Static Site with Stripe. That’s not an affiliate link—it’s just a good course.

"Thanks so much for your work ... I'm migrating my WordPress blog to Hugo and it's been really helpful." — Francisco S., engineer and blogger

"I love your content. The information that you provide have been very useful in the development of my personal website." — Mattia C., engineer and researcher

The planets in our solar system