In synchronous integration, VTEX’s Checkout API triggers and sends a request to the external tax service API whenever there are changes to a shopper’s cart, such as adding or removing items.
To learn more about how to implement a client to connect your tax calculation provider to VTEX’s APIs, check the Tax Service recipe and example.
The timeout for the request is five seconds. There is no retry in case of timeout. If the external service that responds to the request times out constantly, the store will not be able to finish the order. If this integration is active, it applies to all stores in that account.
Tax integration via Checkout API
You must activate the tax integration by configuring the orderForm
, an object that stores contextual information about the order. This data is essential to the checkout process of the order.
Check the flow of orderForm
configuration below:
Get orderForm
To activate the tax integration, first it is necessary to get the current orderForm
settings using the Get orderForm
configuration endpoint.
In the endpoint response, the taxConfiguration
object has the tax information that must be updated, as in the example below:
_10{_10 "taxConfiguration": {_10 "url": "https://{accountName}.myvtex.com/tax-service/order-tax",_10 "authorizationHeader": "99b9935b048dfd86893d0bf9gas628849",_10 "appId": "tradeincart",_10 "isMarketplaceResponsibleForTaxes": false,_10 ..._10}
Update orderForm
Then, Checkout settings must be updated using the Update orderForm configuration endpoint.
In the taxConfiguration
object, it is important to update the following fields.
Property name | Description | Example |
---|---|---|
url | String of external API endpoint of the tax provider that the Checkout will query to receive the calculated taxes. | "https://sandbox-rest.avatax.com/api/v2/transactions/create" |
authorizationHeader | String that the Checkout will use in the Authorization header of calls to the external tax calculation API. This field can be used to define the access credentials for this API. | "99b9935b048dfd86893d0bf9gas628849" |
isMarketplaceResponsibleForTaxes | Boolean that indicates whether the marketplace is responsible for calculating taxes for the products (true ) or if the responsibility lies with the seller (false ). | true |
The
isMarketplaceResponsibleForTaxes
feature is not compatible with stores that have Multilevel Omnichannel Inventory implemented.
Here is an example of the taxConfiguration
object with the expected information:
_10{_10 "taxConfiguration": {_10 "url": "{Tax provider URL}",_10 "authorizationHeader": "{Tax provider authorization header}",_10 "appId": "tradeincart",_10 "isMarketplaceResponsibleForTaxes": true,_10 ..._10 },_10 ..._10}
You must send the entire
orderForm
in the request body in the Update orderForm configuration endpoint.
After successfully submitting the request, the Tax API integration becomes active in synchronous mode.
When a purchase is made in a store, the location from which the order is shipped matters for tax calculation purposes. Because of this, when items from White Label Sellers are part of an order, tax configuration for the marketplace (
seller 1
) is not taken into account for those items. Each seller must have its own tax service configuration in order for this type of integration function properly.
Tax calculation request
The external tax calculation service must provide an endpoint, as the https://sandbox-rest.avatax.com/api/v2/transactions/create
example, that will receive a POST
request. In this request, Checkout provides a body in a specific format. This means that either the endpoint must be prepared to receive this body format, or the integration must contain a parser to adapt it to the correct format.
Here is an example of that body sent by Checkout API:
_89{_89 "orderFormId": "e5098ad8c4jk490bb2f6f03400ac1413",_89 "salesChannel": "1",_89 "items": [_89 {_89 "id": "0",_89 "sku": "26",_89 "productId": "12",_89 "ean": "12345678909123",_89 "refId": "3432",_89 "categoryId": "3",_89 "unitMultiplier": 1,_89 "measurementUnit": "un",_89 "targetPrice": 8.2,_89 "itemPrice": 8.2,_89 "quantity": 1,_89 "discountPrice": 0,_89 "dockId": "1125a08",_89 "freightPrice": 0,_89 "brandId": "2000002",_89 "taxCode": "PC040210",_89 "sellerId": "1"_89 }_89 ],_89 "totals": [_89 {_89 "id": "Items",_89 "name": "Items Total",_89 "value": 820_89 },_89 {_89 "id": "Discounts",_89 "name": "Discounts Total",_89 "value": 0_89 },_89 {_89 "id": "Shipping",_89 "name": "Shipping Total",_89 "value": 0_89 },_89 {_89 "id": "Tax",_89 "name": "Tax Total",_89 "value": 0_89 }_89 ],_89 "clientEmail": "client@email.com",_89 "shippingDestination": {_89 "country": "BRA",_89 "state": "RJ",_89 "city": "Rio de Janeiro",_89 "neighborhood": "Botafogo",_89 "postalCode": "22250-905",_89 "street": "Praia Botafogo"_89 },_89 "clientData": {_89 "email": "client@email.com",_89 "document": "12345678909",_89 "documentType": "cpf",_89 "clientProfileData": "12345678000100",_89 "stateInscription": "12345678"_89 },_89 "paymentData": {_89 "payments": [_89 {_89 "paymentSystem": "2",_89 "bin": null,_89 "referenceValue": 820,_89 "value": 820,_89 "installments": null_89 }_89 ]_89 },_89 "taxApp": {_89 "fields": {_89 "isTradeIn": "Yes",_89 "productuid": "15216842581",_89 "quoteuid": "29882961591",_89 "condition": "working",_89 "tradeInPrice": "220.00",_89 "title": "ROG Phone II 512GB",_89 "productIdApplied": "404",_89 "uid": "29882961591",_89 "taxBase": "1399.99"_89 },_89 "id": "tradeincart",_89 "major": 1_89 }_89}
This body has the main fields:
Field | Type | Description |
---|---|---|
orderFormId | string | orderform ID. |
salesChannel | string | Trade policy ID. |
items | array | List of objects which are the order products, where dockId is a field that refers to its identification on the Logistics system that contains information of its address. |
totals | array | Total amount of the orderForm , divided into taxes, shipping, discounts, and the items prices. |
clientEmail | string | Client's email. address. |
shippingDestination | object | Shipping information. Mandatory. field. |
clientData | object | Information regarding the client that placed the order. |
paymentData | object | Contains an array of payments, where there is information regarding the order payment. |
taxApp | object | Contains an object with custom fields specific to the tax application. |
Tax provider response to the request
In response to the request sent by Checkout, it is expected that the external tax provider API returns an array of products, each with its own array of taxes. See the example below:
_17[_17 {_17 "id": "0",_17 "taxes": [_17 {_17 "name": "TAX 1",_17 "description": "This tax represents a standard sales tax applied to the item.",_17 "value": 3.48_17 },_17 {_17 "name": "TAX 2",_17 "description": "This tax is a special surcharge imposed by the government for environmental conservation purposes.",_17 "value": 22_17 }_17 ]_17 }_17]
The following JSON represents a list of taxes associated with a particular item, with each tax defined by its name, description, and value:
Field | Type | Description |
---|---|---|
id | string | Request item index, which means the SKU's position on the items array sent by the request body. |
taxes | array | List of all the taxes types for an SKU. |
name | string | Tax name that will appear on the checkout. |
description | string | Informative field, which does not appear on the storefront. |
value | number | Absolute numeric value that will be added to the original price. |
In the example above, the only item in the items array has a cost of 10
, and, including the calculated taxes returned by the tax calculation tool, the total value would be 10 + 3.48 + 22 = 35.48
.
If no taxes apply to the items in the order, the expected response is an empty array (
[]
).
For the Checkout API to interpret the request body, the Content-type
must be set to application/vnd.vtex.checkout.minicart.v1+json
.
Jurisdiction fields
If you use Avalara as your tax calculation provider, response bodies can also include the following fields, which refer to the different jurisdictions that may apply according to location.
Field | Type | Description |
---|---|---|
jurisType | string | Type of jurisdiction that applies to calculation. |
jurisCode | string | Unique code that identifies the appropriate jurisdiction. |
jurisName | string | Name of the jurisdiction that applies to the calculation. |
These fields are also read by Checkout and added to the priceTag
.
Below is an example for values that may be contained in these fields, and you can download all of the jurisdictions and respective codes used by Avalara.
_41{_41 "Id": "0", _41 "taxes": [ _41 { _41 "name": "NY STATE TAX: NEW YORK", _41 "description": "Srixon Q-Star Tour Golf Balls 5013392- Dozen Yellow", _41 "rate": 0.04, _41 "value": 1.4, _41 "jurisCode": "36", _41 "jurisType": "State", _41 "jurisName": "NEW YORK" _41 }, _41 { _41 "name": "NY COUNTY TAX: ERIE", _41 "description": "Srixon Q-Star Tour Golf Balls 5013392- Dozen Yellow", _41 "rate": 0.0475, _41 "value": 1.66, _41 "jurisCode": "029", _41 "jurisType": "County", _41 "jurisName": "ERIE" _41 }, _41 { _41 "name": "NY STATE TAX: NEW YORK (SHIPPING)", _41 "description": "freight", _41 "rate": 0.04, _41 "value": 0.17, _41 "jurisCode": "36", _41 "jurisType": "State", _41 "jurisName": "NEW YORK" _41 }, _41 { _41 "name": "NY COUNTY TAX: ERIE (SHIPPING)", _41 "description": "freight", _41 "rate": 0.0475, _41 "value": 0.2, _41 "jurisCode": "029", _41 "jurisType": "County", _41 "jurisName": "ERIE" _41 } _41 ] _41}
By following these guidelines and leveraging the provided examples, you can incorporate tax integration into your VTEX platform.