Table of Contents
Taxes. And Death. And More Taxes.
How Taxes Work in FoxyCart
A “tax” in FoxyCart is a standalone set of properties that define a particular tax that can be applied to your product categories. Your store can have zero tax rates or 100, and they can be applied individually or in parallel, but the best way to understand how taxes work in FoxyCart is to dive into the admin to look at the available options.
Creating a Tax Rate
The section below will discuss each individual option you'll see when you hit the “taxes” menu item in your store's admin.
- Tax
- This select box allows you to load previously configured taxes to view or edit. It also has options to add a new tax or to delete the current tax.
- Tax Summary
- This is an informational field that includes the tax name, tax rate, and tax type.
- Tax Name
- This is the name given to the tax that will be displayed to your customer and also included in API requests and in the transaction datafeed.
- Tax Type
- This select box has five options.
- Global will apply to the order regardless the shipping or billing information.
- Union will apply if the customer's shipping country (or billing country, if no shipping address is present) is an EU member state.
- Country will apply the tax only if the country entered in the tax's settings matches the customer's shipping country (or billing country, if no shipping address is present).
- Region will apply only if the country and state/province entered in the tax's settings match the customer's shipping address (or billing country, if no shipping address is present).
- Local will apply only if the country, state/province, and city entered in the tax's settings match the customer's shipping address (or billing country, if no shipping address is present).
- Country, Region, City
- Location data to be used for the tax type, above.
- Tax is Calculated Automatically
- This option allows you to set
country
orregion
taxes to set tax rates automatically. See below for more details. - For the “Union” tax type, this option allows the “normal” VAT rates per country
- Tax Service Provider
- Choose between our currently supported tax rate service providers, currently including Avalara, Thomson Reuters, and others.
- Tax Rate (%)
- This is the tax rate to be applied as a percentage. So if a tax is 17.5%, enter
17.5
, not0.175
. - This input will not be shown if you have automatic tax calculation selected.
- Use the Origin Country Tax Rates
- For EU VAT as of 2015, there may be situations where you need to charge your store's country's VAT rate instead of the customer's country's rate. This option lets you set a single country's rate to be applied for all EU member states.
- Exempt Customers With a Tax ID
- Removes the tax if a customer has a Tax ID. More info here.
- Apply to Shipping & Handling
- If checked, then when this tax applies to a product, it will also be collected on the amount charged for shipping and handling. Useful if you must collect tax on the freight you charge to your customer as part of the sale.
- This input will not be shown if you have automatic tax calculation selected, as that is determined automatically.
- Applied to Categories
- This will list categories that currently apply the tax. See the below section for more info.
Taxes and Categories
Taxes, by themselves, don't actually do anything. In order for a tax to be applied it must be associated with a category. Once associated, any products in that category will get the appropriate taxes applied. To associate a tax with a category, simply check the appropriate checkbox(es) when you edit your category.
How Multiple Taxes Work Together
If you have multiple non-3rd party taxes (ie. not AvaTax, TaxJar, etc.) to be applied in one transaction they will be applied individually. Here's a quick example of what your cart could look like on pageload on checkout, when certain taxes are “TBD” because the address info hasn't been completed yet:
If you are using AvaTax, TaxJar, or another 3rd-party tax system, that service will override any other taxes configured in Foxy. All taxes must be configured through the 3rd party.
Taxes and Billing v. Shipping Addresses
FoxyCart applies taxes based on the shipping address. If a separate shipping address is not specified, the billing address is assumed to be the shipping address, and taxes are applied accordingly. While there is some debate online as to whether or not this is correct, we have asked multiple tax experts and tax rate services, and this approach is what we believe to be the most correct at this point.
Tax-Exempt Customers
To allow customers to opt out of taxes, you first need to understand how the “exempt customers with a tax id” checkbox works. If you've enabled this option and a customer checks out with a non-empty customer_tax_id
value, FoxyCart will remove the appropriate taxes.
To enable the option to exempt customers with tax id, first go to your Taxes Settings and select the tax for which you'd like to offer the exemption. Check the exempt customers with a tax id:
option and save. You'll need to repeat this for each tax you have set up where you'd like to offer the exemption.
You can add a Customer Tax ID input to your checkout easily in the template configuration page of your admin, under the checkout → “Customize which checkout fields are shown and required” section. Here you can hide the field (default), make it optional, or make it required.
To pass the tax ID value for a customer from your own website through the cart, you can do that by making use of prepopulation. This simply means calling your cart with the name of the field included, and can be with a product add to cart, or a separate JSONP call to the cart. Because the tax ID could be in the billing or the shipping address, there are two possible fields you'll need to prepopulate, shipping_tax_id
and billing_tax_id
. As an example of doing that with JSONP:
var customer_tax_id = "#########"; FC.client.request('https://'+FC.settings.storedomain+'/cart?shipping_tax_id=' + customer_tax_id + '&billing_tax_id=' + customer_tax_id);
If you want to run that code on your own website - it will need to be wrapped within an onLoad
function and a ready.done
event function to ensure it runs when the FoxyCart javascript has initialised. That would look like this:
var FC = FC || {}; FC.onLoad = function() { FC.client.on("ready.done", function() { var customer_tax_id = "#########"; FC.client.request('https://'+FC.settings.storedomain+'/cart?shipping_tax_id=' + customer_tax_id + '&billing_tax_id=' + customer_tax_id); }); }
You can also add this value via the API, if you'd like to control things more tightly. (We plan on adding the ability to modify customers in our admin in a future release.) Even if the field is not visible on the checkout, if the customer has a customer_tax_id
, the taxes will behave as expected on the checkout. In this way, you could have that field hidden, and manually apply tax ID numbers (via an API script) after doing your own validation, and allow only approved customers to be tax-exempt.
Note that no validation on the number is performed.
Tax-Inclusive Pricing
For users in countries where tax-inclusive pricing is common, Foxy allows tax-inclusive pricing. This can get a little more complicated than you might anticipate, so please test this functionality (particularly if you're using coupons and discounts) to ensure thing function as expected for your region.
Foxy's tax-inclusive functionality is largely a display-only change, and is set (inclusive or exclusive) on the template set. (As such, you cannot display both tax-inclusive and tax-exclusive in the same cart.) The price
value of your products will always be the tax-exclusive value.
This is easiest to communicate by way of example. Assume an $83.33 product and a 20% tax. In both cases, the item should be price=83.33
. (Note that there may be situations where, due to rounding, you may need to specify a more accurate price, such as price=83.3333
. Foxy can handle that specificity to fix rounding issues.)
- In a tax-exclusive cart, the item will be displayed as $83.33, the tax will be $16.67, and the order total will be $100.
- In a tax-inclusive cart, the item will be displayed as $100, the tax will be $16.67 (and will show an “inclusive” note in the heading), and the order total will still be $100.
In that sense, this can be considered a “display only” change. All other aspects of Foxy's tax functionality remain effectively unchanged. For example, a tax-exempt customer will pay the price
value of the item(s) in the cart, and it will look identical to how it'd look for a tax-exclusive cart.
Please also review the coupon and discount documentation if you're doing tax-inclusive pricing with coupons or discounts, as that has some possibilities that each individual merchant (and/or their accountants) will want to review.
Rounding within tax calculation
If you're utilising Avalara Avatax, TaxJar or Thomson Reuters ONESOURCE, we utilise the tax figure that they pass us, so you shouldn't see any discrepancies there.
For any taxes that are set with a manual rate, or set to calculate automatically through the “FoxyCart Default” option, some rounding does occur within the calculations. Taxes are calculated on three distinct portions of the order, depending on the aspects that are included. These are on the product subtotal per category, on any taxable coupon discounts, and shipping if any taxes are configured to tax that. Each of these totals are rounded to 2 decimal places and combined together to form the final taxes displayed to the customer.
Automatically Calculated Taxes
If the “Tax is Calculated Automatically” checkbox is checked for a given tax, taxes will be calculated automatically within the designated area for that tax. The default service provider allows for automatic rates in United States and Canada, or when the “Union” tax type is set. If you have an Avalara, TaxJar or ONESOURCE account, you can also configure that native integration for live tax rates in all countries and regions they support.
FoxyCart Default Automatic Tax Rates in the US and CA
As an example of auto-calculated tax, if you have a tax set for California that is set to calculate automatically, FoxyCart will query the tax database based on the customer's zip code and/or city, and apply the appropriate tax rate (which may include state, county, city, or other tax jurisdictions). This automatic lookup will dictate if the tax should apply to shipping or not, and if it relates to the billing or shipping address.
Our tax database is provided by Thomson Reuters ONESOURCE (formerly Tax Data Systems), which is updated on a monthly basis. Note that neither FoxyCart nor Thomson Reuters makes any warranty or claim as to the accuracy of the tax data returned, but many very large merchants use Tax Data Systems. (Note that we also support ONESOURCE Indirect, which is a separate integration suited for medium and large businesses.) Currently we use a two different versions of the database:
- The default approach is to use the more accurate database, which provides the tax rate based on the city name. When a customer enters their ZIP code, all matching city names are returned. The customer can then select the appropriate city, and the corresponding tax rate will be used.
- The other database provides a single rate per zip code that provides a single tax rate per zip code. In the event there are multiple tax rates in a single zip code, the more populated zip code's rate is used. This database is used if the ZIP to city autocompletion is disabled in the template configuration.
Found an Incorrect Tax Rate?
Though the Onesource database is generally very accurate, it's not perfect. If you believe you've got a transaction with an incorrect tax rate, please do the following:
- Get the Foxy transaction ID and shipping ZIP code. (If no separate shipping address is present, get the billing ZIP code.)
- Find at least one 3rd party reference to the rate you believe to be correct. If possible, find your official state website that shows what the correct tax should be.
- Contact us with the transaction ID, ZIP code, and links showing the proper tax rate for that zip code.
We will then contact the tax database provider to alert them. Typically, the rate will then be corrected on the next monthly update. Please note that we can't guarantee whether updates will be made, but we can say from experience that updates do tend to be pretty quick.
If this uncertainty presents too significant a challenge, we do support other tax rate integrations with AvaTax, TaxJar, and Thomson Reuters ONESOURCE Indirect.
Automatic EU VAT Tax Rates
The EU VAT is complicated, and the changes at the beginning of 2015 certainly didn't help. While we're not tax experts on our end, we have tried to give enough flexibility for any merchant that needs to collect the EU VAT. We encourage you to familiarize yourself with the options on the “Union” tax, but here are a few notes:
- You can automatically apply the correct VAT rate based on the customer's country, as required for some products by the 2015 changes.
- To do this, create a “Union” tax type, turn on automatic calculations, and leave the “origin country tax rates” set to “no”.
- You can alternately automatically apply your store's country's VAT tax rate, for other types of products (as of 2015).
- To do this, create a “Union” tax type, turn on automatic calculations, and change the “origin country tax rates” set to “yes”. Then set the country to be the country whose rates you'd like to apply to all EU customers.
- You can also apply an arbitrary rate if a customer is in any EU member state.
- To do this, create a “Union” tax type, turn off automatic calculations, and set the rate as desired.
- And you can apply normal country-based tax rates individually. So if you have specific products that have alternate rates that vary by country, you can create ~30 different country-based tax rates as needed.
- We use this open source tax library for our rates, which takes its data from Europa.eu. Please note that there is no warranty or claim of accuracy for the automatic tax rates we provide, though we will certainly update them if an issue is brought to our attention.
Please note that the EU VAT requirements are many, and we cannot guarantee our functionality as it relates to any country's tax requirements. At this point, there are a few known issues you should be aware of:
- We are still unclear on the requirements for displaying taxes including the VAT. Since certain products now (as of 2015) are taxed at the VAT rate of the customer, this would make it significantly more difficult to actually display a price to a customer until you know with near 100% certainty what country the customer is located in.
- The new rules for customer-based VAT rates require two separate corroborating pieces of data to confirm a customer's country. Our geo IP database determines a customer's country with 99%+ accuracy, and the customer then provides their own address. That said, it's technically possible for a customer to change their country and have a mismatch, which might not be enough for the EU VAT requirements.
- Please be aware of any data retention requirements, and note that FoxyCart does purge data from inactive stores occasionally. (So if you cancel your FoxyCart subscription, you'll need to keep your own backups as necessary.)
- As noted above, our Tax ID field currently does not have any validation. More info here.
Using 3rd Party Tax Integrations: Important Notes
The following notes apply for AvaTax, TaxJar, OneSource Indirect, and any other 3rd party tax integrations that may be added to Foxy. The custom tax endpoint has similar considerations.
tl;dr:
- You should only have 1 approach to taxes for your store. You should not mix an external tax provider (TaxJar, AvaTax, OneSource Indirect, custom tax endpoint) with flat rates or the built-in automatic rates.
- Only set a 3rd-party tax integration at the country level. Details below.
- You should approach the tax integration as though all products will be sent to your tax service, all the time. If you have product categories that are not taxed, you must map that category in Foxy to an untaxed product type in your tax provider.
Basically, you should consider the tax provider to be the authority, and Foxy will do everything it can to ensure that system has all transaction data.
Tax Services Override Other Rates Within a Country
If your store is configured with an AvaTax, TaxJar, or OneSource Indirect tax rate for a specific country, that provider will always be the authority for transactions in that country, even if the tax configuration in Foxy doesn't specify using that provider for a specific region (state, province, country, etc.). This ensures that the correct tax is charged even if you forgot to update your Foxy settings to match your AvaTax settings.
For instance, if you only have a state-level tax configured in Foxy to use AvaTax for California, but you have California and Texas configured in AvaTax, the customer will ultimately be charged Texas sales tax, even though no taxes for Texas are configured in Foxy. This is because Foxy will always defer to AvaTax, if any AvaTax taxes are configured. Similarly, if you have another tax rate (for instance, a static rate) configured in Foxy but not in AvaTax, that tax will ultimately be ignored, as AvaTax is the authority.
There are four important items to note here:
- AvaTax, TaxJar, OneSource Indirect, or other tax integrations we support should be configured at the country level. (Currently we do allow them to be configured at the state/province level, but we will be disallowing this in a future update.) Note that we aren't saying you should collect taxes in all 50 states. The tax service will know which states you should collect in.
- If you're using TaxJar, AvaTax, or another 3rd party provider, you should only use that provider, if possible. If you do need to configure rates not with your provider, you must not mix and match within a single country. (Though this is currently allowed in the Foxy admin, we will be adding limitations to the admin to prevent this.)
- Using multi-ship? At present, AvaTax is the only provider that supports multiship. (Technically, ONESOURCE Indirect can support it, but if you're interested in that please contact us to discuss a custom implementation.)
- You should not ever use different providers within a single store.
Possible Mismatch between Displayed & Charged Taxes
With the tax provider integrations (Avalara, TaxJar, OneSource) above, Foxy will always use the tax total returned from the provider when charging the customer. But… as of Foxy v2.0, the rates displayed to the customer prior to completing their transaction (on the cart and checkout) are calculated dynamically by Foxy based on the tax rate(s) provided by the provider. This may result in the tax displayed to the customer (prior to completing the checkout and proceeding to the receipt) being slightly off (typically by 1¢ due to rounding; if the amount is off by more, it should generally be lower than what's displayed to the customer).
When the customer completes their transaction, the amount of tax charged and displayed on the receipt and in the admin will match exactly what was returned from the provider.
If this may present a problem for your store, we suggest adding an “(estimated)” text. You can do this by changing the “Tax” language string in the Foxy admin to “Tax (estimated)”, in the “language” page of your store's admin. Please contact our support team for assistance with this if you'd like help.
Avalara AvaTax 15 Automatic Tax Rates
If you have an Avalara account, you can add your credentials to the integrations page as shown (Here, the service url:
is for a sandbox environment. If you're using an Avalara production account, you should use https://avatax.avalara.net) :
Important Notes
- We recommend configuring a country tax using AvaTax.
- Please review the notes above to ensure you're familiar with how AvaTax works with Foxy.
- Always be sure to keep your service url correct according to whether or not you're using a live Avalara account or a development account.
- FoxyCart uses the category code for products as the tax code sent to Avalara, and Avalara sets a limit on the tax code to a maximum of 25 characters. If your category code exceeds that length, we trim the code to be 25 characters. To ensure things behave as you expect, we recommend setting your category codes to not be longer than 25 characters.
- After you have input your Avalara credentials, click the test button to ensure they are connecting correctly.
For more information about each setting, see the inline help in the admin by clicking on the ? box. If you have further questions about the configuration information here, please contact Avalara.
Please see the “Possible Mismatch between Displayed & Charged Taxes” section above.
Once your native Avalara integration is configured, you can select it as a service provider when configuring a tax.
Address Validation
The “use address validation” option within your Avalara settings will ensure customer addresses used on the checkout page in the supported countries you specify are validated via Avalara's address validation system and confirmed by the customer as shown below:
If the address is invalid, your customers will see this message, and they will still be able to complete the checkout or modify the address:
TaxJar Automatic Tax Rates
We support TaxJar for automatic rate calculation as well.
Please review the notes above to ensure you're familiar with how TaxJar works with Foxy.
If you have an TaxJar account, you can add your credentials to the integrations page as shown under “native integrations”:
- Enter your TaxJar api token from your TaxJar account.
- Map your FoxyCart categories to TaxJar's product tax codes to ensure the proper values are sent to TaxJar for the products in the categories.
- If you'd like to commit the details of the automatic tax calculations to TaxJar as an Order Transaction, check the box for “create invoices”.
For more information about each setting, see the inline help in the admin by clicking on the ? box. If you have further questions about the configuration information here, please contact TaxJar.
Please see the “Possible Mismatch between Displayed & Charged Taxes” section above.
Once your native TaxJar integration is configured, you can select it as a service provider when configuring a tax:
Thomson Reuters ONESOURCE
We support ONESOURCE for automatic rate calculation as well. There are many different systems within the ONESOURCE brand, so please contact us for more details. Some things to keep in mind:
- Our ONESOURCE integration requires an Advanced or Enterprise Plan.
- Please review the notes above to ensure you're familiar with how external tax systems work with Foxy.
- All product categories should be configured to use OneSource to get tax rates. Please avoid mixing OneSource and non-OneSource tax rates in the same cart or transaction.
- To that end, a store should only and always use OneSource. If different tax rate providers are mixed and matched, the displayed tax total will likely not line up with what's actually charged to the customer and displayed on the receipt.
- Please see the “Possible Mismatch between Displayed & Charged Taxes” section above.
Please let us know if you have any questions.
Custom Tax Endpoint
If you have specific tax integration requirements, or a separate 3rd party system we don't support, you can use the custom tax endpoint functionality. This is similar to our custom shipping endpoint functionality. Foxy will send a payload (mirroring the API format) to your endpoint, and you can respond back with a response like this:
{ "ok":true, "details":"", "name":"Custom Tax", "expand_taxes":[ { "name":"Super Tax", "rate":0.045, "amount":0.23 }, { "name":"Mega Tax", "rate":0.053, "amount":0.27 }, { "name":"Little Tax", "rate":0.021, "amount":0.11 } ], "total_amount":0.6, "total_rate":0.119 }
Attribute | Description |
---|---|
ok | Show that tax was calculate normally or there is some error. |
details | If you have any errors write them here. |
name | This name will be shown on the cart/checkout page. |
expand_taxes | More detail description of applied taxes, should be at least one item. These details are shown on receipt and in the admin, so if there is more than one tax included within expand_taxes , they will be shown individually on the receipt and in the admin. |
expand_taxes.name | Name of the individual tax. |
expand_taxes.amount | Tax amount for the individual tax. |
expand_taxes.rate | Rate for the individual tax, as a decimal (5% is 0.05 ). |
total_amount | Total tax amount, this amount is used for adding to the total order amount. Should be the combined total of the tax amount s within the expand_taxes array. |
total_rate | Total combined tax rate, as a decimal (5% is 0.05 ), shown on the cart/checkout page. Should be the combined total of the rate s within the expand_taxes array. |
If you want to return a successful response that has no taxes to display, you can return a payload as follows:
{ "ok": true, "details": "", "name": "", "expand_taxes": [ ], "total_amount": 0, "total_rate": 0 }
An example of an error response payload is as follows. NOTE that this will prevent customers from checking out. Similarly, if we don't get a valid response from your custom endpoint, it will display a default error and prevent the checkout.
{ "ok":false, "details":"Your error" }
To display taxes without a % rate omit the rate
param:
{ "ok": true, "details": "", "name": "", "expand_taxes": [ ], "total_amount": 0 }
To display 0% taxes, set the rate to 0
:
{ "ok": true, "details": "", "name": "Zero Amount", "expand_taxes": [ { "name": "Tax", "rate": 0, "amount": 0 } ], "total_amount": 0, "total_rate": 0 }
Carts and Transactions
There are currently the following types of requests, differentiated by a header named foxy-webhook-event
with the following values:
tax/calculate
: This is forcart
resources and mostly mirrors the custom shipping endpoint (which itself looks like the API's ''cart'' resource).tax/report
: This happens when the transaction is completed, and the request body mirrors a zoomed ''transaction'' API resource.tax/refund
: Similar totax/report
, but sent when a full refund is issued.tax/void
: Identical totax/refund
, but sent when a payment is voided. (For accounting or reporting purposes, this may necessitate different handling on your end.)tax/update
: Similar totax/report
, but sent when a partial refund is issued.
There is a separate header to indicate the trigger for the event. Currently we only have one trigger:
checkout/submit
: This accompanies atax/calculate
event, and will only be present if the customer has clicked the button to submit their checkout. In other words, this header is not when the cart changes, or when the checkout updates with the customer's address. It happens right before the pre-payment webhook.
Starter Code
Foxy has a Netlify function available for the Custom Tax Endpoint to help you get started. You'll need a Netlify account, then you can follow the directions on our Netlify repo on GitHub and follow the instructions in the readme for setting up the function.
The repository provides an example function, which you can tailor to your needs.
Current Limitations and Future Improvements
3rd Party Tax Service Integrations
Cumulative Taxes
Though cumulative taxes can almost always be supported (with FoxyCart's current functionality) with just a little bit of basic algebra, please contact us if this is of interest to you.