Skip to main content
PayNext integrates with tax providers to automatically calculate, collect, and report taxes on your transactions. Configure tax once, and PayNext handles the rest—from calculating the correct rate based on customer location to reporting transactions for filing. See Integrations for supported tax providers.

How it works

1

Customer initiates payment

PayNext sends transaction details to your tax provider.
2

Tax calculated

Provider returns tax amount based on customer location and your tax behavior setting (exclusive or inclusive).
3

Payment authorized

Payment authorized for the total amount (subtotal + tax for exclusive, or full amount for inclusive).
4

Payment settles

Tax transaction submitted to provider for reporting.
5

Refund processed (if applicable)

Tax transaction reversed with provider.

How tax location is determined

PayNext determines the customer’s tax jurisdiction based on the payment method and available data:
Payment methodTax location source
Apple Pay, Google PayBilling address from the wallet
All other methodszip_code and state passed in the customer object
FallbackCloudflare geolocation from device fingerprint
For US customers, pass zip_code and state in the customer object when creating the checkout session. These two fields are the minimum required for tax calculation—in most cases, the state can be detected automatically from the zip code. For the most accurate calculation, pass the full address object. If address data is not provided, PayNext uses geolocation data from the customer’s device. Learn more about what data PayNext collects to determine tax location.

Address data and audit risk

The accuracy of your tax location data affects audit risk. When a payment method provides a full billing address (like Apple Pay or Google Pay), PayNext automatically uses it for tax calculation.
ScenarioApproachAudit risk
Full billing addressStreet-level geocodingLow
Partial address (zip + state)State-level tax calculationLow
No address providedCloudflare geolocation (zip, state, city, coordinates)Medium
When no customer address is provided, PayNext collects fingerprint data that includes zip code, state, city, coordinates, and IP address from the customer’s device. This approach carries medium audit risk because:
  • Documented methodology — Consistent, defensible process with detailed location data
  • Regulatory precedent — Texas PLR guidance confirms IP-based geolocation is acceptable when addresses aren’t collected
  • Audit trail — All geolocation data is logged and available for compliance review

Location mismatch handling

When creating a client session with customer country set to US but no address data available (no zip_code/state provided and payment method is not Apple Pay or Google Pay), PayNext falls back to device fingerprint for tax location. If the fingerprint indicates the customer is outside the US (e.g., Canada), PayNext does not calculate US sales tax for that transaction. The payment processes without tax, and the API returns the “tax doesn’t apply” response structure. This occurs because the customer’s actual location—based on fingerprint data—is outside the tax provider’s supported jurisdiction.
Disclaimer: PayNext is not a tax provider. The information on this page is for informational purposes only and should not be considered tax advice. Audit risk assessments reflect our opinion, not guidance from a tax professional. Consult a qualified tax advisor for advice specific to your situation.

Configuration

Tax configuration follows a hierarchy—set defaults globally and override tax collection at the plan level when needed.

Global settings

Configure default tax behavior in Dashboard → Integrations → Tax:
SettingDescription
Tax providerYour connected tax provider (e.g., Numeral)
Tax behaviorexclusive (tax added on top) or inclusive (tax included in price)
Product categoryProduct category for tax rate determination—select from the drop-down menu. Each tax provider has its own product categories (e.g., GENERAL_MERCHANDISE, SAAS_GENERAL). Defaults to GENERAL_MERCHANDISE if not set.
Tax registrationsJurisdictions where you’d like to collect tax
Collect tax automaticallyWhen enabled, tax is calculated for all payments by default. When disabled, tax is only collected for plans explicitly configured to collect.

Plan-level settings

Override global tax collection for specific plans in Dashboard → Plans → [Plan Name]. Each plan has a collect_tax setting that controls whether tax is collected for payments under that plan:
collect_taxDescription
DEFAULTUses the global “Collect tax automatically” setting from your tax provider configuration
COLLECTAlways collect tax for this plan, regardless of the global setting
DONT_COLLECTNever collect tax for this plan, regardless of the global setting
When you retrieve plan details via API, the tax object reflects this configuration:
{
  "id": "plan_1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
  "name": "Pro Plan",
  "tax": {
    "collect_tax": "COLLECT"
  }
}
Use DONT_COLLECT for plans where tax should never apply (e.g., internal test plans or donation-based plans). Leave most plans on DEFAULT so they follow your global tax configuration.
Tax behavior (exclusive or inclusive) and product category are configured globally on the tax provider, not per plan. All plans that collect tax use the same behavior and product category.

Tax in API responses

All payments include a tax object. The structure varies based on whether tax was calculated for the transaction.

When tax applies

When tax is enabled for the customer’s location, the tax object includes provider details and calculation status:
{
  "id": "pay_e8a1b2c3-d4f5-6789-abcd-ef0123456789",
  "amount": 10700,
  "amount_usd": 10700,
  "currency_code": "USD",
  "payment_status": "SETTLED",
  
  "tax": {
    "provider": {
      "id": "b2c3d4e5-6789-01ab-cdef-2345678901bc",
      "type": "NUMERAL"
    },
    "calculation_id": "calc_xxx",
    "transaction_id": "txn_xxx",
    "amount_subtotal": 10000,
    "amount_subtotal_usd": 10000,
    "amount_tax": 700,
    "amount_tax_usd": 700,
    "behavior": "exclusive",
    "status": "submitted"
  }
}
When customers pay in other currencies, use amount_subtotal_usd and amount_tax_usd to see the USD equivalent for reporting.

