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

# Tax integration

## What you get from this chapter

After reading this chapter, you can:

* Choose pre-tax pricing, both prices, or tax-inclusive pricing based on how you price your products and whether you need to declare a tax amount you calculated yourself.
* Know which tax items Waffo must configure, and which business rules you need to confirm before integrating.
* Build payment and refund requests correctly, and handle amount changes, empty values, and errors in inquiry and Webhooks.

One-time payments (`ONE_TIME_PAYMENT`) support pre-tax pricing, both prices, and tax-inclusive pricing.

The payment request examples below use [`POST /api/v1/order/create`](/docs/api-reference/order-create/create-new-order), and the result is read through [`POST /api/v1/order/inquiry`](/docs/api-reference/order-inquiry/order-inquiry).

Subscription supports only pre-tax pricing; both prices and tax-inclusive pricing are not supported. For request fields, see [Subscription create API](/docs/api-reference/subscription-create/create-subscription).

## Choose how to pass the amounts

First decide whether your product prices already include tax, then decide whether you need to declare the tax amount you calculated to Waffo. The three approaches are determined by the combination of amount fields in the request.

| Your business need | Recommended approach | Why choose it |
| - | - | - |
| You maintain pre-tax product prices and want Waffo to calculate tax from the applicable configuration and the actual payment details | Pre-tax pricing | You provide only the pre-tax amount; any applicable customer tax is added to the payment amount |
| You want your own system to calculate the tax amount, and to show the customer the tax amount and the tax-inclusive total before payment | Both prices | You submit both prices and the country; Waffo validates or accepts the declared value according to the configuration, and calculates the final tax amount in the later payment flow |
| You maintain a tax-inclusive total and want Waffo to separate the pre-tax price and the tax amount | Tax-inclusive pricing | You provide only the tax-inclusive total and Waffo completes the split; you do not need to separate the tax yourself first |

