If you use synchronous tax service integration, you might find some limitations:
- 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.
For stores in which there are often up to hundreds of items in a cart, like some B2B operations, this flow can become inefficient and expensive. In this case, consider using asynchronous integration.
Asynchronous integration
Asynchronous integration is an alternative that offers a better user experience, as it ensures that the flow will always have the taxes at the right time, although it depends directly on the resilience of third parties.
By definition, this type of integration allows other processes to move forward while it waits for a response from the tax engine. Thus, the system will not respond with a timeout even if it takes more than 5 seconds to respond.
Instead of calling the external tax service API for each item added to the cart, asynchronous integration will send just one request resulting in less data being processed, and thus checkout becomes faster and more efficient. That request can be triggered, for instance, by an event in the checkout UI.
Implementing asynchronous integration
To use asynchronous integration, Checkout can not be configured to handle the External Tax Services. This configuration should only be done for synchronous integration. To use asynchronous integration, make sure that the response from the Checkout Configuration API has the taxConfiguration
object with the value null
. If not, use the Checkout Configuration UPDATE request to make it null
.
The asynchronous call to the tax engine does not require any configuration. It must be implemented by the store, as described in this recipe. It is usually triggered by an event or button in the checkout UI.
To query the tax engine, the implementation must follow this flow:
- Get the orderForm.
- Calculate taxes (or get calculation from the external provider).
- Submit response with taxes.
Getting the orderForm
To obtain the orderForm, the implementation must use the following request:
- Method:
GET
- URL:
https://{accountName}.{environment}.com.br/api/checkout/pub/orderForm/{orderFormId}?disableAutoCompletion=true
The
disableAutoCompletion=true
parameter is necessary to ensure that the requested orderForm won’t be recalculated but delivered exactly as it was at the time of the request.
Although the route is public (
/pub
), it is necessary to use credentials (appKey
andappToken
) with permission to access the cart data to obtain the unmasked data.
Calculating taxes and sending the response
Having obtained the orderForm
, the implementation must calculate the appropriate taxes, or get the appropriate calculations from the external provider, and send the following information in the response:
itemTaxResponse
: an array of items (SKUs), each containing an array of applicable taxes.miniCartRequest
: an object containing the items, delivery address, buyer data, orderForm ID, and trade policy of the cart receiving the taxes.
Taxes should be sent to the following endpoint:
- Method:
POST
- URL:
https://{accountName}.vtexcommercestable.com.br/api/checkout/pvt/orderForms/taxes
Below is an example body.
This request requires the appKey
and appToken
credentials.
_73{_73 "itemTaxResponse": [_73 {_73 "sku": "8",_73 "taxes": [_73 {_73 "name": "Tax1",_73 "value": 1.28_73 },_73 {_73 "name": "Tax2",_73 "value": 7.91_73 }_73 ]_73 },_73 {_73 "sku": "33", // SKU id_73 "taxes": [_73 {_73 "name": "Tax1", // name you want to use for the tax_73 "value": 3.98_73 }_73 ]_73 }_73 ],_73 "miniCartRequest": {_73 "orderFormId": "9c7aad42ee2d4a37a23478a9d5cb6f30",_73 "salesChannel": "1",_73 "items": [_73 {_73 "sku": 8,_73 "ean": null,_73 "refId": "1111A",_73 "unitMultiplier": 1,_73 "measurementUnit": "un",_73 "targetPrice": 80, // price_73 "itemPrice": 240, // total price (price * qty * unitMultiplier)_73 "discountPrice": 0, // item discounts_73 "freightPrice": 0.9, // item’s freight price_73 "quantity": 3,_73 "dockId": "1",_73 "brandId": 2000000_73 },_73 {_73 "sku": 33,_73 "ean": null,_73 "refId": "1111B",_73 "unitMultiplier": 1,_73 "measurementUnit": "un",_73 "targetPrice": 20,_73 "itemPrice": 40,_73 "discountPrice": 0,_73 "freightPrice": 0.6,_73 "quantity": 2,_73 "dockId": "1",_73 "brandId": 2000000_73 }_73 ],_73 "shippingDestination": { // required!_73 "country": "BRA",_73 "state": "RJ",_73 "city": "Rio de Janeiro",_73 "neighborhood": "Botafogo",_73 "postalCode": "22250-040",_73 "street": "Praia de Botafogo"_73 },_73 "clientData": {_73 "email": "email@gmail.com.br",_73 "document": "01234567890",_73 "corporateDocument": null_73 }_73 }_73}
Validating taxes
Note that the tax engine response includes, in addition to the taxes, the minicart that was used for the calculation. So, VTEX is able to guarantee that the calculated tax is consistent with the cart that is completing the purchase. This happens through the following algorithm:
- When the system receives the response from the tax provider, it gets the minicart that was used to calculate the taxes.
- The system generates a token for this minicart and saves it in the cart.
- When the purchase is concluded, the token is sent to the seller, who checks whether it matches the cart that is completing the purchase.
- If it is the same token, the purchase is completed, with the tax applied. If the tokens do not match, the purchase is denied.
- If the purchase is denied, there will be a 500 error in the request
/transaction
: “The order cannot be created. Please try again."
The implementer is responsible for the minicart. Note that it is crucial for validating taxes, so there must be an exact match with the customer's current cart.
If the system takes too long to apply the tax, you can create an order without taxes.
If the system takes too long to review the tax after changes to the cart, it will not be possible to complete the order.
Example of order object
Finally, see an example of an order object at VTEX after taxes are applied. Note that they have been included in the totals
array.
_226{_226 "emailTracked": "0df1ee8d7f58474782c3750e2f42e4ed@ct.vtex.com.br",_226 "approvedBy": null,_226 "cancelledBy": null,_226 "cancelReason": null,_226 "handlingData": null,_226 "orderId": "v3613574JRSA-01",_226 "sequence": "3613574",_226 "marketplaceOrderId": "",_226 "marketplaceServicesEndpoint": "http://oms.vtexinternal.com.br/api/oms?an=mystore",_226 "sellerOrderId": "00-v3613574JRSA-01",_226 "origin": "Marketplace",_226 "affiliateId": "",_226 "salesChannel": "1",_226 "merchantName": null,_226 "status": "ready-for-handling",_226 "statusDescription": "Pronto para o manuseio",_226 "value": 598095,_226 "creationDate": "2020-05-26T17:37:56.2353707+00:00",_226 "lastChange": "2020-05-26T17:39:24.4238867+00:00",_226 "orderGroup": null,_226 "totals": [_226 {_226 "id": "Items",_226 "name": "Total de los items",_226 "value": 656890_226 },_226 {_226 "id": "Discounts",_226 "name": "Total de descuentos",_226 "value": 0_226 },_226 {_226 "id": "Shipping",_226 "name": "Costo total del envÃo",_226 "value": 0_226 },_226 {_226 "id": "Tax",_226 "name": "Costo total del cambio",_226 "value": 0_226 },_226 {_226 "id": "CustomTax",_226 "name": "FI_ SOCIOS PREMIUM ",_226 "value": 297_226 },_226 {_226 "id": "CustomTax",_226 "name": "VI_P 3X2_BEBIDAS ",_226 "value": 400_226 }_226 ],_226 "items": [_226 {_226 "uniqueId": "892FF75C59301ACE8CBE75BA7P0E1BE9",_226 "id": "4321",_226 "productId": "1234",_226 "ean": "9974111597732",_226 "lockId": "00-v3612391AKPI-01",_226 "itemAttachment": {_226 "content": {},_226 "name": null_226 },_226 "attachments": [],_226 "quantity": 1,_226 "seller": "1",_226 "name": "My Product",_226 "refId": "11570114019",_226 "price": 5750,_226 "listPrice": null,_226 "manualPrice": null,_226 "priceTags": [],_226 "imageUrl": "https://mystore.vteximg.com.br/arquivos/ids/521666-55-55/myproduct-12-U-1-2256.jpg?v=536862711303520990",_226 "detailUrl": "/my-product/p",_226 "components": [],_226 "bundleItems": [],_226 "params": [],_226 "offerings": [],_226 "sellerSku": "2511",_226 "priceValidUntil": null,_226 "commission": 0,_226 "tax": 0,_226 "preSaleDate": null,_226 "additionalInfo": {_226 "brandName": "BRAND",_226 "brandId": "1190",_226 "categoriesIds": "/1/29/201/",_226 "categories": [_226 {_226 "id": 201,_226 "name": "Category1"_226 },_226 {_226 "id": 29,_226 "name": "Category2"_226 },_226 {_226 "id": 1,_226 "name": "Category3"_226 }_226 ],_226 "productClusterId": "1171,1401,2643,2832,4121,4318,4342",_226 "commercialConditionId": "1",_226 "dimension": {_226 "cubicweight": 0.0002,_226 "height": 1,_226 "length": 1,_226 "weight": 1,_226 "width": 1_226 },_226 "offeringInfo": null,_226 "offeringType": null,_226 "offeringTypeId": null_226 },_226 "measurementUnit": "un",_226 "unitMultiplier": 1,_226 "sellingPrice": 5750,_226 "isGift": false,_226 "shippingPrice": null,_226 "rewardValue": 0,_226 "freightCommission": 0,_226 "priceDefinitions": null,_226 "taxCode": null,_226 "parentItemIndex": null,_226 "parentAssemblyBinding": null,_226 "callCenterOperator": null,_226 "serialNumbers": null,_226 "assemblies": [],_226 "costPrice": null_226 }_226 ],_226 "marketplaceItems": [],_226 "clientProfileData": {_226 "id": "clientProfileData",_226 "email": "email@gmail.com",_226 "firstName": "John",_226 "lastName": "Doe",_226 "documentType": "DNI",_226 "document": "12345678",_226 "phone": "+55012345678",_226 "corporateName": null,_226 "tradeName": null,_226 "corporateDocument": null,_226 "stateInscription": null,_226 "corporatePhone": null,_226 "isCorporate": false,_226 "userProfileId": "1c87654df-f25c-49jf-9e9f-4839c00009a6",_226 "customerClass": null_226 },_226 "giftRegistryData": null,_226 "marketingData": null,_226 "ratesAndBenefitsData": {_226 "id": "ratesAndBenefitsData",_226 "rateAndBenefitsIdentifiers": [_226 {_226 "description": "christmas2020",_226 "featured": true,_226 "id": "011p7fbc-2129-424e-b45e-4b53fa8d4805",_226 "name": "christmas2020",_226 "matchedParameters": {_226 "productCluster@CatalogSystem": "4848|inclusive"_226 },_226 "additionalInfo": null_226 }_226 ]_226 },_226 "shippingData": {_226 ..._226 },_226 "paymentData": {_226 ..._226 },_226 "packageAttachment": {_226 "packages": []_226 },_226 "sellers": [_226 {_226 "id": "1",_226 "name": "My Store",_226 "logo": "",_226 "fulfillmentEndpoint": "http://fulfillment.vtexcommerce.com.br/api/fulfillment?sc=1&an=mystore"_226 }_226 ],_226 "callCenterOperatorData": null,_226 "followUpEmail": "email@vtex.com",_226 "lastMessage": null,_226 "hostname": "storehostname",_226 "invoiceData": null,_226 "changesAttachment": null,_226 "openTextField": null,_226 "roundingError": 0,_226 "orderFormId": "48x123e0baff36718d0nc81e56cec29c",_226 "commercialConditionData": null,_226 "isCompleted": true,_226 "customData": null,_226 "storePreferencesData": {_226 "countryCode": "ARG",_226 "currencyCode": "ARS",_226 "currencyFormatInfo": {_226 "CurrencyDecimalDigits": 2,_226 "CurrencyDecimalSeparator": ",",_226 "CurrencyGroupSeparator": ".",_226 "CurrencyGroupSize": 3,_226 "StartsWithCurrencySymbol": true_226 },_226 "currencyLocale": 11274,_226 "currencySymbol": "$",_226 "timeZone": "Argentina Standard Time"_226 },_226 "allowCancellation": false,_226 "allowEdition": false,_226 "isCheckedIn": false,_226 "marketplace": {_226 "baseURL": "http://oms.vtexinternal.com.br/api/oms?an=mystore",_226 "isCertified": null,_226 "name": "mystore"_226 },_226 "authorizedDate": "2020-05-26T17:38:10.2154236+00:00",_226 "invoicedDate": null,_226 "itemMetadata": null,_226 "subscriptionData": null,_226 "taxData": null,_226 "checkedInPickupPointId": null,_226 "cancellationData": null_226}