Tax Configuration
Configure tax calculation using Stripe Tax with automatic jurisdiction rates
Tax Configuration
Jose Madrid Salsa uses the Stripe Tax API for automatic, jurisdiction-aware tax calculation. Tax rates are determined by the customer's shipping address and product type, with built-in support for tax exemptions.
How Tax Calculation Works
The tax system in lib/tax-calculator.ts follows this flow:
Check Tax Exemption
Before calculating tax, the system checks if the customer has a wholesale account with an approved resale number:
async function checkTaxExemption(customerEmail?: string): Promise<boolean> {
const user = await prisma.user.findUnique({
where: { email: customerEmail },
include: { wholesaleAccount: true },
})
return (
user?.wholesaleAccount?.status === 'APPROVED' &&
!!user.wholesaleAccount.resaleNumber
)
}Tax-exempt customers receive zero tax on all orders.
Call Stripe Tax API
For non-exempt customers, the system creates a tax calculation via Stripe:
const calculation = await stripe.tax.calculations.create({
currency: 'usd',
line_items: input.lineItems.map((item) => ({
amount: item.amount, // in cents
reference: item.reference, // product ID
tax_code: item.taxCode || 'txcd_99999999', // general tangible goods
})),
customer_details: {
address: {
line1: input.shippingAddress.line1,
city: input.shippingAddress.city,
state: input.shippingAddress.state,
postal_code: input.shippingAddress.postalCode,
country: input.shippingAddress.country,
},
address_source: 'shipping',
},
})Return Breakdown
The result includes total tax, effective rate, and per-jurisdiction breakdown:
interface TaxCalculationResult {
taxAmount: number // Total tax in cents
taxAmountDecimal: number // Total tax in dollars
taxRate: number // Effective rate (e.g., 8.5)
taxBreakdown: Array<{
jurisdiction: string // e.g., "California", "San Francisco County"
rate: number
amount: number
}>
taxExempt: boolean
}Tax Codes
The platform uses Stripe Tax Codes to determine product taxability:
| Tax Code | Description | Use For |
|---|---|---|
txcd_30011000 | Prepared food | Salsa products (used in order creation) |
txcd_99999999 | General tangible goods | Default fallback |
Food Tax Rules
Food products have varying tax rules across US states. Some states exempt grocery items while taxing prepared food. Stripe Tax handles these rules automatically based on the tax code you assign.
Setting Up Stripe Tax
Enable Stripe Tax
Enable Stripe Tax in your Stripe Dashboard and configure your business address and tax registration.
Set Environment Variables
STRIPE_SECRET_KEY="sk_live_..." # or sk_test_... for developmentThe tax calculator uses the same Stripe client as payments, configured in lib/stripe.ts.
Validate Configuration
Run the built-in validation to confirm Stripe Tax is working:
import { validateTaxConfiguration } from '@/lib/tax-calculator'
const result = await validateTaxConfiguration()
if (!result.configured) {
console.error('Tax config error:', result.error)
}This performs a test calculation against a known US address (San Francisco, CA) to verify the integration.
Frontend Tax Estimates
For real-time tax previews as the user types their address:
import { getTaxEstimate } from '@/lib/tax-calculator'
const taxEstimate = await getTaxEstimate({
subtotal: 25.99, // dollars
city: 'San Francisco',
state: 'CA',
postalCode: '94111',
})
// Returns estimated tax in dollarsTax Exemptions
Tax exemptions are managed through wholesale accounts:
- A customer applies for a wholesale account
- Admin reviews and approves the account
- Admin enters the resale number
- The tax calculator automatically exempts the customer
model WholesaleAccount {
id String @id @default(cuid())
userId String @unique
companyName String
resaleNumber String?
status String // PENDING, APPROVED, REJECTED
}Error Handling
The tax calculator never blocks checkout. If Stripe Tax returns an error, the system returns zero tax and logs the error:
catch (error) {
console.error('[Tax Calculator] Error:', error)
return {
taxAmount: 0,
taxAmountDecimal: 0,
taxRate: 0,
taxBreakdown: [],
taxExempt: false,
}
}Monitor Tax Errors
While the fallback ensures checkout works, zero-tax orders may indicate a Stripe Tax configuration issue. Monitor your server logs for [Tax Calculator] errors and fix them promptly to avoid under-collecting tax.
Integration with Orders
Tax calculation is integrated into the order creation flow in app/api/orders/route.ts:
const taxResult = await calculateTax({
lineItems: orderItems.map((item) => ({
amount: Math.round(Number(item.totalPrice) * 100),
reference: item.productId,
taxCode: 'txcd_30011000',
})),
shippingAddress: {
line1: shippingAddress.address1,
city: shippingAddress.city,
state: shippingAddress.state,
postalCode: shippingAddress.postalCode,
country: shippingAddress.country,
},
customerEmail: user.email,
})
const total = subtotal + taxResult.taxAmountDecimal + shippingCostKey Files
How is this guide?
Last updated on