Tax

Integration Overview

In this guide, you'll learn how to integrate a 3rd party tax provider into your checkout to handle dynamic sales tax calculations.

This guide uses the TaxJar Sales Tax API as an integration example.

Let’s get started

We will be using an existing open source demo store to integrate our tax layer to get started, first create a Chec account and create a copy of the the project ChopChop demo store using the Chec CLI:

// Register for a Chec account
chec register
Sign up for a Chec account through your browser

// Login in to your Chec account
chec login

// Download the ChopChop demo store
chec demo-store commercejs-chopchop-demo chopchop-taxjar
✔ Downloaded commercejs-chopchop-demo to /Users/jaeriah/projects/tmp/chopchop-taxjar

chopchopmarketing

After downloading the demo store using the Chec CLI, we now have a store with products you can add to the cart and checkout to start calculating and collecting sales tax. We will go through the below steps to ensure a seamless integration of TaxJar in our application.

Integration flow

  • Ensure all tax configuration is disabled in the Chec dashboard
  • Sign up for TaxJar account and obtain API key (30 day trial)
  • Authenticate TaxJar by setting up client with the provided TaxJar API key
  • Make a request to TaxJar’s Taxes API and use the response to set taxes in Chec
  • TaxJar’s response is used to update the checkout using the Update checkout API

First, go to your Chec dashboard in Settings > Tax to check that the enable tax setting is disabled as we want to opt out of Chec’s native tax handling. The “Do your prices include tax” configuration will depend on whether your country is tax inclusive or tax exclusive. In our example integration, we will be using United States, which is a tax exclusive country and thus, we set the tax inclusion flag to false.

taxdashboard

Configuring TaxJar

Next, you will need to sign up for TaxJar so that we can use TaxJar’s Sales Tax API. We can use the response from this API to provide tax amounts when using Chec’s Update Checkout API which will update set the tax amounts on the live object.

The TaxJar response provides calculated tax at the total tax level and individual line items level. We will be using the line items in Chec to pass into TaxJar’s request payload using the line_items array property. There are two baseline required parameters — to_country and shipping. Since we need additional data from a customer’s input to power the API, we will test the integration by going through an arbitrary cart and checkout process. We currently have our merchant information set to a US location and our products will only be available to ship within the US.

Update checkout TaxJar form

Connecting to the update checkout API

We can now start hooking TaxJar into the ChopChop store using Commerce.js and the Update Checkout API. You will need to obtain your Chec secret API key to use this endpoint as the Update Checkout API can only be used with your secret key.

Because we will need to make updates to the checkout using the Chec secret API key and TaxJar’s API key, we can develop a Node.js serverless function to handle the update logic on the server-side without exposing the keys. We will walk through the serverless function created in more details below.

The ChopChop demo store is built with Next.js which comes built-in with serverless function support with API routes. We can use this feature as a solution to build out our API endpoint in order to calculate our checkout sales tax.

We’ve created a tax.js at pages/api. Any file inside of the api folder will be treated as an API endpoint instead of a page. The API route takes in a request and a response helper and in the serverless function, we’ve pulled “checkout token”, “country”, “region” and “zip” attributes from the request. The checkout token ID is created when the checkout is first generated and the other location based parameters will come from the customer input. All from_ parameters are required if no Nexus addresses are provided in TaxJar. Conveniently, we can also fetch the required data from our merchant address to include in the TaxJar’s request payload. As noted above, the shipping price is required along with either an amount or line items, both can come from the checkout live object. Once we’ve fetched our line items and shipping price, we can include the information to make the TaxJar request to start calculating our tax.

The next step is where we start to use the recently added Update Checkout API. The flexibility of this secret key API allows us to use TaxJar’s powerful tax management to collect sales tax calculations following compliance. Chec does come built in with native tax but is limited when it comes to handling more complex tax management. We’ve built the new API with this in mind, in that a true headless commerce stack should allow for swapping in and out any best of the breed tool.

We’ve hooked into the checkout using Taxjar’s sales tax API, which takes precedence over all tax calculations at the checkout level. When the request returns with a tax response from calling the sales tax API, it has everything we need to map to the tax object in Chec.

An example response from TaxJar:

