Custom Checkout with Stripe Elements
Instructions on how to setup Sphere's API while using Stripe Elements in a custom checkout
To create a custom checkout session with taxes calculated using the Sphere API, follow these steps:
Call the Sphere Tax Calculation API to retrieve applicable tax rates.
Develop a custom payment form using Stripe Elements, either for one-time payments (Step 3a) or subscriptions (Step 3b), and link the Stripe tax rates obtained from the Sphere API calculations.
Custom Checkout with Stripe Elements
Stripe Elements are Stripe's pre-built, customisable UI components for collecting payment details. This guide shows how to add Sphere tax calculation to a custom checkout built on Elements, so the amount your customer pays includes the right tax and the resulting Stripe records carry Sphere's calculation.
There are two flows, depending on what you are selling:
A one-time purchase
PaymentIntent
The PaymentIntent amount
Step 2a
A subscription
Subscription (created via a Subscription Schedule)
tax_amounts on the first invoice's line items
Step 2b
A subscription sold through Stripe Checkout
Checkout Session, mode: 'subscription'
Stripe Tax Rate objects on line_items[].tax_rates, removed after completion
Step 2c
Both flows start with the same call to Sphere (Step 1) and end with the same Stripe Elements confirmation on the client.
Prerequisites
A Sphere API key and the setup described in Tax Calculation: products with Product Tax Codes, and registrations for the regions you sell into.
A Stripe integration on API version
2025-03-31.basilor later. Field names in this guide (confirmation_secret,lines[].pricing) follows the current Stripe API version,2026-08-26.dahlia.The customer's full billing address before you create the payment. By default the Payment Element collects only a postal code and country; use the Address Element or set the Payment Element's
fields.billingDetails.addresstofull.
Step 1: Calculate tax with Sphere
Call the Tax Calculation API with the customer's address and the items in the cart. The response gives you the tax per line, broken down by jurisdiction, and a sphere_tax_calculation_id that you store on the Stripe object so Sphere can match the calculation to the payment or invoice.
For business customers pass tax_id (and include_taxability_reason: true to see when a reverse charge applied). Field-by-field detail, tax IDs and error codes are on the Tax Calculation page.
Step 2a: One-time payment
A PaymentIntent has no invoice or line items, so tax goes into the amount. Your server adds Sphere's tax to the subtotal, creates the PaymentIntent, and records the calculation id in metadata.
What we will do
The client sends the customer's address and cart to your server.
The server calls Sphere (Step 1) and sums the returned
tax_amounts.Server creates a PaymentIntent whose
amountis subtotal + tax, withsphere_tax_calculation_idinmetadata.Server returns the
client_secretplus subtotal, tax and total for display.Client shows the breakdown and confirms with the Payment Element. Tax is paid as part of the single charge.
Server
Trimmed to the fields this guide uses.
Client
Show the subtotal, tax and total your server returned, then confirm as in Stripe's quickstart:
Step 2b: Subscription
Stripe finalises the first invoice of a subscription inside the subscriptions.create call, before tax can be added. Creating the subscription through a Subscription Schedule starting now gives you the first invoice as a draft: Sphere writes the tax onto its lines, your server finalises it and returns the payment client_secret to the client exactly as in Stripe's standard Elements flow.
What we will do
Create the subscription through a Subscription Schedule with
start_date: 'now'. This creates a real Subscription and its first invoice, but the invoice is a draft rather than finalised.Set
auto_advance: falseon the draft so Stripe does not finalise it while you work (it would otherwise do so after about an hour).Call Sphere with the invoice id. Sphere reads the draft, calculates tax from its line items and the customer's address, and writes the result onto each line as
tax_amounts. No Tax Rate objects are created, andsphere_tax_calculation_idis recorded on the invoice's metadata.Finalise the invoice and read its
confirmation_secret.Client confirms with the Payment Element exactly as in Stripe's standard subscription flow; on payment the subscription is active and later renewals are taxed by Sphere's webhook.
Server
1. Create the schedule
2. Hold the draft
3. Sphere - the invoice id, and the connected account when you have more than one:
4. Finalise
1. Schedule - note the invoice is a draft with auto_advance: true:
2. Invoice - "auto_advance": false, still "status": "draft".
3. Sphere - status is applied when tax was written, unchanged when the invoice already carries the same calculation, and no_tax when none applies. All three are safe to finalise:
The invoice's lines now carry the tax; the display_name, percentage and jurisdiction Sphere sent appear on the customer's invoice.
4. Finalised invoice
All responses trimmed to the fields this guide uses.
Client
Unchanged from Stripe's subscription guide: mount the Payment Element and call stripe.confirmPayment with the clientSecret. When payment succeeds the subscription is active.
Step 2c: Subscription via Stripe Checkout
Stripe Checkout creates and finalises the first invoice when the customer completes the hosted page, so there is no draft to write tax_amounts onto. Instead, collect the address before creating the session, create Stripe Tax Rate objects from Sphere's response, pass them on the session's line items, and remove them from the subscription as soon as Checkout completes so that renewals are taxed by Sphere.
You will need to be listening to the checkout.session.completed webhook for this method, which is the normal way of handling payment success/failure. You'll need to modify your webhook handler so that on success , you remove the Stripe Tax Rate objects we create and attach as part of this process. Even though we must use them with the Stripe Hosted Checkout, they prevent Sphere from keeping your tax calculations up to date as the customer and legislation change.
What we will do
Collect the customer's address.
Create a Stripe Tax Rate object to add Tax to the subscription
Create the Checkout Session in
subscriptionmodeDirect the customer to the Stripe Hosted Checkout page.
On
checkout.session.completed, update the subscription for Sphere to maintain your tax calculations.
Collect the address first (for example with the Address Element) and run Step 1.
Create a Tax Rate object for each
tax_ratein the response. Specify the tax rate attributes such as percentage, display name, and jurisdiction.
Create the Checkout Session in
subscriptionmode, with the calculation id on both the session and the subscription. Here we add the Tax Rate ids on each line item andsphere_tax_calculation_idon both the session and the subscription, then send the customer tosession.url.
subscription is populated once the customer completes the page.
Direct the customer to the Stripe Hosted Checkout page. The Hosted Checkout functions the same way as it normally does, though you will notice the tax being properly calculated and presented to the user.
Remove the rates on completion. Checkout copies
tax_ratesonto the subscription items, and Stripe would apply them to every renewal. To ensure Sphere can function thecheckout.session.completed, we clear them. The first invoice will still have the Tax Rates attached, but these were correct at time of purchase. Sphere ensures tax rates remain correct on future renewals.
Renewals
Nothing to do. Stripe creates each renewal invoice as a draft and holds it for about an hour; Sphere's Stripe integration writes tax onto it during that window, then Stripe finalises and charges as normal. The same applies to post-trial invoices and plan-change credits billed on the next invoice. Keep automatic_tax off and Tax Rate objects off your subscriptions, or Sphere's tax cannot be applied. Alternatively your billing system can manually call Sphere at renewal time, for stronger syncronous guarantees.
If you used Step 2c, this depends on the checkout.session.completed cleanup having run.
Common "gotchas" to watch out for
stripe.subscriptions.create(...) directly for card-on-file subscriptions
The first invoice is finalised inside the call. Use a Subscription Schedule (Step 2b).
Attaching Tax Rate objects as tax_rates outside Step 2c, or leaving them on a subscription after Checkout completes
Cannot coexist with tax_amounts on the same invoice, so Sphere cannot tax renewals; the objects also accumulate in your account unless reused and archived.
Enabling automatic_tax on any object
Mutually exclusive with Sphere on an invoice.
Relying on the Payment Element's default address collection
Postal code and country only. Collect the full address first.
Last updated
Was this helpful?