> ## Documentation Index
> Fetch the complete documentation index at: https://funnelfox.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Taxes in Web SDK

> Show the total with tax in your checkout when FunnelFox Payments Taxes is on

When [FunnelFox Payments Taxes](/docs/billing/taxes.md) is on for your organization,
the Web SDK reports the total and the tax through callbacks. The SDK doesn't
show tax in the checkout, so update the prices on your page from
`onTaxChange`.

> [!NOTE]
>
> Tax follows your Taxes setup in the FunnelFox Payments dashboard. There's no
> SDK option to turn it on or off.

## How it works

1. When the checkout loads, the SDK reports a tax estimate for the buyer's
   location, detected from their IP.
2. If your organization collects the customer's location, the card form
   shows country and zip code fields. When the buyer changes them, the SDK
   recalculates tax and reports the new total.
3. Before a card payment, the SDK finishes any pending recalculation, so the
   charge matches the address the buyer entered.

Learn how to collect the customer's location in
[Customer address](/docs/billing/taxes.md#customer-address).

## Tax callbacks

Each method supports these callbacks:

| Method | Callbacks |
| - | - |
| [createCheckout()](/docs/develop/create-checkout.md) | `onTaxChange`, `onTaxPending`, `onTaxError` |
| [Billing.initMethod()](/docs/develop/init-method.md) | `onTaxChange`, `onTaxPending`, `onTaxError` |
| [Billing.stripe.createCardForm()](/docs/develop/stripe.md#create-card-form) | `onTaxChange`, `onTaxError` |
| [Billing.stripe.purchaseWallet()](/docs/develop/stripe.md#purchase-with-wallet) | `onTaxChange` |

**onTaxChange** `(info: TaxInfo) =\> void`

Called with the total and the tax. See when it's called below.

**onTaxPending** `() =\> void`

Called as soon as the buyer changes their country or zip code. The SDK
recalculates tax 600 ms after the last change. Use this callback to show
that the price is updating.

**onTaxError** `(error: Error) =\> void`

Called when a recalculation fails. The error is an `APIError` or a
`NetworkError`.

### When onTaxChange is called

* **`createCheckout()`**: When the checkout is ready, after each
  recalculation, and after
  [updatePrice()](/docs/develop/checkout-instance.md#methods).
* **`Billing.initMethod()`**: When the payment method renders, and after each
  recalculation in the card form.
* **`Billing.stripe.createCardForm()`**: When the form mounts, and after each
  recalculation as the buyer edits the address.
* **`Billing.stripe.purchaseWallet()`**: Once, before the payment sheet
  opens, with the tax the sheet shows.

> [!NOTE]
>
> The Apple Pay and Google Pay sheet in `Billing.stripe.purchaseWallet()`
> charges the tax estimate for the detected location. The amount doesn't change
> with the address the buyer picks in the sheet.

## TaxInfo

**amountTotal** `number`

The total the customer pays, in minor units (for example, cents).

**taxAmount** `number`

The tax to show the buyer, in minor units. With exclusive tax, it's the tax
added on top of the price. With inclusive tax, it's the tax already inside
the price. `0` when unknown.

**currency** `string`

The currency code.

**taxBehavior** `'inclusive' | 'exclusive'`

Whether tax is part of the price or added on top. `undefined` when unknown.

**taxRate** `number`

The total tax rate as a percentage, for example `21` or `8.875`. Show this
value instead of computing the rate from rounded amounts. Omitted when
FunnelFox Payments sends no tax breakdown.

**taxBreakdown** `TaxBreakdownEntry[]`

The tax per jurisdiction. Omitted when FunnelFox Payments sends no tax
breakdown.

**TaxBreakdownEntry properties**

**amount** `string`

The tax amount for this jurisdiction as a decimal string, for example
`"1.73"`.

**percentage** `string`

The tax rate for this jurisdiction, for example `"21"`.

**inclusive** `boolean`

Whether this tax is part of the price.

**taxType** `string | null`

The tax type, for example `"vat"`.

**country** `string | null`

The country of the jurisdiction.

**subdivision** `string | null`

The state or region of the jurisdiction.

**taxabilityReason** `string | null`

Why the tax applies, as reported by the tax provider.

## Show total

Amounts arrive in minor units. Convert them with the currency's number of
decimal places before you show them:

```javascript theme={null}
import { createCheckout } from '@funnelfox/billing';

const priceEl = document.getElementById('price');
const taxEl = document.getElementById('tax-note');

function formatAmount(minorUnits, currency) {
  const formatter = new Intl.NumberFormat(undefined, {
    style: 'currency',
    currency,
  });
  const digits = formatter.resolvedOptions().maximumFractionDigits;
  return formatter.format(minorUnits / 10 ** digits);
}

const checkout = await createCheckout({
  orgId: 'your-org-id',
  priceId: 'price_123',
  customer: { externalId: 'user_456', email: 'user@example.com' },
  container: '#checkout-container',

  onTaxPending: () => {
    priceEl.classList.add('is-updating');
  },
  onTaxChange: info => {
    priceEl.classList.remove('is-updating');
    priceEl.textContent = formatAmount(info.amountTotal, info.currency);

    const tax = formatAmount(info.taxAmount, info.currency);
    const rate = info.taxRate !== undefined ? ' (' + info.taxRate + '%)' : '';

    if (info.taxAmount > 0 && info.taxBehavior === 'exclusive') {
      taxEl.textContent = 'Total includes ' + tax + ' tax' + rate;
    } else if (info.taxAmount > 0 && info.taxBehavior === 'inclusive') {
      taxEl.textContent = 'Price includes ' + tax + ' tax' + rate;
    } else {
      taxEl.textContent = '';
    }
  },
  onTaxError: () => {
    priceEl.classList.remove('is-updating');
  },
});
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.