Integrate with an external tax processor

Commerce includes built-in integrations with Avalara AvaTax and Vertex O Series to calculate sales tax in the shopping cart.

This section applies to both OSF and Storefront Classic. This section applies to Open Storefront Framework (OSF) and Storefront Classic.

If you have an account with a different tax processor, you can configure an integration that tells Commerce to use that tax processor to perform tax calculations.

Understand the external tax processor integration

Commerce does not configure taxes, but integrates with tax processors that calculate sales tax in the shopping cart. To integrate with an external tax processor:

  • Make sure you have a valid account with the tax processing service you want to use.
  • Configure the production-externalTaxCalculation webhook (for the production storefront) and the publishing-externalTaxCalculation webhook (for the preview environment) with the URL where Commerce will send order information to the tax processor’s web service and the username and password you use to log into the tax processor.
  • Use the Commerce administration interface or the REST Admin API to configure the Commerce settings that identify and enable your tax processor and specify your warehouse ship-from address.

Commerce automatically uses the enabled tax processor to calculate taxes for every order placed on your store. The tax processor calculates sales tax as part of the pricing operation. When the shopping cart is priced with a request to include tax pricing (when the shopper begins the checkout process and when the order is submitted), Commerce sends the order information to the external tax processor in the body of a webhook request. The tax processor calculates the total tax amount and sends it in a response. The response breaks down the tax into individual components, for example, the total tax amount might include sales tax assessed by both the state and county.

Note: Commerce is not involved in the settlement process.

Commerce uses a fallback method for calculating taxes when it cannot connect to your tax processor’s web service in the event of an outage. See Monitor tax processors for details about fallback tax calculation and information about configuring its settings.

If you want to display prices with tax included, for example prices that include VAT, create a price group for those tax-inclusive prices. See Configure Tax Processors for more information. If your store uses price groups with tax-inclusive prices, you may need to update certain settings on your tax processor’s site.

Important: As a merchant, it is your responsibility to add the appropriate tax jurisdictions when you configure your profile or account on your tax processing service. Jurisdictions tell the tax processor where and when to calculate and report tax. All sales that occur in jurisdictions you have not configured in your tax processor result in tax calculation that returns a value of zero tax. Commerce is not responsible for configuring or validating jurisdictions. Look in your tax processor’s documentation for information about nexus and tax jurisdictions.

Understand the Tax Code property

Each product and shipping method you create in Commerce has a Tax Code property. Tax calculators use the value of the Tax Code property to determine the tax category for a product or shipping method. For example, if you process taxes with vertex O Series, the value of the Tax Code property is the code you assigned to a taxability category in Vertex O Series. Consult the documentation for your tax processor to learn how it lets you categorize products and services.

Tax Code is not a required property; that is, Commerce allows you to create and publish a product or shipping method without providing a tax code. However, you should assign a tax code to every product and shipping method you create. Otherwise, the tax calculations may return unexpected results, including a higher-than-anticipated tax amount.

Configure the External Tax Calculation webhook

When the shopping cart is priced with a request to include tax pricing, the External Tax Calculator webhook sends a POST request to the URL you specified when you configured the webhook. (Typically this is the URL where your tax processor’s web service listens for requests.) The body of the request contains the complete order data in JSON format.

The following example shows the body of an External Tax Calculation webhook POST request from Oracle Commerce. The request body is a JSON representation of the order.

