When checkout shows a base price and applies taxes silently on charge, the first time a user sees the true total is on their bank statement — a direct violation of FTC Negative Option Rule (2025) transparency requirements and EU Consumer Rights Directive 2011/83/EU Article 6 pre-contract information obligations. Jurisdictions with sales tax on SaaS (Texas, Ohio, Washington, and most EU member states) make this gap material and frequent. Silent tax additions are also the second most-cited cause of subscription chargebacks after unrecognized recurring charges.
High because silently adding taxes between the displayed price and the actual charge constitutes a material misrepresentation under both FTC rules and EU consumer law, triggering chargeback liability and regulatory exposure.
Show a tax-inclusive price summary before the payment submit button. For Stripe Checkout, enable automatic_tax and Stripe will display the breakdown:
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
automatic_tax: { enabled: true },
})
For custom checkout forms, call a preview endpoint and render an order summary in app/(checkout)/checkout/page.tsx:
<div className="order-summary">
<div className="line-item"><span>Pro Plan (monthly)</span><span>$29.00</span></div>
<div className="line-item"><span>Tax</span><span>{tax > 0 ? formatCents(tax) : 'Calculated at checkout'}</span></div>
<div className="line-item total"><strong>Total due today</strong><strong>{formatCents(total)}</strong></div>
</div>
The total must appear before, not after, the submit button.
ID: subscription-compliance.pre-purchase.total-cost-disclosed
Severity: high
What to look for: Trace the checkout flow from the pricing page through to payment entry. At what point does the user see the total amount they will be charged — including applicable taxes? Check whether the checkout page or modal shows a price breakdown: base price, tax (if applicable), and total. Look for Stripe Tax integration (automatic_tax: { enabled: true }) and whether the tax display is enabled for the customer before payment collection. If using Stripe Checkout (hosted), check whether automatic_tax is enabled in the session creation — Stripe will then display taxes automatically. If using a custom payment form, check whether a price confirmation summary with tax is shown before the submit button. Check whether the total displayed matches what is actually charged (i.e., the price shown is not pre-tax while the actual charge is post-tax). Count all instances found and enumerate each.
Pass criteria: Before the user submits payment, the total amount to be charged — including taxes and any fees — is displayed. If taxes are calculated dynamically (e.g., by jurisdiction), the tax estimate or confirmation is shown before the charge is processed, not only in the receipt. At least 1 implementation must be confirmed.
Fail criteria: The user sees only the base price before submitting payment; taxes are applied silently. The first time the user sees the total including tax is on the receipt or bank statement. No price confirmation summary is shown before the submit button.
Skip (N/A) when: The application does not charge tax and there are no additional fees beyond the stated subscription price (tax-free jurisdictions or B2B with reverse-charge).
Detail on fail: Specify what is missing. Example: "Checkout page shows $29/month but tax is not displayed before submission. Stripe Tax is enabled on the account but automatic_tax is not enabled in the session, so taxes are applied post-submission." or "Custom checkout form shows the plan price in the heading but no total-with-tax line item above the Pay button.".
Remediation: Show a price summary with tax before the user submits payment:
// For Stripe Checkout — enable automatic tax display:
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
automatic_tax: { enabled: true },
tax_id_collection: { enabled: true }, // for B2B
// Stripe Checkout will display tax breakdown automatically
})
// For custom checkout forms — retrieve and display tax estimate:
// app/api/checkout/preview/route.ts
export async function POST(req: Request) {
const { priceId, customerLocation } = await req.json()
// Calculate tax preview using Stripe Tax
const invoice = await stripe.invoices.retrieveUpcoming({
customer: customerId,
subscription_items: [{ price: priceId }],
})
return Response.json({
subtotal: invoice.subtotal,
tax: invoice.tax ?? 0,
total: invoice.total,
currency: invoice.currency,
})
}
Display the summary in your checkout UI:
<div className="order-summary">
<div className="line-item">
<span>Pro Plan (monthly)</span>
<span>$29.00</span>
</div>
<div className="line-item">
<span>Tax</span>
<span>{taxAmount > 0 ? `$${(taxAmount / 100).toFixed(2)}` : 'Calculated at checkout'}</span>
</div>
<div className="line-item total">
<strong>Total due today</strong>
<strong>${(total / 100).toFixed(2)}</strong>
</div>
</div>