Open-source project
stripe-samples/checkout-single-subscription avatar
stripe-samples/checkout-single-subscription

A Stripe sample that puts a subscription page up with no server logic

Learn how to combine Checkout and Billing for fast subscription pages

897 stars390 forksCSSMIT

At a glance

What is it?
The checkout-single-subscription sample is a reference implementation of Stripe Checkout combined with Billing, with eight server languages, a client directory and a seeded data file. It suits a developer who wants a working subscription page in an afternoon, and it is a sample rather than a component you can drop into a product.
Who is it for?
This sample suits a developer who wants to see a complete subscription payment flow work end to end before writing any of it, because it shows the price setup, the Checkout Session call, the redirect and the webhook handling in eight languages side by side.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 175 days ago.
What is it written in?
Mainly CSS, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The point is that there is no server

The sample's premise is stated in the opening line. Checkout is a pre-built payment page that lets you accept cards and Apple Pay, Billing is a suite of APIs for modelling complex subscription plans, and combining the two gives you a subscription payment page without the need of a server.

That last clause is the interesting one, and it needs care. There is a server in this repository, eight of them. What the sample avoids is the server doing anything clever. When the customer is ready to pay, you use Stripe.js with the ID of your Checkout Session to redirect them to the Checkout page. The page is rendered by Stripe, on Stripe's side, styled with your business name and logo.

So the flow is: the server creates a Checkout Session for a price, hands the session ID to the client, Stripe.js redirects the browser to Stripe's hosted page, the customer pays there, and the subscription is created on Stripe's side. The integration uses the Checkout Sessions API for additional functionality.

The table in the README makes the scope explicit. The prebuilt checkout page is supported, so you can create a payment page customizable with your business name and logo. Prices can be defined in the Dashboard or via the API. And a subscription can be started for an existing Customer, using the Customers API to keep track of additional customer data.

For a developer evaluating how much work a subscription page is, that is a useful shape to see before designing one.

Prices are objects you create first, and the sample starts with two

The sample requires two Price IDs, and that constraint shapes the whole setup. Products and Prices are objects on Stripe that let you model a subscription, and the README asks you to create both a basic and a premium plan so the finished page can offer a choice.

The Stripe CLI is the fastest route. Two commands create the two products:

sh
./stripe products create --name="Basic" --description="Basic plan"
sh
./stripe products create --name="Premium" --description="Premium plan"

The response gives you an id for each product, in the form of a JSON body with an id field, and you need those to create prices. Each price command names the product, an amount in the smallest currency unit, a currency and a recurring interval:

sh
./stripe prices create \
  -d "product=ID_OF_BASIC_PRODUCT" \
  -d "unit_amount=1800" \
  -d "currency=usd" \
  -d "recurring[interval]=month"

The amount of 1800 with currency usd is 18 dollars, and the recurring interval is what makes it a subscription rather than a one-off payment. The same command with a different product id produces the premium price.

The README also covers a variant with Stripe Tax, where the product creation adds a tax code such as txcd_10000000, the one the README names for general electronically provided services, and the price adds a tax_behavior of inclusive or exclusive. The example uses exclusive.

And there is a non-CLI path. Products and Prices can be created in the Stripe dashboard, and the README says to create two recurring prices there to run the sample. Either way, the two price IDs then go into the .env file as BASIC_PRICE_ID and PRO_PRICE_ID, which are the variables the server reads when it builds the Checkout Session.

Cloning, keys and the eight server implementations

Running it locally takes a few steps, and the README gives both routes.

The CLI route is one command. The Stripe CLI is described as the fastest way to clone and configure a sample, and it walks you through picking your server and client languages and configuring your .env config file with your Stripe API keys:

sh
./stripe samples create checkout-single-subscription

The manual route is a git clone, followed by copying the .env.example file into a .env file in the folder of the server you want to use, for example cp .env.example server/node/.env. The .env.example in the repository shows the full contract: the publishable and secret keys, a webhook secret that is required to run the webhook, a DOMAIN value of http://localhost:4242, the two price IDs, and a STATIC_DIR pointing at the client directory.

The server implementations are the striking part of the layout. The README says the sample includes 8 server implementations in Node, Ruby, Python, Java, PHP, PHP with Slim, Go and .NET, and the server directory is where you find the one you want. You then follow the instructions in the README of that server folder, because the run command differs per language.

The top-level package.json gives the Node path, with a start script of node server/node/server.js, and it declares the runtime dependencies: express, body-parser, dotenv and the stripe node library at version 8.107.0 or later. There is also a client directory holding the front end, a spec directory with a .rspec file for RSpec, a main.tf Terraform file, a .cli.json file that describes the sample to the CLI, and a sample-seed.json file.

One step is easy to miss and will stop the page rendering properly. In order to use Checkout you must set an account or business name in the Stripe dashboard.

Testing it with the numbers Stripe publishes

The demo runs in test mode, and the README names the card numbers to use.

The card number 4242424242424242 is the test card to use with any CVC and a future expiration date. A second number, 4000002500003155, triggers a 3D Secure challenge flow, which is how you exercise the strong customer authentication path rather than assuming it works.