{
 "shippingGroups" : [ {
   "priceInfo" : {
     "amount" : 59.96,
     "total" : 84.96,
     "shipping" : 25,
     "shippingSurchargeValue" : 0,
     "tax" : 0,
     "subTotal" : 59.96,
     "currencyCode" : "USD",
     "totalWithoutTax" : 84.96
   },
   "discountInfo" : {
     "orderDiscount" : 0,
     "shippingDiscount" : 0,
     "discountDescList" : [ ]
   },
   "shippingMethod" : {
     "shippingTax" : 0,
     "cost" : 25,
     "taxCode" : "",
     "value" : "priorityShippingMethod",
     "shippingMethodDescription" : "Priority"
   },
   "shippingGroupId" : "sg140414",
   "shippingAddress" : {
     "lastName" : "Smith",
     "country" : "US",
     "address3" : "",
     "address2" : "",
     "city" : "Syracuse",
     "prefix" : "",
     "address1" : "101 TNT Drive",
     "postalCode" : "13202",
     "companyName" : "",
     "jobTitle" : "",
     "county" : "",
     "suffix" : "",
     "firstName" : "Jean",
     "phoneNumber" : "555-555-1212",
     "faxNumber" : "",
     "alias" : "Home",
     "middleName" : "",
     "state" : "NY",
     "email" : null
   },
   "items" : [ {
     "unitPrice" : 14.99,
     "quantity" : 4,
     "productId" : "Product_1Ci",
     "commerceId" : "ci1443367",
     "rawTotalPrice" : 59.96,
     "returnedQuantity" : 0,
     "salePrice" : 0,
     "detailedItemPriceInfo" : [ {
       "discounted" : false,
       "amount" : 59.96,
       "quantity" : 4,
       "tax" : 0,
       "orderDiscountShare" : 0,
       "detailedUnitPrice" : 14.99,
       "currencyCode" : "USD"
     } ],
     "shippingSurchargeValue" : 0,
     "discountAmount" : 0,
     "catRefId" : "Sku_1Di",
     "discountInfo" : [ ],
     "price" : 59.96,
     "onSale" : false,
     "stateDetailsAsUser" : "The item has been initialized within the shipping group",
     "listPrice" : 14.99,
     "status" : "INITIAL"
   } ]
 } ],
 "creationTime" : 1486073446086,
 "isTaxIncluded" : true,
 "orderId" : "o130414",
 "profile" : {
   "firstName" : "Jean",
   "lastName" : "Smith",
   "taxExempt" : false,
   "receiveEmail" : "yes",
   "id" : "se-570031",
   "locale" : "en",
   "email" : "home@example.com",
   "daytimeTelephoneNumber" : ""
 },
 "orderStatus" : "Incomplete",
 "creationDate" : "2017-02-02T22:10:46.086Z",
 "orderProfileId" : "se-570031",
 "callType" : "SalesOrder",
 "priceInfo" : {
   "amount" : 59.96,
   "total" : 59.96,
   "shipping" : 25,
   "shippingSurchargeValue" : 0,
   "tax" : 0,
   "subTotal" : 59.96,
   "currencyCode" : "USD",
   "totalWithoutTax" : 59.96
 },
 "discountInfo" : {
   "unclaimedCouponMultiPromotions" : { },
   "orderCouponsMap" : { },
   "orderDiscount" : 0,
   "shippingDiscount" : 25,
   "orderImplicitDiscountList" : [ ],
   "unclaimedCouponsMap" : { },
   "claimedCouponMultiPromotions" : { }
 },
 "shipFromAddress" : {
   "country" : "US",
   "lastName" : null,
   "address3" : null,
   "address2" : null,
   "city" : "Cambridge",
   "address1" : "1 main st",
   "prefix" : null,
   "postalCode" : "02142",
   "county" : null,
   "ownerId" : null,
   "suffix" : null,
   "firstName" : null,
   "middleName" : null,
   "state" : "MA"
 },
 "shoppingCart" : {
   "numberOfItems" : 4,
   "items" : [ {
     "unitPrice" : 14.99,
     "quantity" : 4,
     "productId" : "Product_1Ci",
     "rawTotalPrice" : 59.96,
     "salePrice" : 0,
     "detailedItemPriceInfo" : [ {
       "discounted" : false,
       "amount" : 59.96,
       "quantity" : 4,
       "tax" : 0,
       "orderDiscountShare" : 0,
       "detailedUnitPrice" : 14.99,
       "currencyCode" : "USD"
     } ],
     "displayName" : "Liberty Heights",
     "shippingSurchargeValue" : 0,
     "giftWithPurchaseCommerceItemMarkers" : [ ],
     "discountAmount" : 0,
     "isItemValid" : true,
     "taxCode" : null,
     "catRefId" : "Sku_1Di",
     "skuProperties" : [ {
       "propertyType" : "sku-base",
       "name" : "Name",
       "id" : "displayName",
       "value" : null
     }, {
       "propertyType" : "sku-base",
       "name" : "Active",
       "id" : "active",
       "value" : true
     }, {
       "propertyType" : "sku-base",
       "name" : "Id",
       "id" : "id",
       "value" : "Sku_1Di"
     } ],
     "discountInfo" : [ ],
     "price" : 59.96,
     "variant" : [ ],
     "onSale" : false,
     "id" : "ci13000413",
     "listPrice" : 14.99
   } ]
 },
 "giftWithPurchaseInfo" : [ ],
 "shippingAddress" : {
   "lastName" : "Smith",
   "country" : "US",
   "address3" : "",
   "address2" : "",
   "city" : "Syracuse",
   "prefix" : "",
   "address1" : "101 TNT Drive",
   "postalCode" : "13202",
   "companyName" : "",
   "jobTitle" : "",
   "county" : "",
   "suffix" : "",
   "firstName" : "Jean",
   "phoneNumber" : "",
   "faxNumber" : "",
   "alias" : "Home",
   "middleName" : "",
   "state" : "NY",
   "email" : null
 }
}

