Collect taxes for recurring payments
To calculate tax for recurring payments, Stripe offers Stripe Tax and Tax Rates.
Stripe Tax—a paid product that automatically calculates the tax on your transactions without the need to define the rates and rules. Fees only apply after you’ve added at least one location where you’re registered to calculate and remit tax. For more information, see Stripe Tax.
Tax Rates—a free feature that allows you to define any number of tax rates for invoices, subscriptions, and one-time payments that use Checkout. Stripe won’t create or maintain any tax rates on your behalf. For more information, see Tax Rates and how to use them.
Stripe Tax allows you to calculate the tax to collect on your recurring payments when using Stripe Billing. You can create new subscriptions or add Stripe Tax to existing subscriptions, and examine any potential impact to the amount on your customer’s upcoming invoice. Stripe Tax is natively integrated with Stripe Billing and automatically handles tax calculation with your pricing model (e.g., sub-cent, package), prorations, discounts, trials, and more. This guide assumes you’re setting up Stripe Tax and Billing for the first time.
Set up StripeServer-side
Use our official libraries for access to the Stripe API from your application:
# Available as a gem sudo gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
Create products and prices
Stripe Tax uses information stored in the Products and Prices APIs to determine which rates and rules to apply when calculating tax. Update the products and prices you use with Subscriptions to include:
- Tax behavior: either
inclusive
orexclusive
. This determines whether tax should be included in the price. For example, an inclusive line item with an amount of 10 USD totals to 10 USD, whereas an exclusive line item with an amount of 10 USD totals to 10 USD + tax. Exclusive pricing is common practice in US markets and for B2B sales, while inclusive is common practice for B2C buyers in many markets outside the US. - (optional) Tax code: a selection from a list of options that determine what type of product it is. Some examples include Audio book, Gift card, or Software as a service. If this isn’t set explicitly, your default tax code applies.
You can’t change tax_behavior
after it’s been set to exclusive
or inclusive
.
If you don’t need to create your products and prices upfront, you can pass price_data.tax_behavior
and product_data.tax_code
when creating subscriptions. See Products, prices, tax codes, and tax behavior to learn more.
This guide uses an e-magazine as an example of a product with a recurring payment.
First, create a Price on your server with a monthly charge for a new Product named “My Product”. For tax purposes you add two extra fields:
tax_behavior
on the Price object. Set toinclusive
orexclusive
. This is a required field if you are using Stripe Tax with Subscriptions.tax_code
on the Product object. A Stripe tax code. Consult our list of tax codes for more information.
curl https://api.stripe.com/v1/products \ -u
: \ -d "name"="My Product" \ -d "tax_code"=sk_test_4eC39HqLyjWDarjtT1zdp7dc:"txcd_10000000"
Record the product ID for the product. It looks like this:
{ "id": "prod_H94k5odtwJXMtQ",
Use the product ID to create a price.
curl https://api.stripe.com/v1/prices \ -u
: \ -d "unit_amount"=1000 \ -d "currency"="usd" \ -d "recurring[interval]"="month" \ -d "product"="prod_H94k5odtwJXMtQ" \ -d "tax_behavior"="exclusive"sk_test_4eC39HqLyjWDarjtT1zdp7dc
When price_data.tax_behavior
is set to exclusive
, tax is added onto the subtotal amount you specify. This is common in U.S. markets and for B2B sales. When set to inclusive
, the amount your buyer pays will never change, even if the tax rate varies. This is common practice for B2C buyers in many markets outside the U.S.
Record the price ID so it can be used in subsequent steps. It looks like this:
{ "id": "price_HGd7M3DV3IMXkC",
Create a customerServer-side
When a user subscribes to your website, create a Customer on your server.
When creating a customer, you can just send us a description and the payment method. However, the more information you send us, the better the tax calculation can identify the location of your customer and tax them accordingly. We recommend populating the customer.address
field. Expand the tax
field to confirm the location Stripe Tax has identified for your customer.
You can either add a country and a postal code:
curl https://api.stripe.com/v1/customers \ -u
: \ -d "description"="a new user" \ -d "email"="franklin@example.com" \ -d "payment_method"="pm_1FU2bgBF6ERF9jhEQvwnA7sX" \ -d "address[country]"="US" \ -d "address[postal_code]"=94103 \ -d "expand[]"="tax"sk_test_4eC39HqLyjWDarjtT1zdp7dc
Or, ideally, add a complete billing address:
curl https://api.stripe.com/v1/customers \ -u
: \ -d "description"="a new user" \ -d "email"="franklin@example.com" \ -d "payment_method"="pm_1FU2bgBF6ERF9jhEQvwnA7sX" \ -d "address[line1]"="510 Townsend St" \ -d "address[city]"="San Francisco" \ -d "address[state]"="CA" \ -d "address[country]"="US" \ -d "address[postal_code]"=94103 \ -d "expand[]"="tax"sk_test_4eC39HqLyjWDarjtT1zdp7dc
Or, just an IP address:
curl https://api.stripe.com/v1/customers \ -u
: \ -d "description"="a new user" \ -d "email"="franklin@example.com" \ -d "payment_method"="pm_1FU2bgBF6ERF9jhEQvwnA7sX" \ -d "tax[ip_address]"="127.0.0.1" \ -d "expand[]"="tax"sk_test_4eC39HqLyjWDarjtT1zdp7dc
The expanded tax
field indicates the computed tax location (using the address first, falling back on the given IP address) and if the customer is compatible with automatic tax calculation:
{ "id": "cus_13729he8947269", "object": "customer", // ... other fields omitted "tax": { "location": {"country": "US", "state": "CA", "source": "billing_address"}, "ip_address": null, "automatic_tax": "supported", } }
The value of automatic_tax
has four possible states:
Status | Description | Possible Action |
---|---|---|
supported | Automatic tax fully supported. | No further action needed. |
unrecognized_location | The address isn’t valid for determining a tax location. | Ask customer for an updated address and set customer.address to the new value. |
not_collecting | The address is resolvable to a location for which you haven’t set up a registration. | Depending on your tax obligations, you can either continue to proceed and Stripe Tax won’t assess any taxes, or you might want to add a new registration for the jurisdiction in which the customer is based. |
failed | An error occurred with Stripe’s servers. This is rare. | Try the request again, or contact Stripe support for additional assistance. |
Create a subscriptionServer-side
Now that the Customer is set up for tax calculation, you can create a Subscription on your server with the customer and their selected plan. To enable automatic tax calculation on subscriptions, set the automatic_tax[enabled]
parameter to true
:
curl https://api.stripe.com/v1/subscriptions?expand[]=latest_invoice \ -u
: \ -d "customer"="cus_13729he8947269" \ -d "items[0][price]"="price_HGd7M3DV3IMXkC" \ -d "automatic_tax[enabled]"="true" \ -d "payment_behavior"="default_incomplete"sk_test_4eC39HqLyjWDarjtT1zdp7dc
Setting this parameter causes all subsequent Invoices to be created with automatic tax calculations activated.
To inspect the results of the latest tax calculation, retrieve the latest Invoice of a Subscription. You can do this by expanding the latest_invoice
field on any Subscription request, as in the examples above. You can retrieve the tax amounts from the tax
and total_tax_amounts
fields on the latest invoice, and also from the per-line item tax_amounts
fields.
The Invoice will have an automatic_tax
field showing the status of the calculation, with one of three possible states:
Status | Description | Possible Action |
---|---|---|
complete | Stripe Tax has successfully assessed the taxes on the payment. | You can retrieve the tax amounts from the tax and total_tax_amounts fields on the latest invoice, as well as the per-line item tax_amounts fields. |
requires_location_inputs | Stripe Tax did not have enough information to determine the customer’s location, and was unable to assess taxes. | Collect more information from a customer (such as a full street address) and update the customer.address field. |
failed | Internal Stripe error. | Try the request again, or contact Stripe support for additional assistance. |
If the tax calculation fails during a Subscription request that creates and pays an Invoice, the API returns an HTTP status 400 response.
Collect payment information to activate the subscriptionClient-side
To complete payment of the first invoice and activate the subscription, use stripe.confirmCardPayment
when your customer submits the form.
const btn = document.querySelector('#submit-payment-btn'); btn.addEventListener('click', async (e) => { e.preventDefault(); const nameInput = document.getElementById('name'); // Create payment method and confirm payment intent. stripe.confirmCardPayment(clientSecret, { payment_method: { card: cardElement, billing_details: { name: nameInput.value, }, } }).then((result) => { if(result.error) { alert(result.error.message); } else { // Successful subscription payment } }); });
The subscription automatically becomes active
upon payment. See our Subscriptions with Elements guide for more details on setting up your checkout page.
Handling location validation
We recommend validating a customer’s automatic_tax
status before attempting to create a subscription or one-off draft invoice.
Creating or updating a subscription or invoice will behave the following way if customer.tax.automatic_tax
returns unrecognized_location
:
- Creating or updating a subscription that causes an immediate invoice and payment attempt will error with an HTTP status 400 response.
- Creating or updating a draft invoice (either within the short window after a subscription cycle, or for a one-off invoice) will update the invoice’s
automatic_tax.status
torequires_location_inputs
. You can then either update the customer object to correct the address, and then update or finalize the invoice, or turn off automatic tax calculation. If you don’t take any action, the invoice will remain in adraft
state, regardless of the value ofauto_advance
.
If tax calculation fails due to an invalid customer location on a recurring Subscription Invoice, Stripe will send a invoice.finalization_failed
webhook when attempting to finalize the invoice.
Previewing a price before creating a customer or subscriptionOptional
Stripe also provides an endpoint for previewing an upcoming invoice for a subscription. You can use this endpoint to preview the initial invoice for a new subscription:
curl https://api.stripe.com/v1/invoices/upcoming \ -u
: \ -d "customer"="{{ CUSTOMER_ID }}" \ -d subscription_items[0][price]="{{ PRICE_ID }}" \ -d subscription_items[0][quantity]=1 \ -d "automatic_tax[enabled]"=truesk_test_4eC39HqLyjWDarjtT1zdp7dc
If you haven’t created a Customer yet, but you’ve collected your customer’s billing information, you can use the customer_details
parameter in the place of a Customer ID:
curl https://api.stripe.com/v1/invoices/upcoming \ -u
: \ -d "customer_details[address][line1]"="510 Townsend St" \ -d "customer_details[address][city]"="San Francisco" \ -d "customer_details[address][state]"="CA" \ -d "customer_details[address][country]"="US" \ -d "customer_details[address][postal_code]"="94103" \ -d subscription_items[0][price]="{{ PRICE_ID }}" \ -d subscription_items[0][quantity]=1 \ -d "automatic_tax[enabled]"=truesk_test_4eC39HqLyjWDarjtT1zdp7dc
When previewing the first invoice for a subscription, the subscription ID in the response will not point to a valid subscription.
You can also use this endpoint if you have an ongoing subscription without taxes enabled and would like to preview what the upcoming invoice would look like if you were to enable automatic tax.
curl https://api.stripe.com/v1/invoices/upcoming \ -u
: \ -d subscription="{{ SUBSCRIPTION_ID }}" \ -d "automatic_tax[enabled]"=truesk_test_4eC39HqLyjWDarjtT1zdp7dc