The features listed at the top of the README are the ones a hosted page gives you for free: localization in over 25 different languages, built-in Apple Pay support, and built-in dynamic 3D Secure that is ready for SCA.

That last point is the one with the most practical consequence for a European deployment. Strong customer authentication is the regime that decides whether a card payment needs a step-up challenge, and a hosted Checkout page handles it without your code deciding anything. A team that has been building a custom payment form has probably had to think hard about that; a team using Checkout does not.

There is a live demo linked from the README and a CodeSandbox fork of the Node implementation, and the README points to the Stripe testing documentation for more.

The webhooks deserve a note too, because they are where the real work is. The .env.example marks STRIPE_WEBHOOK_SECRET as required to run the webhook, with a comment pointing at the README for how to use the Stripe CLI to set it up. The subscription is not finished until your server has received and verified the webhook that tells you the payment succeeded, and the sample includes that handler in each language.

What a sample cannot tell you

Samples are written to be read, not to be depended on, and this one is explicit about its limits in ways worth taking at face value.

The first is the test script. The package.json defines a test command that echoes an error message and exits with status 1, which is the npm default for a package with no tests configured. So there is no test suite here to copy, and the RSpec files in the spec directory are not a framework for testing your own integration.

The second is the licence mismatch, and it is the thing to check before reusing any of this. The licence field on the repository says MIT, while the sample's own package manifest declares ISC as the licence. Those are not the same licence, and the file each one refers to is the one that governs. Neither is a restrictive licence in the sense that most companies worry about, but a mismatch in a repository of samples is worth resolving in your own favour before you copy files out of it.

The third is the absence of production concerns. There is no error handling around a failed payment, no retry logic, no customer portal, no proration handling when a customer upgrades between the two plans, no dunning for a failed renewal, and no authentication on the webhook endpoint beyond verifying the signature. A subscription product needs all of those, and none of them is in this repository.

The fourth is scope. This is one subscription, one page, two plans, one Checkout Session. Products with tiered pricing, usage-based billing, trials, coupons or multiple currencies are all outside what the sample demonstrates, even though the underlying API supports them.

What the sample does give you is the wiring, in eight languages, which is the part people otherwise get wrong.

How the project is being maintained

The repository is not archived, and the last push was on 2026-05-01, a little under six months before the present date. There are no GitHub releases, so there is no tagged version to pin to, which is normal for a sample repository where the code is read rather than consumed as a dependency.

The default branch is main, and the package.json version is 1.0.0, which describes the sample package rather than a published release.

The fact that Stripe maintains a set of these samples at all is the useful signal, and it comes with the connection to the documentation. Every step of the README links into the Stripe documentation for the relevant API, from the Checkout documentation for subscriptions through the testing documentation to the tax setup guide and the list of available tax codes. The sample is therefore best read as an executable index of the documentation rather than as a standalone artifact.

The support arrangement is also worth noting for anyone who plans to build on it. The README points at the Stripe developer dashboard for API keys, at the Stripe CLI installation steps, and at the Stripe documentation for setup. The Stripe CLI itself is described as useful for cloning samples and locally testing webhooks and Stripe integrations, which is the tool that makes the whole local loop work.

The honest summary is that this repository is a well-organised, current reference for one specific Stripe integration pattern, maintained by the vendor, and worth copying as a pattern rather than as a component.

Editorial conclusion

This sample suits a developer who wants to see a complete subscription payment flow work end to end before writing any of it, because it shows the price setup, the Checkout Session call, the redirect and the webhook handling in eight languages side by side. It is the wrong tool for a production integration, since it ships no abstraction, no error handling to speak of and no test suite, and it is the wrong tool for anyone who has already integrated Stripe and needs the pattern rather than the code. Before you copy anything, create the two products and recurring prices in your own test account, set the two price ID variables in the .env file, and note that the repository licence and the licence declared in the sample's own package manifest do not match, so read both files before you reuse the code.

Frequently asked questions

What does the checkout-single-subscription sample demonstrate?

It combines Stripe Checkout, the pre-built payment page, with the Billing APIs for modelling subscription plans, so a subscription payment page works without a server doing the payment work. The server creates a Checkout Session and Stripe.js redirects the customer to the hosted page.

How many server implementations does the sample include?

Eight, according to the README: Node, Ruby, Python, Java, PHP, PHP with Slim, Go and .NET. You pick one, follow the instructions in the README inside that server folder, and start it.

Which test card numbers should I use with the Stripe subscription sample?

Use 4242424242424242 with any CVC and a future expiration date for a normal test payment. Use 4000002500003155 to trigger a 3D Secure challenge flow. The demo runs in test mode.

What has to be created in Stripe before the sample works?

Two products and two recurring prices, one basic and one premium, whose price IDs then go into the .env file as BASIC_PRICE_ID and PRO_PRICE_ID. You also have to set an account or business name in the Stripe dashboard, without which Checkout will not render properly.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. stripe-samples/checkout-single-subscription on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/stripe-samples-checkout-single-subscription.svg)](https://hysenlabs.com/projects/stripe-samples-checkout-single-subscription)

Was this useful?