When you calculate the tax yourself, confirm the [validation policy for your tax calculation](#validation-policy-for-your-tax-calculation).

After you pass a tax-inclusive price, discounts, currency conversion, and fees can still affect the amount the customer finally pays. For field combinations and examples, see "Integration details" below.

## Tax items configured by Waffo

The following tax items are configured by Waffo. Tell Waffo the transaction behavior you need, and confirm the Sandbox and Production configurations separately.

| Configuration item | Configurable content | Impact on your integration |
| - | - | - |
| Tax service scope | `Calculation Only` / `Full Tax Service` | Determines whether Waffo is responsible for tax remittance; see [Differences between the tax service options](#differences-between-the-tax-service-options) for the division of responsibilities |
| Product tax category mapping and default tax category | Map product names to tax categories, and specify the default tax category; when there is no match, reject the transaction or use the default tax category | Confirm the mapping of `goodsInfo.goodsName` and the handling of unmatched products with Waffo; a name change may affect tax calculation |
| Validation policy for your tax calculation | `Strict` / `Loose`, see [Option description](#validation-policy-for-your-tax-calculation) | Applies only to both prices; determines how differences in the declared tax amount are handled |

### Differences between the tax service options

* **`Calculation Only`**: Waffo calculates the tax amount and collects it from the customer at payment time, then remits the collected tax to you through Settlement; you are responsible for remitting it to the tax authority. After tax extraction completes, each tax amount can be read from `orderTaxAmount` in the [Order inquiry API](/docs/api-reference/order-inquiry/order-inquiry) and the corresponding Webhook.
* **`Full Tax Service`**: Waffo handles the tax amount calculation, the tax collection, and the remittance; before integrating, confirm the applicable scope and the division of responsibilities with Waffo.

### Validation policy for your tax calculation

This configuration applies only to both prices. Pre-tax pricing and tax-inclusive pricing do not use this declared tax amount validation.

**Strict**

Compare the tax amount you declared with the Waffo calculated value. On a mismatch, Waffo can be configured either to reject the transaction or to overwrite it with the Waffo calculated value.

**Loose**

Accept the declared tax amount without comparing it with the Waffo calculated value. For example, with a pre-tax price of `100.00` and a tax-inclusive price of `110.00`, the declared tax amount is `10.00`; `Loose` accepts that declared value and still checks the amount combination, the tax configuration, and the required parameters.

Accepting the declared value at creation does not mean that the final tax amount or the customer payment is settled; rely on order inquiry or Webhook.

## Integration details

### Before you integrate

Tax configuration is managed by Waffo. Before integrating, provide the Merchant ID, the Sandbox or Production environment, the country of sale, and the product category, and confirm that the environment already has the required tax agreement and payment methods.

The tax-inclusive price on this page is a price in the order currency. It does not mean that the Merchant bears the tax, and it is not guaranteed to equal the amount the customer actually pays after currency conversion, fees, and discounts.

### Passing parameters when creating an order

All amounts use strings, and the currency is `orderCurrency`. Provide at least one amount basis; the tax-inclusive price must be greater than zero and meet the currency precision requirements. Country codes use ISO 3166-1 alpha-3, for example `PHL`. See [Amounts and currencies](/docs/en/developer-docs/core-concepts/currency).

| Approach | `orderAmount` | `taxInfo.orderAmountInclTax` | `taxInfo.taxCountry` | Effect |
| - | - | - | - | - |
| Pre-tax pricing | Pass the pre-tax price | Do not pass | Do not pass | Waffo calculates tax from the matching configuration; any applicable customer tax is added to the payment amount |
| Both prices | Pass the pre-tax price | Pass the tax-inclusive price, not less than the pre-tax price | Required | Waffo processes the declared tax represented by the difference between the two prices according to the configuration, then calculates the final tax amount through the actual payment flow |
| Tax-inclusive pricing | Do not pass | Pass the tax-inclusive total | Do not pass | Waffo extracts the tax from the tax-inclusive amount; after tax extraction completes, `orderAmount` becomes the pre-tax amount |

For pre-tax pricing, omit the entire `taxInfo` object. Passing an empty object `{}` also returns `A0003` because the field combination is invalid. Omitting `taxInfo` does not mean requesting a tax exemption.

The following JSON only shows the tax-related fragment of the order creation request. You still need to fill in the common parameters such as Merchant, payment method, customer, request identifiers, time, and signature.

To compare the three approaches, assume the order and payment currency are both USD, the applicable tax rate is 10%, and there are no discounts or extra fees. The tax rate and amounts below are used only to explain the calculation; they do not represent the actual tax rate of `PHL` or your tax agreement; the result when passing both prices depends on the declared-value validation policy in your actual configuration.

**Pre-tax pricing**

```json theme={null}
{
  "orderCurrency": "USD",
  "orderAmount": "100.00"
}
```

Under the assumptions above, the tax is `10.00` and the customer pays `110.00`. If there is no matching taxable configuration, you cannot conclude from this example alone that `10.00` will always be added.

**Both prices**

```json theme={null}
{
  "orderCurrency": "USD",
  "orderAmount": "100.00",
  "taxInfo": {
    "taxCountry": "PHL",
    "orderAmountInclTax": "110.00"
  }
}
```

The difference between the two prices, `10.00`, is the tax amount you declare. Waffo processes that difference according to the declared-value validation policy of the tax agreement and checks the available payment methods. The specific behavior is determined by the [validation policy for your tax calculation](#validation-policy-for-your-tax-calculation). Passing both prices does not modify your tax rate configuration.

**Tax-inclusive pricing**

```json theme={null}
{
  "orderCurrency": "USD",
  "taxInfo": {
    "orderAmountInclTax": "110.00"
  }
}
```

Under the assumptions above, after tax extraction completes, the pre-tax price `100.00` and the tax amount `10.00` are returned; tax is not added again on top of `110.00`. Do not pass `orderAmount` or `taxCountry` at the same time.

### Read the payment result

The creation response returns order identifiers, status, and next actions, without the tax amount fields in this section. After creating the order, use [`POST /api/v1/order/inquiry`](/docs/api-reference/order-inquiry/order-inquiry) to read these fields. Order inquiry and `PAYMENT_NOTIFICATION` return the tax fields in the table below. Inquiry fields are in the response's `data` object; for the outer Webhook structure, see [Webhook event types](/docs/en/developer-docs/webhook/event-types).

| Field | Currency or meaning | Integration note |
| - | - | - |
| `orderCurrency` | Order currency | `orderAmount`, `orderTaxAmount`, and the tax-inclusive order amount are all understood in this currency |
| `orderAmount` | Pre-tax order amount after tax extraction | When tax-inclusive pricing has not yet been extracted, it may temporarily be the tax-inclusive amount; do not infer the final pre-tax price from an intermediate value at creation time |
| `orderTaxAmount` | Tax amount in the order currency | May be empty. Empty means tax has not yet been extracted or there is no tax result; do not treat it directly as a confirmed zero tax amount |
| `taxInfo.orderAmountInclTax` | Current final post-discount tax-inclusive order amount | Returned only for both prices or tax-inclusive pricing; after tax extraction completes it equals `orderAmount + orderTaxAmount`, not the original pre-discount request value |
| `taxInfo.taxCountry` | The declared country for tax calculation at creation | Only both prices has a declared value; under tax-inclusive pricing it may be empty, and it is not a universal echo of the final billing country |
| `finalDealAmount`, `userCurrency` | The amount the customer pays and its currency | Use them to reconcile what the customer actually paid after payment completes; do not add a tax amount in the order currency directly to an amount in another currency |

A pre-tax pricing response can have `orderTaxAmount` while having no `taxInfo`. Therefore, the presence of `taxInfo` cannot be used to determine whether tax is charged.

An intermediate state of tax-inclusive pricing can also show `orderAmount = "110.00"`, an empty `orderTaxAmount`, and `taxInfo.orderAmountInclTax = "110.00"` at the same time. In this case, do not subtract or add tax again. Keep your original request value, and reconcile the result against the status and amounts in the final inquiry or Webhook.

For the complete fields and response examples, see [Order inquiry API](/docs/api-reference/order-inquiry/order-inquiry).

`orderTaxAmount` is already included in the tax-inclusive total expression; do not add it to `finalDealAmount` again. When payment is not complete, do not treat the currently calculated amount as an amount already charged. For Webhook signature verification and status handling, see [Webhook overview](/docs/en/developer-docs/webhook/overview).

### How to pass refund amounts

The refund amount also uses the order currency. `refundAmount` means the pre-tax refund amount; whether you can pass `taxInfo.refundAmountInclTax` is determined by how the original payment order was submitted.

| Original payment order approach | Allowed refund submission | Not allowed |
| - | - | - |
| Pre-tax pricing | Pass only `refundAmount` | Pass a tax-inclusive refund amount |
| Both prices | Pass only `refundAmount`, or pass both `refundAmount` and `taxInfo.refundAmountInclTax` | Pass only the tax-inclusive refund amount |
| Tax-inclusive pricing | Pass only `refundAmount`, or pass only `taxInfo.refundAmountInclTax`, choose one | Pass both prices at the same time |

The following is a partial request for `POST /api/v1/order/refund`; you still need to fill in the common parameters such as the original order number, the refund request identifier, and the reason.

**Pre-tax amount only**

Applies to all three original payment order types. Pass only `refundAmount`, and Waffo calculates the corresponding tax refund.

```json theme={null}
{
  "refundAmount": "50.00"
}
```

**Both amounts**

Applies only to an original payment order that used both prices. Declare a pre-tax `50.00` and a tax-inclusive `55.00`; the difference is still processed according to the configured self-calculated tax validation policy.

```json theme={null}
{
  "refundAmount": "50.00",
  "taxInfo": {
    "refundAmountInclTax": "55.00"
  }
}
```

**Tax-inclusive amount only**

Applies only to an original payment order that used tax-inclusive pricing. Pass only the tax-inclusive refund amount, and Waffo converts it to the pre-tax refund amount.

```json theme={null}
{
  "taxInfo": {
    "refundAmountInclTax": "55.00"
  }
}
```

The amounts above follow the assumptions from earlier. A full refund should be based on the final amount of the original order and the currently refundable balance; do not copy the original request value from before the discount. The tax amount for a partial refund is calculated by Waffo, which also validates the cumulative amount; do not round by order ratio on your own and treat that as the authoritative refund tax result.

In refund inquiry [`POST /api/v1/refund/inquiry`](/docs/api-reference/refund-inquiry/refund-inquiry) and `REFUND_NOTIFICATION`, read mainly:

| Field | Integration note |
| - | - |
| `refundAmount` | The normalized pre-tax refund amount, in the original order currency; a tax-inclusive input may be converted to a different value |
| `orderTaxAmount` | The tax amount refunded by this refund, in the order currency, not the original tax amount of the whole payment order; it may be empty, and a zero-tax result may also be explicitly returned as `"0"` |
| `taxInfo.refundAmountInclTax` | Returned when there is a refund tax result and the original order used both prices or tax-inclusive pricing, equal to this `refundAmount + orderTaxAmount`; do not assume this group is present in every intermediate state |
| `finalDealAmount`, `userCurrency` | The refund amount the customer actually receives and its currency; it may include refunded tax and refundable fees, so you cannot reconcile the arrival only with `refundAmount` |
| `remainingRefundAmount` | The remaining refundable pre-tax order amount in the original order currency, not a tax-inclusive balance and not a balance in the customer payment currency |

While a refund is in progress or has failed, the returned calculated amount does not mean the customer has received the refund; reconcile the arrival against the final successful status.

When retrying the same `paymentRequestId` or `refundRequestId`, keep the original amount, tax-inclusive amount, and submission approach unchanged. Inquire about an unknown result first, then decide the next step; see [Idempotency](/docs/en/developer-docs/core-concepts/idempotency).

### Common errors

| Error code | Meaning and handling |
| - | - |
| `A0003` | Check the amount format, field combinations, country, and other parameters; for pre-tax pricing omit `taxInfo` (an empty object `{}` is also not allowed), and for tax-inclusive pricing do not pass `taxCountry` |
| `A0051` | The declared tax amount was rejected by Waffo, for example handled by the reject policy after a strict comparison; check the two prices, the country, the tax calculation details, and the agreement policy, and do not retry the same wrong amount by only changing the idempotency key |
| `A0052` | There is no payment method that can support this declared tax amount, or `taxInfo` was passed without a tax agreement; contact Waffo to verify the configuration and support scope |
| `A0053` | Missing billing details required to validate the declared tax amount, for example a postcode; fill in the required parameters per the [Create order API](/docs/api-reference/order-create/create-new-order) and submit again |

### Integration checklist

* Confirm the tax agreement, product information, and billing detail requirements with Waffo in the target environment.
* Build requests using one pricing approach, and verify the results for no tax, taxable, missing postcode, and each enabled policy when the declaration does not match.
* Save the original request values, the order currency amounts, and the customer payment currency amounts separately.
* Verify how the fields of tax-inclusive pricing change between the creation intermediate state and after payment completes.
* Verify full and partial refunds by original payment order type, and check the inquiry and Webhook.


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