When you configure the webhook, you need to supply a URL for your tax processor’s web service and the username and password you use to log into your tax processor account. You must also configure the external tax calculator service to read the data, calculate the tax, and send a response that includes the tax. You can configure webhooks on the Commerce administration interface or with the REST Admin API. See Use Webhooks for more information.

The following example shows a sample JSON response sent from a tax processor. Notice that the response breaks the total tax down into several individual components (state, city, and commuter) for each item in the order, including shipping.

"response": {
   {
   "shippingGroups": [
    {
      "taxPriceInfo": {
        "cityTax": 0,
        "amount": 22.99,
        "valueAddedTax": 0,
        "countyTax": 0,
        "isTaxIncluded": false,
        "miscTax": 0,
        "districtTax": 0,
        "stateTax": 0,
        "countryTax": 0
      },
      "priceInfo": {
        "amount": 89.94,
        "total": 114.94,
        "shipping": 25,
        "taxable": 89.94,
        "shippingSurchargeValue": 0,
        "tax": 22.988,
        "subTotal": 89.94,
        "currencyCode": "USD",
        "totalWithoutTax": 91.95
      },
      "shippingMethod": {
        "shippingTax": 0,
        "cost": 25,
        "taxable": 25,
        "taxDetails": [
          {
            "jurisType": "state",
            "rate": "0.2000",
            "tax": 2.5,
            "taxName": "state tax"
          },
          {
            "jurisType": "city",
            "tax": 2.5,
            "taxName": "city tax"
          },
          {
            "jurisType": "commuter",
            "tax": 1,
            "taxName": "commuter tax"
          }
        ],
        "rate": 0,
        "tax": 5,
        "taxCode": "",
        "value": "twoDayShippingMethod",
        "shippingMethodDescription": "Two Day"
      },
      "shippingGroupId": "sg70413",
      "items": [
        {
          "unitPrice": 14.99,
          "quantity": 6,
          "productId": "Product_1Ci",
          "commerceId" : "ci1443367",
          "rawTotalPrice": 89.94,
          "taxable": 89.94,
          "taxDetails": [
            {
              "jurisType": "state",
              "rate": "0.2000",
              "tax": 8.994,
              "taxName": "state tax"
            },
            {
              "jurisType": "city",
              "tax": 8.994,
              "taxName": "city tax
            },
            {
              "jurisType": "commuter",
              "tax": 1.9879999999999995,
              "taxName": "commuter tax"
            }
          ],
          "returnedQuantity": 0,
          "salePrice": 0,
          "shippingSurchargeValue": 0,
          "discountAmount": 0,
          "tax": 17.988,
          "catRefId": "Sku_1Di",
          "discountInfo": [],
          "rate": 0,
          "price": 89.94,
          "onSale": false,
          "stateDetailsAsUser": "The item has been initialized within the shipping group",
          "listPrice": 14.99,
          "status": "INITIAL"
        }
      ]
    }
  ],
  "creationTime": 1475951869000,
  "isTaxIncluded": false,
  "orderId": "o60413",
  "orderStatus": "Incomplete",
  "creationDate": "2016-10-08T18:37:49.000Z",
  "orderProfileId": "se-570031",
  "taxDate": "02-02-2017",
  "callType": "SalesOrder",
  "status": "success",
  "timestamp": "2017-02-02T00:57:25.017"
}
}

If the tax processor cannot calculate the tax based on the information in the POST request, it returns an error response. The following example shows a sample JSON error response sent from a tax processor for an order that contains an invalid shipping address.

