
Tax Calculation
- 74 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Calculate accurate sales tax and VAT at checkout using TaxJar or Avalara with nexus management for multi-state and international compliance.
About
Computes checkout sales tax and VAT via TaxJar or Avalara with nexus management for multi-state and international sales. A developer uses it to charge correct taxes at checkout.
- TaxJar or Avalara integration at checkout
- Nexus management for multi-state and international
Tax Calculation by the numbers
- 74 all-time installs (skills.sh)
- Ranked #3,067 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill tax-calculationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 74 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Calculate accurate sales tax and VAT at checkout using TaxJar or Avalara with nexus management for multi-state and international compliance.
Files
Tax Calculation
Overview
Accurate tax calculation at checkout is a legal requirement, not an optimization. In the United States, there are over 13,000 taxing jurisdictions. EU VAT rules require charging the customer's local VAT rate. Getting it wrong leads to under-collection (a liability you must cover) or over-collection (refunds and customer complaints). All major platforms have built-in tax calculation or direct integrations with TaxJar and Avalara that handle this correctly without custom code.
When to Use This Skill
- When expanding sales to states or countries where you have tax nexus obligations
- When manual tax rates are causing compliance issues or needing constant updates
- When implementing EU VAT compliance (OSS/IOSS registration)
- When displaying accurate tax before the customer confirms payment
Core Instructions
Step 1: Understand nexus before configuring tax
You only need to collect tax in jurisdictions where you have nexus (a tax obligation).
US Sales Tax nexus:
- Physical nexus: you have employees, warehouses, or offices in a state
- Economic nexus: most states trigger at $100,000/year in sales OR 200 transactions to customers in that state (California and Texas use $500,000)
- Check each state's current threshold before configuring — thresholds change
EU VAT:
- EU-based sellers must charge VAT at the customer's country rate for all EU sales
- Non-EU sellers must register for EU VAT (via OSS scheme) once annual EU B2C sales exceed €10,000
- B2B sales within the EU: reverse charge applies — the buyer handles VAT via self-assessment
Step 2: Set up tax calculation on your platform
---
Shopify
Option A: Shopify's built-in tax (recommended for US stores)
1. Go to Settings → Taxes and duties 2. Under Tax regions, select the regions where you have nexus 3. For US: Shopify calculates taxes automatically at the correct state + county + city rate based on the customer's shipping address — no third-party service needed for basic US compliance 4. Enable Charge tax on shipping if your state requires it (varies by state) 5. For product-level exemptions (e.g., clothing exempt in PA): go to Products → [Product] → Tax and set the appropriate tax category or create a custom tax override
Option B: Stripe Tax (via Shopify + Stripe)
Shopify's built-in tax covers US well. For international VAT compliance, install Stripe Tax via a Shopify app integration.
Option C: TaxJar or Avalara (for complex multi-jurisdiction requirements)
1. Install TaxJar or Avalara AvaTax from the Shopify App Store 2. Follow the app's setup wizard: connect to your Shopify store, enter your nexus states, and configure product tax categories 3. The app replaces Shopify's built-in tax calculation with its own real-time calculation at checkout 4. Committed transactions are automatically sent to TaxJar/Avalara for filing reports
WooCommerce
Option A: WooCommerce built-in tax
1. Go to WooCommerce → Settings → Tax and enable tax calculation 2. Set your store base address (this affects which rates apply) 3. Go to Tax → Standard rates and manually enter rates per state/country 4. Limitation: manual rates are not updated automatically; for compliance, use TaxJar or Avalara
Option B: TaxJar (recommended for US compliance)
1. Sign up at taxjar.com and get your API token 2. Install TaxJar for WooCommerce plugin (free, from WordPress.org) 3. Go to WooCommerce → TaxJar and enter your API token 4. Enable Automatic tax calculation — TaxJar calculates the correct rate at checkout in real-time based on your nexus states and the customer's address 5. Enable Transaction sync — completed orders are automatically sent to TaxJar for filing reports
Option C: Avalara AvaTax
1. Sign up at avalara.com and create a company in AvaTax 2. Install the Avalara AvaTax for WooCommerce plugin 3. Enter your Account ID, License Key, and Company Code from the Avalara dashboard 4. Enable calculation and transaction recording
BigCommerce
1. Go to Store Setup → Tax 2. BigCommerce has a built-in tax calculation for basic US rates 3. For full compliance: go to Store Setup → Tax → Tax Provider and connect Avalara AvaTax or TaxJar 4. Follow the provider's BigCommerce setup guide — both have native integrations that replace the built-in tax engine with real-time compliant calculations
EU VAT on BigCommerce: Enable VAT by country under Store Setup → Tax → VAT for EU VAT compliance. For full OSS compliance, use Avalara's EU VAT module.
---
Custom / Headless
Use Stripe Tax (simplest) or the TaxJar/Avalara API directly:
Option A: Stripe Tax (recommended for Stripe-based stores)
// Enable Stripe Tax on the PaymentIntent — Stripe calculates and collects tax automatically
const paymentIntent = await stripe.paymentIntents.create({
amount: orderSubtotalCents, // Subtotal only — Stripe adds tax
currency: 'usd',
automatic_payment_methods: { enabled: true },
// Stripe Tax configuration
// See: https://stripe.com/docs/tax/integration
});
// Or use Stripe Checkout with automatic_tax enabled:
const session = await stripe.checkout.sessions.create({
line_items: lineItems,
mode: 'payment',
automatic_tax: { enabled: true }, // Stripe Tax handles calculation
customer_details: { address: { country: customerCountry }, address_source: 'shipping' },
success_url: `${domain}/success`,
cancel_url: `${domain}/cart`,
});Configure Stripe Tax under Stripe Dashboard → Tax → Configure — set your tax registration numbers and the tax behaviors for each product category.
Option B: TaxJar API
const Taxjar = require('taxjar');
const taxjar = new Taxjar({ apiKey: process.env.TAXJAR_API_KEY });
async function calculateTaxForOrder({ fromAddress, toAddress, lineItems, shippingCost }) {
const response = await taxjar.taxForOrder({
from_country: fromAddress.country,
from_zip: fromAddress.zip,
from_state: fromAddress.state,
to_country: toAddress.country,
to_zip: toAddress.zip,
to_state: toAddress.state,
to_city: toAddress.city,
amount: lineItems.reduce((sum, i) => sum + i.unit_price * i.quantity, 0),
shipping: shippingCost,
line_items: lineItems.map(item => ({
id: item.id,
quantity: item.quantity,
unit_price: item.unit_price,
product_tax_code: item.tax_code ?? null, // e.g., '20010' for general goods
})),
});
return {
totalTax: response.tax.amount_to_collect,
taxRate: response.tax.rate,
hasNexus: response.tax.has_nexus, // false = no tax to collect
breakdown: response.tax.breakdown,
};
}
// After order is confirmed, commit the transaction for filing reports
async function commitTaxTransaction(order) {
await taxjar.createOrder({
transaction_id: order.id,
transaction_date: new Date().toISOString().split('T')[0],
from_country: WAREHOUSE_ADDRESS.country,
from_zip: WAREHOUSE_ADDRESS.zip,
from_state: WAREHOUSE_ADDRESS.state,
to_country: order.shippingAddress.country,
to_zip: order.shippingAddress.zip,
to_state: order.shippingAddress.state,
amount: order.subtotal,
shipping: order.shippingCost,
sales_tax: order.taxAmount,
line_items: order.lineItems.map(item => ({
id: item.id,
quantity: item.quantity,
unit_price: item.price,
sales_tax: item.taxAmount,
})),
});
}EU VAT reverse charge (B2B cross-border within EU):
For EU B2B transactions, validate the buyer's VAT number via the EU VIES service before applying zero-rate:
async function validateEUVATNumber(vatNumber) {
const countryCode = vatNumber.slice(0, 2);
const number = vatNumber.slice(2);
const res = await fetch(
`https://ec.europa.eu/taxation_customs/vies/rest-api/ms/${countryCode}/vat/${number}`
);
const data = await res.json();
return data.isValid === true;
}Step 3: Commit tax transactions after order completion
Tax calculation services require you to "commit" each transaction after payment is confirmed — this records it in your filing reports. TaxJar and Avalara apps for Shopify/WooCommerce do this automatically. For custom integrations, call the create/commit API after the order is confirmed (not before payment).
Best Practices
- Never hard-code tax rates — rates change constantly; use TaxJar, Avalara, Stripe Tax, or your platform's built-in tax engine
- Calculate tax in real-time at checkout — display the exact tax amount before the customer confirms payment; estimated tax that changes at payment causes distrust and cart abandonment
- Commit transactions after payment, not before — only committed transactions appear in filing reports; commit when the payment is confirmed
- Void tax transactions on refunds — when you issue a refund, void the corresponding tax transaction in TaxJar/Avalara to avoid over-reporting on your filing
- Handle tax API errors gracefully — if the tax API is unavailable, apply a fallback rate (US average ~8.5%) rather than blocking checkout
Common Pitfalls
| Problem | Solution |
|---|---|
| Tax calculated but not committed to the filing API | Ensure your platform integration (TaxJar plugin, Avalara plugin) is configured to auto-commit on order completion; verify in the provider's transaction dashboard |
| EU VAT charged on B2B cross-border sales | Validate VAT numbers via VIES before applying reverse charge; if validation fails, charge VAT as B2C |
| Tax API adds 500ms to checkout | TaxJar and Avalara both have caching built into their Shopify/WooCommerce plugins; for custom builds, cache estimates by destination zip code and cart total for 1 hour |
| Shopify charging wrong tax rate for a state | Verify your nexus state list in Settings → Taxes is correct and up to date; check for product-level tax overrides that may be incorrectly configured |
| WooCommerce showing "0 tax" after TaxJar install | Verify TaxJar API key is correct; check the plugin's status page for API errors; confirm your warehouse address and nexus states are configured in the TaxJar dashboard |
Related Skills
- @checkout-flow-optimization
- @multi-currency
- @order-processing-pipeline
- @stripe-integration
- @tax-compliance-automation
{
"context": "Tests whether the agent correctly integrates Avalara AvaTax including proper client initialization, SalesOrder vs SalesInvoice transaction types, correct line item tax codes, the two-phase estimate-then-commit workflow, transaction code storage, and order cancellation voiding.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Correct package",
"max_score": 8,
"description": "Uses the 'avatax' npm package (import/require from 'avatax'), not a generic HTTP client"
},
{
"name": "Client initialization fields",
"max_score": 8,
"description": "Avalara client is initialized with all four fields: appName, appVersion, environment, and machineName"
},
{
"name": "Sandbox vs production env",
"max_score": 8,
"description": "The 'environment' field is set to 'production' when AVALARA_ENV equals 'production', and 'sandbox' otherwise — not hardcoded to either"
},
{
"name": "withSecurity() for credentials",
"max_score": 8,
"description": "Credentials (username and password) are passed via .withSecurity({ username, password }), not via constructor or other methods"
},
{
"name": "SalesOrder for estimates",
"max_score": 8,
"description": "Tax estimate calls use transaction type 'SalesOrder' (commit=false), not 'SalesInvoice'"
},
{
"name": "SalesInvoice for commits",
"max_score": 8,
"description": "Tax commit calls use transaction type 'SalesInvoice' (commit=true), not 'SalesOrder'"
},
{
"name": "Shipping line FR010000",
"max_score": 8,
"description": "A separate shipping line item is included in Avalara transactions with taxCode 'FR010000'"
},
{
"name": "Default tax code P0000000",
"max_score": 8,
"description": "Product lines without a specific tax code fall back to 'P0000000' (not null, empty, or a different code)"
},
{
"name": "Transaction code persisted",
"max_score": 9,
"description": "The Avalara transaction code (result.code) is saved on the order record after calculation or commit"
},
{
"name": "Commit on order confirmed",
"max_score": 9,
"description": "The commit step is triggered after order/payment confirmation (e.g., in an order.confirmed handler, post-payment webhook, or commit step), not during estimate/checkout"
},
{
"name": "Void on cancellation",
"max_score": 9,
"description": "Order cancellation logic calls a void or cancel operation on the Avalara transaction (references commitTransaction or voidTransaction on the committed code)"
},
{
"name": "Consistent estimate/commit params",
"max_score": 9,
"description": "The same address, line item, and shipping parameters are used for both the estimate and the final commit — no parameters differ between the two calls"
}
]
}
Integrate International Tax Calculation for a Global Marketplace
Problem/Feature Description
NexaTrade is expanding from a domestic US-only marketplace to serving customers in Canada, Australia, and the EU. Their existing checkout has no tax engine at all — orders just ship and the finance team manually reconciles taxes monthly, which is becoming unsustainable as international volume grows. The Head of Finance has mandated that all orders must generate tax filing records automatically.
The team has chosen Avalara AvaTax because it covers all the jurisdictions NexaTrade now ships to. There are two distinct moments where tax matters: first when the customer is filling in their shipping address (to show an accurate tax total before they pay), and second when the payment succeeds (to lock in the official filing record). The solution also needs to handle the case where an order is cancelled after payment — the filing record must not count that revenue.
Your task is to build the Avalara tax integration module for NexaTrade's Node.js checkout service.
Output Specification
Produce a JavaScript module at lib/avalaraTax.js that exports:
1. A function to calculate a tax estimate for an order at checkout time (given from/to addresses, line items with sku, title, unit price, quantity, optional tax code, and shipping cost). The estimate should NOT create a filing record. 2. A function to commit a finalized tax transaction after payment succeeds (accepts an order object that may already have a tax transaction code from the estimate step). 3. A function to void a committed transaction when an order is cancelled.
Also produce lib/avalaraClient.js that sets up and exports the initialized Avalara client, reading all configuration from environment variables.
Include a brief integration-notes.md describing when each function should be called in the order lifecycle.
{
"context": "Tests whether the agent correctly implements EU VAT logic including B2B reverse charge with VIES validation, correct fallback to B2C when VAT number is invalid, proper EU country detection, and awareness of OSS/IOSS thresholds and economic nexus rules.",
"type": "weighted_checklist",
"checklist": [
{
"name": "EU country list used",
"max_score": 8,
"description": "Code includes a list of EU country codes (at minimum: AT, BE, DE, FR, IT, ES, NL, PL, SE) used to determine if the destination is within the EU"
},
{
"name": "B2B cross-border reverse charge",
"max_score": 10,
"description": "When buyer provides a VAT number AND the destination country differs from the seller's country, reverse charge logic is applied (totalVAT = 0, vatType = 'reverse_charge' or equivalent)"
},
{
"name": "VIES validation before reverse charge",
"max_score": 10,
"description": "VAT number is validated via an external API call BEFORE applying reverse charge — reverse charge is NOT applied without validation"
},
{
"name": "VIES REST API URL",
"max_score": 10,
"description": "VIES validation uses the EC REST endpoint (ec.europa.eu/taxation_customs/vies/rest-api/ms/...) not a SOAP endpoint or third-party library"
},
{
"name": "Invalid VAT falls back to B2C",
"max_score": 10,
"description": "If VIES returns isValid=false (or the call fails), the code does NOT apply reverse charge but instead charges VAT at the destination country rate (B2C path)"
},
{
"name": "VAT rate by destination country",
"max_score": 8,
"description": "B2C VAT is charged at the destination country's rate (not a single flat rate for all EU countries)"
},
{
"name": "No VAT outside EU",
"max_score": 8,
"description": "Orders shipping to non-EU countries return zero VAT (vatType = 'none' or equivalent), not a default rate"
},
{
"name": "OSS/IOSS threshold documented",
"max_score": 8,
"description": "Code or comments mention the €10,000/year EU sales threshold above which OSS/IOSS registration is required for non-EU sellers"
},
{
"name": "US nexus thresholds documented",
"max_score": 8,
"description": "Code or comments include the economic nexus thresholds ($100,000/year OR 200 transactions/year for most US states)"
},
{
"name": "Real-time display emphasized",
"max_score": 8,
"description": "Integration notes or comments specify that tax must be calculated and displayed before the customer confirms payment (not deferred to post-payment)"
},
{
"name": "No hardcoded rates as primary",
"max_score": 6,
"description": "Does NOT use hardcoded VAT rates as the sole authoritative source — either calls a tax API or clearly documents rates as a simplified local fallback with a comment to use a maintained database in production"
},
{
"name": "Validated VAT number returned",
"max_score": 6,
"description": "When reverse charge applies, the validated VAT number is included in the return value (for audit/display purposes)"
}
]
}
Add EU VAT Compliance to a Cross-Border Checkout
Problem/Feature Description
EuroMart is a software tools marketplace based in Germany that recently opened sales to customers across all EU member states and the UK. The finance team has flagged an urgent compliance problem: the checkout currently charges a flat 19% German VAT on all orders regardless of the buyer's country or their business status. This is wrong — business customers in other EU countries should not pay VAT at all if they have a valid EU VAT registration number (the VAT liability shifts to them under the reverse charge mechanism), and B2C customers should pay VAT at their own country's rate, not Germany's.
Additionally, after a recent board meeting, the CTO asked for a summary of when EuroMart's cross-border tax obligations actually kick in — specifically, under what annual revenue conditions they need to register for OSS, and what thresholds apply to US sales tax nexus for a potential future US expansion.
Your task is to implement a VAT calculation module for EuroMart's Node.js checkout service and produce a short compliance reference document.
Output Specification
Produce a JavaScript module at lib/vatCalculation.js that exports a function to compute VAT for an order given:
toAddress(with acountryfield using ISO 3166-1 alpha-2 codes)lineItems(array withunitPriceandquantity)buyerVatNumber(optional string — present for B2B buyers)
The function should return an object describing the VAT amount, rate, and the type of VAT treatment applied.
Also produce a compliance-notes.md file summarizing:
- When EU VAT reverse charge applies and the validation step required
- The EU OSS/IOSS threshold that triggers mandatory registration for non-EU sellers
- The most common US economic nexus thresholds for context
Assume the seller is based in Germany (DE). The module should make real HTTP calls for VAT number validation — write the code as if network access is available.
{
"context": "Tests whether the agent correctly integrates TaxJar for US sales tax calculation, including correct package usage, API parameter structure, shipping tax codes, caching strategy, and graceful error handling.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Correct package",
"max_score": 8,
"description": "Uses the 'taxjar' npm package (import/require from 'taxjar'), not a generic HTTP client or hardcoded rates"
},
{
"name": "API key from env",
"max_score": 8,
"description": "Initializes TaxJar client with apiKey read from process.env.TAXJAR_API_KEY (not hardcoded)"
},
{
"name": "Address fields",
"max_score": 8,
"description": "Passes both from_* (from_country, from_zip, from_state, from_city, from_street) AND to_* address fields to taxForOrder()"
},
{
"name": "Amount calculation",
"max_score": 8,
"description": "Sets 'amount' as the computed sum of unitPrice * quantity across all line items (not a fixed value)"
},
{
"name": "Line item structure",
"max_score": 8,
"description": "line_items array includes id, quantity, unit_price, product_tax_code, and discount fields for each item"
},
{
"name": "Shipping tax code",
"max_score": 8,
"description": "Uses 'FreightInside' (or equivalent TaxJar freight code) as the tax code for shipping, not a blank or omitted code"
},
{
"name": "No hardcoded rates",
"max_score": 8,
"description": "Does NOT contain hardcoded numeric tax rate constants (e.g. 0.08, 8%) used as primary tax values — relies on TaxJar API response"
},
{
"name": "Cache key uses zip and total",
"max_score": 8,
"description": "Cache key is derived from both the destination zip code and the line item total (not just one or neither)"
},
{
"name": "Cache TTL is 1 hour",
"max_score": 8,
"description": "Cache entries are set with a TTL of 3600 seconds (1 hour)"
},
{
"name": "MD5 hash for cache key",
"max_score": 9,
"description": "Uses MD5 (createHash('md5')) to generate the cache key from the zip and total values"
},
{
"name": "Error fallback rate",
"max_score": 9,
"description": "When TaxJar API throws an error, applies a fallback tax rate of approximately 8.5% (0.085) OR explicitly blocks checkout with an error message — not silently ignores the error"
},
{
"name": "Transaction code stored",
"max_score": 10,
"description": "The returned tax result includes or the code persists the TaxJar transaction code (amount_to_collect / rate) on the order record for future reference"
}
]
}
Implement Tax Calculation for US E-Commerce Checkout
Problem/Feature Description
ShopBridge is a mid-sized US online retailer selling home goods and electronics across all 50 states. Until now, their checkout has displayed a static placeholder ("Tax: TBD") and collected taxes only at a flat 5% — a practice that has already drawn two state audit notices. The CTO wants to replace this with a proper, real-time tax engine that accurately computes sales tax the moment a customer enters their shipping address, before they click "Place Order."
The engineering team has decided to use TaxJar as their tax calculation provider. They want a reusable tax calculation module that can be called from the checkout service, plus a lightweight caching layer to keep checkout latency acceptable. The solution must also be resilient: if TaxJar is down during peak traffic, checkout should not break entirely.
Output Specification
Produce a self-contained JavaScript module at lib/taxCalculation.js that:
- Exports a function to calculate US sales tax for an order given from/to addresses, line items (each with a sku, title, unit price, quantity, optional tax code, and optional discount), and a shipping cost.
- Exports a function to retrieve a cached tax estimate or calculate and cache it if not present.
- Includes error handling so that a TaxJar API failure does not crash the checkout — document clearly in a comment what fallback strategy is used.
Also produce a short README.md explaining how to configure and call the module, including which environment variables are required.
Assume Redis is available at process.env.REDIS_URL. Do not actually connect to Redis or TaxJar in the code — the functions should be written as if those services exist but no real credentials are needed to read the code.
{
"name": "finsi/tax-calculation",
"version": "0.1.0",
"summary": "Tax engine integration (TaxJar, Avalara) with nexus rules and VAT handling",
"skills": {
"tax-calculation": {
"path": "SKILL.md"
}
}
}