{
  tax: {
    amount_to_collect: 4.17,
    breakdown: {
      city_tax_collectable: 2.29,
      city_tax_rate: 0.04875,
      city_taxable_amount: 47,
      combined_tax_rate: 0.08875,
      county_tax_collectable: 0,
      county_tax_rate: 0,
      county_taxable_amount: 0,
      line_items: [
        {
          city_amount: 1.95,
          city_tax_rate: 0.04875,
          city_taxable_amount: 40,
          combined_tax_rate: 0.08875,
          county_amount: 0,
          county_tax_rate: 0,
          county_taxable_amount: 0,
          id: 'item_7RyWOwmK5nEa2V',
          special_district_amount: 0,
          special_district_taxable_amount: 0,
          special_tax_rate: 0,
          state_amount: 1.6,
          state_sales_tax_rate: 0.04,
          state_taxable_amount: 40,
          tax_collectable: 3.55,
          taxable_amount: 40
        }
      ],
      shipping: 7,
      special_district_tax_collectable: 0,
      special_district_taxable_amount: 0,
      special_tax_rate: 0,
      state_tax_collectable: 1.88,
      state_tax_rate: 0.04,
      state_taxable_amount: 47,
      tax_collectable: 4.17,
      taxable_amount: 47
    },
    freight_taxable: true,
    has_nexus: true,
    jurisdictions: {
      city: 'NEW YORK CITY',
      country: 'US',
      county: 'KINGS',
      state: 'NY'
    },
    order_total_amount: 47,
    rate: 0.08875,
    shipping: 7,
    tax_source: 'destination',
    taxable_amount: 47
  }
}

We can then pass this response to the Update Checkout API call by including it in the payload body to set the new tax calculation.

You can refer to the ChopChop repository for the full serverless function but for practicality sake, we will demonstrate the new API with a cURL request:

curl -X POST \
    -G "http://localhost:3000/api/tax?token=chkt_jwOjzjLMbpEpAl&country=US&region=NY&zip=11211" \
    -H "Accept: application/json"

In the request, it’s important to note that we have provided the checkout token and the required shipping location fields. We will use this information to:

  • Fetch the origin address (where the business is located/selling from) from the merchant information in Chec
  • Pull the line items added to the checkout
  • Send the data to TaxJar to calculate accurate sales tax based on the above parameters
  • Update the Commerce.js checkout with the new tax calculations

An example abbreviated response should look like this from calling the Update Checkout API:

{
  "id": "chkt_J5aEpaW8pMExV5",
  "cart_id": "cart_Vw7x9DWp9X0AVo",
  "created": 1623285606,
  "expires": 1623890406,
  "live": {
    "merchant_id": 19303,
    "currency": {
      "code": "USD",
      "symbol": "$"
    },
    "subtotal": {
      "raw": 40,
      "formatted": "40.00",
      "formatted_with_symbol": "$40.00",
      "formatted_with_code": "40.00 USD"
    },
    "tax": {
      "amount": {
        "raw": 3.55,
        "formatted": "3.55",
        "formatted_with_symbol": "$3.55",
        "formatted_with_code": "3.55 USD"
      },
      "breakdown": [
        {
          "amount": 3.55,
          "rate": 0.08875,
          "rate_percentage": "8.875%",
          "type": "Tax"
        }
      ],
      "included_in_price": false,
      "zone": [],
      "provider": "TaxJar"
    },
    "total": {
      "raw": 47,
      "formatted": "47.00",
      "formatted_with_symbol": "$47.00",
      "formatted_with_code": "47.00 USD"
    },
    "total_with_tax": {
      "raw": 47,
      "formatted": "47.00",
      "formatted_with_symbol": "$47.00",
      "formatted_with_code": "47.00 USD"
    },
    "adjustments": {
      "taxable": {
        "raw": 0,
        "formatted": "0.00",
        "formatted_with_symbol": "$0.00",
        "formatted_with_code": "0.00 USD"
      },
      "untaxable": {
        "raw": 0,
        "formatted": "0.00",
        "formatted_with_symbol": "$0.00",
        "formatted_with_code": "0.00 USD"
      },
      "total": {
        "raw": 0,
        "formatted": "0.00",
        "formatted_with_symbol": "$0.00",
        "formatted_with_code": "0.00 USD"
      }
    }
  }
}

With a successful request, the API route at tax.js returns a json response with a status code of 200. The checkout state is then updated with the new tax and we are then able to dynamically output tax calculations on location, shipping or line item changes.

Update checkout TaxJar total

What’s next

For US-based businesses, you can add a list of Nexus states within TaxJar. The only difference is that the list of Nexus addresses are provided to the serverless function instead of to single location fields. Another powerful feature is that TaxJar’s sales tax API categories endpoint allows merchants to collect tax based the good’s categories. This is a US-only tax compliance used for products that are either exempt from sales tax or taxed at a reduced rate. The TaxJar categories parameters can be matched with categories or meta data at the product level in Chec to collect accurate tax based on a subset of products.

What about other tax providers?

You can choose to integrate any tax provider and implement custom business rules with your own serverless functions.