Avalara
Medusa plugin for integrating Avalara AvaTax as a tax provider, enabling automated tax calculations and compliance for e-commerce transactions.
📋 Table of Contents
- ✨ Features
- 🚀 Quick Start
- 📋 Configuration Options
- 🎯 Advanced Usage
- ⚙️ How the plugin works?
- 🔧 Troubleshooting
- ✋ Need help?
- 🤝 Contributing
- 📄 License
✨ Features
- Real-time Tax Calculations: Automatically calculate accurate taxes during checkout using Avalara's AvaTax API
- Order Transaction Management: Create AvaTax transactions when orders are placed for proper tax recording
- Transaction Lifecycle Tracking:
- Commit transactions when orders are completed/fulfilled
- Void transactions when orders are canceled
- Flexible Configuration: Support for custom shipping addresses and tax codes
- Address Validation: Validate and standardize shipping addresses using Avalara's address validation service
🚀 Quick Start
1. Installation
# npmnpm install @u11d/medusa-avalara# yarnyarn add @u11d/medusa-avalara
2. Basic Medusa Config
Add the plugin to your Medusa config file () using the helper:
import { defineConfig } from "@medusajs/framework/utils";import { withAvalaraPlugin, AvalaraPluginOptions } from "@u11d/medusa-avalara";const avalaraPluginOptions: AvalaraPluginOptions = {client: {accountId: "YOUR_ACCOUNT_ID",licenseKey: "YOUR_LICENSE_KEY",environment: "sandbox",companyCode: "DEFAULT",},shipFromAddress: {line1: "512 S Mangum St Ste 100",city: "Durham",region: "NC",country: "US",postalCode: "27701-3973",},taxCodes: {shipping: "FR020100",default: "PC040100", // Clothing and related products (business-to-customer)-general},};module.exports = defineConfig(withAvalaraPlugin({projectConfig: {// your project config},plugins: [// other plugins],modules: [// your modules],},avalaraPluginOptions));
Important Notes:
- The wrapper handles plugin modules registration and dependency injection automatically. See Manual Module Registration section if you need to understand what the helper does or configure modules manually
- You can use environment variables instead of hardcoding options, especially important for credentials like and
- The example above will use for each product. See Advanced Usage for assigning specific tax codes to individual products
- The should reflect your Avalara configuration - ensure it matches the address configured in your Avalara account for accurate tax calculations
- The plugin fully supports tax-inclusive pricing and automatically respects the region's tax-inclusive preferences
3. Run Database Migration
After configuring your Medusa setup, run the database migration to create the required tables:
npx medusa db:migrate
4. Enable AvaTax Provider
After starting your Medusa server:
- Go to your admin panel (locally available at by default)
- Navigate to Settings > Tax Regions
- Select a tax region and edit it
- Select Avalara as your tax provider
- Save the configuration
📋 Configuration Options
Client Options ()
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
| ✅ | - | Your Avalara account ID | ||
| ✅ | - | Your Avalara license key | ||
| ✅ | - | AvaTax environment | ||
| ✅ | - | Your company code in AvaTax | ||
| ❌ | Controls whether documents should be recorded in AvaTax. If set to transactions (sales invoices) are not created | |||
| ❌ | - | Custom app name | ||
| ❌ | - | Custom app version | ||
| ❌ | - | Machine identifier |
Ship From Address ()
| Option | Type | Required | Description |
|---|---|---|---|
| ✅ | Street address line 1 | ||
| ❌ | Street address line 2 | ||
| ✅ | City name | ||
| ⚠️ (required for US) | State/province code | ||
| ✅ | ISO 2-letter country code | ||
| ✅ | Postal/ZIP code |
Tax Codes ()
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
| ❌ | Default tax code for products | |||
| ❌ | Tax code for shipping |
🎯 Advanced Usage
Using Different Tax Codes for Products
In most e-commerce scenarios, different products require different tax codes based on their category, material, or intended use. The plugin uses the table to determine which specific Avalara tax code should be applied to each product during tax calculations. You can manage these product-specific tax codes either by updating the database table directly or by using the provided admin API endpoint.
You can find the complete list of available Avalara tax codes at: https://taxcode.avatax.avalara.com/
By default, all products will use the value. To assign specific Avalara tax codes to individual products, you'll need to authenticate and make a request to .
Step 1: Get Authentication Token
First, authenticate to get a Bearer token:
curl -X POST http://localhost:9000/auth/user/emailpass \-H "Content-Type: application/json" \-d '{"email": "your_admin_email@example.com","password": "your_admin_password"}'
This will return a response containing a field. Copy the token value.
Step 2: Update Product Tax Codes
Use the token to update product tax codes:
curl -X PUT http://localhost:9000/admin/avalara-products \-H "Content-Type: application/json" \-H "Authorization: Bearer YOUR_TOKEN_HERE" \-d '{"avalara_products": [{"product_id": "prod_123","tax_code": "PC040100"},{"product_id": "prod_456","tax_code": "PS081000"}]}'
Address Validation
The plugin provides a endpoint that serves as a proxy to Avalara's ResolveAddressPost API. Address validation is critical for accurate tax calculations, as Avalara requires correctly formatted and validated addresses to determine proper tax rates and jurisdictions.
Request Body:
{"line1": "512 S Mangum St Ste 100","city": "Durham","region": "NC","postalCode": "27701","country": "US"}
Notes:
- Address validation is available for US and Canadian addresses
- Use this endpoint in your storefront during checkout to validate and save corrected addresses before tax calculation
- The response includes validated addresses, coordinates, resolution quality, and tax authorities
- Refer to Avalara's API documentation for more details: https://developer.avalara.com/api-reference/avatax/rest/v2/methods/Addresses/ResolveAddressPost/
Manual Module Registration
If you prefer to configure modules manually without using the wrapper, you can register each module individually:
import { defineConfig, Modules } from "@medusajs/framework/utils";import { AvalaraPluginOptions } from "@u11d/medusa-avalara";const options: AvalaraPluginOptions = {// your options here};module.exports = defineConfig({plugins: ["@u11d/medusa-avalara"],modules: [{resolve: "@u11d/medusa-avalara/modules/avalara-product",dependencies: [Modules.CACHE],},{resolve: "@u11d/medusa-avalara/modules/avatax-factory",options,dependencies: [Modules.CACHE],},{resolve: "@medusajs/medusa/tax",options: {providers: [{resolve: "@u11d/medusa-avalara/providers/avatax",options,},],},dependencies: [Modules.CACHE],},],});
Note: Manual registration requires careful attention to module dependencies and isolation. The wrapper is recommended as it handles these complexities automatically.
⚙️ How the plugin works?
The Medusa Avalara plugin integrates with Avalara's AvaTax service through a modular architecture:
Core Components
modules/avalara-product
- Manages product-specific tax codes through the model and database migration
- Used by the API endpoint
- Saves mapping of → in cache for fast retrieval by the AvaTax provider
modules/avatax-factory
- Provides AvaTax client configured with plugin options
- Validates plugin options and credentials
- Validates communication with Avalara to ensure credentials are correct (validation happens in loader)
providers/avatax
- Tax calculation provider implementation (separate from due to Medusa's module isolation requirements)
- Skips tax calculation if shipping address is not provided
- Retrieves from cache; uses default tax code or throws error if not found
- Makes API calls to AvaTax to create entities (temporary entities for dynamic cart tax calculations)
- The method handles tax rate calculations using the AvaTax API
Order Lifecycle Integration
Subscribers and Workflows:
- orderPlacedHandler: Creates Sales Invoices (permanent entities representing finalized transactions in Avalara)
- orderCanceledHandler: Voids the transaction in Avalara
- orderCompletedHandler: Commits the transaction in Avalara
This architecture ensures accurate tax calculations during checkout and proper transaction lifecycle management in Avalara's system. The wrapper simplifies the setup by automatically configuring all these modules with proper dependencies and isolation.
🔧 Troubleshooting
Migration Error: "relation 'public.avalara_product' does not exist"
If you encounter this error:
Solution: Run the database migration:
npx medusa db:migrate
This ensures the table is created and the cache module is properly configured.
Tax calculations returning $0
If you're seeing $0 tax amounts in your calculations, follow these troubleshooting steps:
- Check application logs for any error messages or warnings related to AvaTax API calls
- Verify shipping address - tax calculation is skipped if no valid shipping address is provided
- Validate Avalara account configuration:
- Ensure your company location is properly configured in your Avalara account
- Verify that tax jurisdictions are set up correctly for your business locations
- Check that your company has nexus configured for the relevant states/regions
- Test tax calculation directly in Avalara:
- Log into your Avalara account
- Use the AvaTax API testing tools to verify tax calculations work with your setup
- Test with the same addresses and product codes you're using in Medusa
- Review tax codes:
- Ensure products have valid Avalara tax codes assigned
- Verify that the tax codes are appropriate for your products and jurisdiction
- Check environment settings:
- Confirm you're using the correct environment (sandbox vs production)
- Verify your API credentials are valid and have the necessary permissions
✋ Need help?
If you encounter any issues or need assistance with this plugin, please visit our GitHub Issues page. Our team actively monitors and responds to bug reports, feature requests, and questions from the community. We aim to provide timely support to ensure your integration with Avalara runs smoothly.
Need expert assistance or want our team to support your Medusa project? We're here to help! Contact us at https://u11d.com/contact/ for professional support and consultation services.
📖 Learn More
Read our comprehensive blog article about integrating Avalara with Medusa: https://u11d.com/blog/automated-tax-calculations-medusa-avalara-integration
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.