When tax doesn’t apply

When tax is not enabled for the customer’s location (no tax provider configured, location not in your tax registrations, or plan set to DONT_COLLECT), the provider field is null and amount_tax is 0:
{
  "id": "pay_e8a1b2c3-d4f5-6789-abcd-ef0123456789",
  "amount": 10000,
  "amount_usd": 10000,
  "currency_code": "USD",
  "payment_status": "SETTLED",
  
  "tax": {
    "provider": null,
    "amount_subtotal": 10000,
    "amount_subtotal_usd": 10000,
    "amount_tax": 0,
    "amount_tax_usd": 0
  }
}
Use the provider field to determine if tax was calculated:
  • provider is present → tax was calculated, check status for the outcome
  • provider is null → tax not applicable for this transaction

Tax fields

FieldDescription
provider.idPayNext internal ID for your tax provider integration
provider.typeTax provider type (e.g., NUMERAL)
calculation_idTax provider’s calculation reference
transaction_idTax provider’s transaction reference (populated after settlement)
amount_subtotalRevenue amount pre-tax
amount_subtotal_usdRevenue amount in USD
amount_taxTax amount collected
amount_tax_usdTax amount in USD
behaviorexclusive or inclusive
statusTax lifecycle status (see below)
errorError details when status is failed (see When calculation fails)

Tax status lifecycle

StatusDescription
calculatedTax calculated during authorization, not yet reported to provider
submittedPayment settled, tax transaction reported to provider
reversedPayment refunded, tax transaction reversed with provider
failedCalculation or transaction submission failed (see error for details)

Amount relationships

payment.amount = tax.amount_subtotal + tax.amount_tax
FieldDescription
amountTotal charged to customer
tax.amount_subtotalRevenue portion (pre-tax)
tax.amount_taxTax collected

When calculation fails

If the tax provider doesn’t respond within 1.5 seconds or returns an error, PayNext processes the payment without tax to ensure you don’t lose revenue. The status is failed and an error object contains the failure reason. Failed transactions appear in Dashboard → Tax → Failed where you can review and submit them manually to your tax provider.
Error codeDescription
timeoutTax provider did not respond within 1.5 seconds
calculation_failedTax provider request failed (see message for details)
Provider-specific codesTax provider error types are mapped directly (e.g., INVALID_CURRENCY_CODE, CALCULATION_NOT_FOUND)
"tax": {
  "provider": {
    "id": "b2c3d4e5-6789-01ab-cdef-2345678901bc",
    "type": "NUMERAL"
  },
  "calculation_id": null,
  "transaction_id": null,
  "error": {
    "code": "timeout",
    "message": "calculation timeout"
  },
  "amount_subtotal": 10000,
  "amount_subtotal_usd": 10000,
  "amount_tax": 0,
  "amount_tax_usd": 0,
  "behavior": "exclusive",
  "status": "failed"
}
"tax": {
  "provider": {
    "id": "b2c3d4e5-6789-01ab-cdef-2345678901bc",
    "type": "NUMERAL"
  },
  "calculation_id": null,
  "transaction_id": null,
  "error": {
    "code": "INVALID_CURRENCY_CODE",
    "message": "Invalid customer currency code. Must be one of: EUR, RON, PLN, DKK, ..."
  },
  "amount_subtotal": 10000,
  "amount_subtotal_usd": 10000,
  "amount_tax": 0,
  "amount_tax_usd": 0,
  "behavior": "exclusive",
  "status": "failed"
}
When tax calculation succeeds during authorization but the transaction submission fails after capture, the status becomes failed while preserving the original calculation amounts:
"tax": {
  "provider": {
    "id": "b2c3d4e5-6789-01ab-cdef-2345678901bc",
    "type": "NUMERAL"
  },
  "calculation_id": "calc_xxx",
  "transaction_id": null,
  "error": {
    "code": "CALCULATION_NOT_FOUND",
    "message": "Unable to find calculation with ID: calc_xxx in test environment."
  },
  "amount_subtotal": 10000,
  "amount_subtotal_usd": 10000,
  "amount_tax": 700,
  "amount_tax_usd": 700,
  "behavior": "exclusive",
  "status": "failed"
}
Tax calculation failures are rare. When they occur, absorbing the tax cost is preferable to declining payments and losing revenue.

Webhooks

PayNext sends tax data in the tax object of payment webhook payloads. The tax object is included in:
  • payment.created — Initial payment with tax status: "calculated" (or "failed" if calculation errored)
  • payment.updated — After settlement (status: "submitted"), after refund (status: "reversed"), or after capture failure (status: "failed")
  • refund.created / refund.updated — Tax status: "reversed" when refund succeeds

Best practices

Set your global default to exclusive. This lets you display pre-tax prices and add tax at checkout—the standard approach for US and B2B sales. Override to inclusive only for specific markets (like EU B2C) where tax-inclusive pricing is expected.
You can start calculating and collecting tax before you’re officially registered in a jurisdiction. Once registered, most tax providers support backfilling historical transactions. PayNext returns $0 tax for customers in locations where you haven’t enabled tax collection in Integrations.
For reporting convenience, use the _usd fields (amount_subtotal_usd, amount_tax_usd) to avoid currency conversion complexity. Your final tax liability is available in your tax provider’s dashboard (e.g., Numeral)—use that information for your accounting and filing.