{
"response": {
     "status": "error",
      "errors": [
                {
                 "errorCode": 100123,
                 "description": "No taxing jurisdiction found.
                     Please check the address"}
                ]
      }
}

Configure the tax processing settings

To configure the external tax processor integration, you must supply your unique merchant ID. You can enable the external tax processor integration and configure its settings either on the Tax Processing settings page in the Commerce administration interface or with the Commerce Admin API.

Configure tax processing settings with the administration interface

This section describes how to configure integration settings for external tax processors in the Commerce administration interface.

To configure the settings for an external tax processor:

  1. Click the Settings icon.
  2. Select Tax Processing from the Settings list to display the Tax Processing page.
  3. Select External from the Tax Processor list and select the Enable Tax Processor checkbox.
  4. (Optional) Select Show Tax Summary to display an order’s tax amount in the cart, checkout, and order summary pages, or deselect it to hide the tax summary. This checkbox is selected by default.

    If your store’s prices include tax, such as VAT, you likely do not want to display the tax summary. However, if prices do not include tax, you should always display a tax summary.

    This setting has no effect on the display of the tax summary in the emails your store sends to customers. To learn how to remove the tax summary line from the order summaries in emails, see Customize tax display in templates.

  5. Enter your Merchant ID.

    This page does not ask for a URL or account login information, as you specify those when you configure the webhook. See Configure the External Tax Calculation webhook.

  6. Enter your Ship from Warehouse Location settings.

    If you run multiple sites within a single instance of Commerce, each site can have its own ship-from address. See Configure Ship from Warehouse Locations for more information.

  7. Click Save.

The following table describes the Merchant ID and Ship-From Warehouse Location properties.

Configure tax processing settings with the Admin API

This section describes how to configure tax processing settings with the Admin API. See Use the REST APIs for information you need to know before you start working with the Admin API, including how to get an access token to make API requests.

If you run multiple sites within a single instance of Commerce, each site can have its own ship-from address. The ship-from address is a property of the site object, even if your Commerce instance runs only a single site. In previous releases, the ship-from address was a property of the taxProcessor object.

To configure the settings for an external tax processor, issue a PUT request to /ccadmin/v1/taxProcessors/ext-p.

The following table describes the properties for the request.

Property Description
company String that specifies your Vertex O Series taxpayer code.
enabled Boolean that specifies whether the tax processor is enabled for tax calculations on your store.
showTaxSummary

Boolean that specifies whether a tax summary will be shown on your store’s cart, checkout, and order summary. By default, the value of showTaxSummary is false.

If your store’s prices include tax, such as VAT, you likely do not want to display the tax summary. However, if prices to not include tax, you should always display a tax summary.

This setting has no effect on the display of the tax summary in the emails your store sends to customers. To learn how to remove the tax summary line from the order summaries in emails, see Customize tax display in templates.

type A string that specifies the tax processor to use. The value must be external to specify any tax processor that is not Avalara AvaTax or Vertex O Series.

The following example shows a PUT request that enables an external tax processor. Note that the request does not include a URL or account login information, as you specify those when you configure the webhook. See Configure the External Tax Calculation webhook for more information. This sample request also sets fallback tax calculation settings that Commerce uses if it cannot reach your tax processor, for example, in the event of an outage. See Monitor tax processors for more information.

PUT /ccadmin/v1/taxProcessors/ext-p HTTP/1.1
Content-Type: application/json
Authorization: Bearer <access_token>

{
   "enabled": true,
   "showTaxSummary": true,
   "type": "external",
   "fallbackEnabled": true,
   "defaultTaxRate": 5,
   "fallbackRequestVolumeThreshold": 2,
   "fallbackTimeThreshold": 60000,
   "fallbackSleepWindow": 5000
}

The following is the sample response body for this request. Commerce returns properties for a warehouse ship-from address, even though you do not set them on the taxProcessors object. See Configure a ship-from address for more information.

 {
    "country": null,
    "isTaxIncluded": false,
    "fallbackRequestVolumeThreshold": 2,
    "city": null,
    "defaultTaxRate": 5,
    "postalCode": null,
    "hasPassword": false,
    "type": "external",
    "isActive": false,
    "url": null,
    "enabled": false,
    "fallbackTimeThreshold": 60000,
    "fallbackEnabled": true,
    "merchantId": 12345,
    "fallbackSleepWindow": 5000,
    "addressLine1": null,
    "region": null,
    "showTaxSummary": true,
    "username": null
}

Configure returns and exchanges tax calculation dates

You can use the useOrderSubmittedDateForTax flag in the merchant settings Admin API, to use the order's submitted date for tax calculations. This allows you to calculate tax refunds for returns using the original order date, rather than the current date. This sends the date of the original order to the external tax processors.

To modify this setting, issue a PUT command to the /ccadmin/v1/merchant/useOrderSubmittedDateForTax flag to true. For example:
{"userOrderSubmittedDateForTax":true}

To continue using the current date for tax calculations, set the useOrderSubmittedDateForTax flag to false.

If you are using Oracle Commerce 20.1 or earlier, the default for this flag is set to false. Later versions of Oracle Commerce have this flag default to true.

Configure a ship-from address

If you run multiple sites within a single instance of Commerce, each site can have its own address from which items are shipped. For example, suppose you run two sites, one that sells items in the United States, and another that sells items in France. For the US store, items would ship from your warehouse in Chicago; for the French store, items would ship from your warehouse in Paris. The ship-from address is specified by the shipFromAddress property of the site object, even if your Commerce instance runs only a single site. See Configure Sites for more information about working with site objects.

In previous releases, the ship-from properties were stored on the taxProcessor object. If you integrated with Avalara AvaTax, Vertex O Series, or an external tax processor in a previous release and configured a ship-from address, Commerce automatically adds the address to the default site in this release. If you do not specifically configure a ship-from address for a site, the site automatically inherits the address used for the default site.

To configure the ship-from address for a site, issue a PUT request to /ccadmin/v1/sites/{id}.

The following table describes the properties for the request.

Property Description
addressLine1 String that specifies the first line of the address where your products ship from.
addressLine2 String that specifies the second line of the address where your products ship from.
addressLine3 String that specifies the third line of the address where your products ship from.
city String that specifies the name of the city where your products ship from.
country String that specifies the country where your products ship from.
postalCode String that specifies the postal/ZIP code for the address where you products ship from. For addresses in the United States, use the nine-digit ZIP+4 code for best results.
region String that specifies the name of the region, province, or state where your products ship from.

The following example shows a PUT request that configures the ship-from address for a site.

PUT /ccadmin/v1/sites/100002  HTTP/1.1
Authorization: Bearer <access_token>
x-ccasset-language: en

{
"properties": {
"shipFromAddress": {
"city": "cambridge",
"addressLine1": "2 Main Street",
"country": "US",
"region": "WA",
"postalCode": "12345"
}
}
}

Note that this call modifies the productionURL property only for the specified site, but also modifies defaultLocaleId, which is a global property, on all sites.

Configure tax exempt status

Some shoppers may have tax exempt status for certain purchases. For example, individuals working for a charitable organization may be exempt from sales tax on items used in running the charity.

Avalara AvaTax and Vertex O Series can automatically take into account a shopper’s tax exempt status when they calculate sales tax. (Other tax processor services should be able to do this as well.) Configuring Commerce and your tax processor to handle tax exemptions involves the following steps:

  1. The shopper obtains a tax exemption certificate from a taxing authority. (In the United States, the taxing authority is typically the state government of the state in which the shopper resides.) The certificate specifies what exemptions the shopper qualifies for.
  2. The shopper provides you (the merchant) with a copy of the certificate.
  3. Using the certificate management system supplied by your tax processor service, you upload the certificate to the service.
  4. The tax processor returns a tax exemption code that is used to associate the shopper with the appropriate exemptions. (Some tax processors may use a different name for this value. Avalara AvaTax and Vertex O Series both refer to this as the customer code in their certificate management systems.) Or you can explicitly set the code to a specific value in the certificate management system.
  5. You use the Commerce Admin API to set the taxExemptionCode property of the shopper’s profile to the code returned by the tax processor.

Once you have performed these steps, no further intervention is required unless the shopper’s tax exemption status changes. Each time the shopper places an order on your site, Commerce includes the shopper’s tax exemption code with the order data it sends to the tax processor. The tax processor uses the code to look up the shopper’s certificate and then applies the specified exemptions when it calculates sales tax on the order.

Set the shopper’s tax exemption code

The only configuration step actually performed in Commerce setting the taxExemptionCode property of the shopper’s profile. To do this, you use the updateProfile endpoint in the Admin API. For example:

PUT /ccadmin/v1/profiles/110000  HTTP/1.1
Authorization: Bearer <access_token>

{
    "taxExemptionCode": "187652"
}

Note that if the taxExemptionCode property is null or an empty string, Commerce instead includes the value of the profileId property as the tax exemption code when it sends order data to the tax processor. This means that if you explicitly set the exemption code in the certificate management system to the value of the shopper’s profileId, you do not need to set the taxExemptionCode property on the profile.

Set the tax exemption code for account-based commerce

For account-based commerce, the tax exemption certificate applies to the account as a whole, and Commerce sends the exemption code to the tax processor for every purchase made by that account, regardless of the shopper. The account has a taxExemptionCode property that you set to this value using the updateOrganization endpoint in the Admin API. For example:

PUT /ccadmin/v1/organizations/100002  HTTP/1.1
Authorization: Bearer <access_token>

{
    "taxExemptionCode": "339657"
}

Set the tax exemption code for account-based commerce

For account-based commerce, the tax exemption certificate applies to the account as a whole, and Commerce sends the exemption code to the tax processor for every purchase made by that account, regardless of the shopper. The account has a taxExemptionCode property that you set to this value using the updateOrganization endpoint in the Admin API. For example:

PUT /ccadmin/v1/organizations/100002  HTTP/1.1
Authorization: Bearer <access_token>

{
    "taxExemptionCode": "339657"
}