Stripe's accept-a-payment samples: choosing between Checkout Sessions and Payment Intents
Learn how to accept a payment from customers around the world with a variety of payment methods.
At a glance
- What is it?
- The stripe-samples accept-a-payment repository holds four working integrations built on two Stripe APIs, with a comparison table that shows which combination of API and UI gives you automatic payment methods, built-in tax, adaptive pricing and localisation, and which one leaves you to implement all four yourself. The README recommends the Checkout Sessions API for most new work.
- Who is it for?
- Use this repository to understand what each level of checkout customisation costs you, and copy the sample closest to your requirement rather than starting from a tutorial. For a new integration, the README's own recommendation is the Checkout Sessions API, either the Stripe-hosted page or Elements with Checkout Sessions.
- 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 received new commits within the last day.
- What is it written in?
- Mainly JavaScript, 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
What the repository is and how to run it
The repository is a set of sample integrations for accepting payments on the web with Stripe, described as a way to explore different ways to accept payments on the web with Stripe.
The first tip at the top of the README is the practical one. If you downloaded it via stripe samples create, your server is in ./server/ and your client is in ./client/, and you install dependencies and start, for example cd server && npm install && npm start for Node. If the page does not load, or you see errors about missing keys, make sure you ran stripe login before stripe samples create, because the CLI needs that to write server/.env.
That last detail is the one that catches people. The sample does not ask for your keys, the CLI writes them for you, and the CLI refuses to do that until you are logged in. The repository also ships a .env.example that shows the shape of what is expected, with a publishable key, a secret key and a webhook signing secret, plus a static directory path, a price identifier, a local domain and a list of payment method types for the store.
The repository is a multi-language project. The top level has a Gemfile and an .rspec, so Ruby is one of the languages in play, and there is a playwright/ directory with a docker-compose.playwright.yml for browser testing, plus main.tf, which suggests a Terraform configuration for provisioning the test environment. The frontend is served from a directory that the .env.example points at with a comment noting that PHP has its own front end implementation.
The licence is MIT and there are no GitHub releases, so this is a moving set of examples tied to the current state of the Stripe API rather than a versioned library.
The two APIs and the four ways to combine them
The repository includes four sample integrations built on two different APIs, and the README is explicit that the Checkout Sessions API is the recommended one for most integrations because it requires less code than the Payment Intents API and includes built-in support for tax, discounts, shipping, adaptive pricing, recurring payments, localisation and automatic payment methods.
The four combinations are the two APIs crossed with two levels of UI control.
Prebuilt Checkout page is the Stripe-hosted option: you create a Checkout Session and redirect. The README describes it as the simplest integration with the least code, with low complexity, where creating a session and redirecting is all you do. You can customise the logo, images and colours, payment methods are added automatically with a single line change, Apple Pay and Google Pay support is built in, customers redirect to the Stripe-hosted page and then return to your site, and input validation and error handling are built in. It is localised in more than fifty languages and automates sales tax, VAT and GST calculation with one line of code.
Elements with Checkout Sessions creates a session on your server and then renders the Payment Element on your site with ui_mode set to elements. The README calls this moderate complexity, a few API calls on your server and a few lines of Stripe.js on your client. Customisation is through the Appearance API rather than raw CSS, and customers stay on your site with a redirect after payment completion.
Payment Element uses the Payment Intents API directly. Similar UI to Elements with Checkout Sessions, but you manage the payment lifecycle yourself, which the README describes as more server-side payment lifecycle management. Payment completion may trigger a redirect, tax is calculated using the Tax API, and error handling is yours to implement.
Custom payment flow is the advanced option: individual Stripe Elements with the Payment Intents API and full control over every detail. It is high complexity, you manage the full lifecycle and UI, each payment method is a separate integration, Apple Pay and Google Pay need extra code, and you implement your own input validation, error handling and localisation.
The comparison table and what it tells you
The at a glance table is the most useful artefact in the repository, because it prices the four options against each other on six dimensions.
Complexity runs from low for the prebuilt Checkout page through moderate for the two Elements options to high for the custom flow. Customisation runs from logo, images and colours through the Appearance API for the two Elements options to full CSS for the custom flow.
Customer experience splits cleanly: the prebuilt page redirects to Stripe, the other three keep the customer on your site, with the Payment Element option noting that payment completion may trigger a redirect. Recurring payments need a small refactor for the two Checkout Sessions options and a large refactor for the two Payment Intents options, which is the tax you pay for managing the lifecycle yourself.
Tax calculation is built-in for the Checkout Sessions options and requires the Tax API for the Payment Intents ones. Adaptive pricing is built-in for the Checkout Sessions options and not available in the other two, which is a capability you cannot add later without changing API.
And payment methods are automatic for the first three and manual per method for the custom flow.
The pattern through that table is consistent. Each step away from the Stripe-hosted page moves work from Stripe to you, and the specific work that moves is not random. It is the work that requires knowing something about your business: your tax jurisdictions, your pricing, your error copy, your languages. Checkout Sessions bundles that knowledge into the API; the custom flow expects you to have it.
That is why the README frames the choice as capability rather than preference. If you need adaptive pricing, you are on Checkout Sessions. If you want a checkout page that looks like the rest of your site to the pixel, you are on the custom flow and you are implementing payment methods, validation, errors and localisation yourself.
The payment method support table and what it rules out
A second table maps twenty-eight payment methods against the four options, and it is the table to check before choosing.
The pattern is that the prebuilt Checkout page supports everything, because Stripe renders the page and decides what to show. Elements with Checkout Sessions supports everything. The Payment Element supports most things but not all. The custom payment flow supports the fewest, because each method is a separate integration there.
Looking at specific rows, Amazon Pay is unavailable in the Payment Element and the custom flow. Affirm, Afterpay and Clearpay, Alipay, Apple Pay, Bancontact, BECS Direct Debit, Boleto, Cards, EPS, FPX, Google Pay, GrabPay, iDEAL, Klarna, OXXO, PayPal, Przelewy24 or P24, SEPA Direct Debit, WeChat Pay and Zip are all available across the first three options and unavailable in the custom flow.
ACH Debit and Bacs Direct Debit are available in the first three but not in the custom flow. Cash App Pay, Link, Multibanco, OXXO and Revolut Pay are unavailable in the Payment Element, and Cash App Pay and Revolut Pay are also unavailable in the custom flow.
The consequence for the custom flow is specific and worth stating plainly. It is not that you cannot offer Klarna or iDEAL there; it is that each of them becomes an integration you write and maintain, and the README's bullet list puts it as implement each payment method as a separate integration. If your business needs a specific local method, that option carries a real ongoing cost.
One more detail from the .env.example is worth carrying into your own setup. The payment method types for the store are set there with a note that some payment methods support only a subset of currencies, which is a reminder that enabling a method in code is not the same as it working for every currency you sell in.
The tooling around the samples
The samples come with the tooling you need to run them rather than just read them.
The Stripe CLI is the recommended way to use the sample, and the README links the install documentation for it. The CLI is what supplies the test keys, the local forwarding of webhook events to your machine and the trigger commands for firing test events, which is the part of the payment integration you cannot fake with a plain HTTP request.
The .env.example shows the three credentials you need. The publishable key and the secret key are the obvious pair, and the third is the one people forget: STRIPE_WEBHOOK_SECRET, with the comment that it is required to verify signatures in the webhook handler, and a pointer to the README for using the CLI to test webhooks. That signature verification is the entire basis of a trustworthy payment integration, since it is how you know the event really came from Stripe, and the sample treats it as mandatory rather than optional.
The repository also has a .cli.json, which is the Stripe CLI's own configuration file, a .devcontainer/ directory for working inside a container, a .prettierrc.yml and .prettierignore for formatting, a TESTING.md, and a spec/ directory with the .rspec alongside the Gemfile, which suggests the tests are written in Ruby and run against the samples.
The four sample directories each correspond to one of the integration options: prebuilt-checkout-page/, elements-with-checkout-sessions/, payment-element/ and custom-payment-flow/. The README links each one separately with both the sample code and the relevant documentation, which means you can read the README's decision framework and then jump straight to the one that matches your requirement rather than reading all four.
Limits: samples are not a library, and the choice is a commitment
The limits are worth stating clearly, because samples and production code are different kinds of artefact.
The first is that this is not something you depend on. There is no package to install and no versioned API. The repository has no GitHub releases, so there is nothing to pin, and the code inside the sample directories changes as Stripe's API and its own recommended approach change. Copying the code into your application means you own it from that point, including keeping it working as Stripe's API moves.
The second is that a sample optimises for demonstrating, not for operating. A sample will show you the happy path and the shape of the integration. It is unlikely to cover the parts that matter in production: idempotency on webhook retries, the race between a redirect returning and a webhook arriving, reconciliation for a payment that succeeds but whose webhook is delayed, refund and dispute handling, and the currency-specific quirks of the methods it enables. The presence of a webhook signing secret in the environment file shows the shape of the security requirement, and that is more than many tutorials include.
The third is that the choice in the comparison table is closer to a commitment than a preference. Choosing the custom payment flow means owning payment methods, validation, error handling and localisation forever, and the migration path back toward more automation is not in the table. Choosing the prebuilt Checkout page is easy to start and easy to leave, because you can move to Elements with Checkout Sessions and keep the same underlying API.
The fourth is the environment. Running the sample requires the Stripe CLI, a logged-in Stripe account and test keys, and the samples are tied to the current state of the Stripe API and its own recommended approach, so a sample written against an older API shape may not demonstrate the current recommendation.
The fifth is scope. This repository covers accepting a payment on the web. It is not a guide to saving a card for later, to subscriptions, to marketplaces with split payments, to tax registration, or to refunds, and the fact that each sample mentions how much work recurring payments would be is a reminder of how much of the payment domain sits outside it.
The samples against the Stripe documentation and against a payment platform
Two comparisons make the choice clearer.
The first is against reading the Stripe documentation alone. The docs describe the API; the samples show it assembled into something that runs, with the environment file, the CLI invocation and the four-directory structure in place. For the first integration, that difference is real: the documentation for Checkout Sessions assumes you already know how a checkout page is meant to be put together, and the sample shows you the redirect, the session creation and the return handling as a working pair. The cost of the samples is that they can lag the docs, and the README's own recommendation, to build on the Checkout Sessions API and to use the samples as a starting point rather than a specification, reflects that.
The second comparison is against a payment platform rather than a payment API. Stripe gives you the API and leaves the customer-facing experience to you, except in the prebuilt option. A platform that is a hosted checkout product, or a local payment method that dominates a market, gives you a flow optimised for that method and takes a percentage for it. The Stripe samples, with their table of twenty-eight methods and their rows marking which are unavailable where, describe a different approach: one integration that can reach many markets, with the local method as a configuration choice rather than a separate product.
That approach is why the repository exists at all. The hard part of accepting payments internationally is not the API call, it is the per-method integration work, the localisation, the tax and the error copy. The table is a map of how much of that Stripe will do for you, and the recommendation to start with the Checkout Sessions API is an instruction to take the most of it.
Editorial conclusion
Use this repository to understand what each level of checkout customisation costs you, and copy the sample closest to your requirement rather than starting from a tutorial. For a new integration, the README's own recommendation is the Checkout Sessions API, either the Stripe-hosted page or Elements with Checkout Sessions. Verify first which payment methods your customers actually use, because the support table shows several that are unavailable in the most customisable flow, and that the sample you copy matches the API you plan to keep using.
Frequently asked questions
Which Stripe API does Stripe recommend for new payment integrations?
The Checkout Sessions API. The README says it requires less code than the Payment Intents API and includes built-in support for tax, discounts, shipping, adaptive pricing, recurring payments, localization and automatic payment methods, and recommends either the Stripe-hosted Checkout page for simplicity or Elements with Checkout Sessions for full design control.
How do I run the accept-a-payment samples locally?
The recommended way is with the Stripe CLI, and if you downloaded via stripe samples create your server is in ./server/ and your client in ./client/, so you run cd server && npm install && npm start for Node. You must run stripe login before stripe samples create, because the CLI needs to be authenticated to write server/.env with your test keys.
Which payment methods are not available in the custom payment flow?
Many of them. The support table shows Amazon Pay, Affirm, Afterpay and Clearpay, Alipay, Apple Pay, Bancontact, BECS Direct Debit, Boleto, cards, EPS, FPX, Google Pay, GrabPay, iDEAL, Klarna, OXXO, PayPal, Przelewy24, SEPA Direct Debit, WeChat Pay and Zip are unavailable there, because in that flow each payment method is implemented as a separate integration.
Official sources
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.
[](https://hysenlabs.com/projects/stripe-samples-accept-a-payment)Was this useful?