Sales tax on the setup fee: test-mode dry run

2026-10-09 · Stripe TEST mode (Sandbox) · stage TEST company (Fairhaven Residential (TEST)) on propflow-stage · code: branch feat/setup-fee-sales-tax (part 2) on top of #18415 (part 1)

Result

CheckResult
Stripe page shows the $4,000.00 fee plus a sales tax line for 1601 Blake St, Denver CO 80202PASS: tax $206.00 (5.15%), total $4,206.00
Clicking pay twice, the second time after the address was entered, serves the same pagePASS: same session id both times
A company that already has a Stripe customer can open the taxed page (the stored-customer path)PASS: Stripe accepted it and saved the address on the customer
After paying, the company record keeps the fee and the tax apartPASS: fee 400000, tax 20600, total 420600 (cents)
Billing card says "plus sales tax" before payingPASS

No bugs found. One thing to know: Colorado taxes this fee only in its 65 home-rule cities (Denver is one). The state treats it as exempt SaaS, so a Colorado customer billed outside those cities correctly pays $0 tax.

The two clicks

ClickCheckout sessionSession total at the time
New-customer page, first clickcs_test_a1xse1NKXD0xu6Vzzcc35KKjn6ThQICJRhocDHQPEOvrhPL3iepZi83kiY$4,000.00 (no address yet)
Same page, second click after entering the Denver addresscs_test_a1xse1NKXD0xu6Vzzcc35KKjn6ThQICJRhocDHQPEOvrhPL3iepZi83kiY (same)$4,206.00
Stored-customer page (after the first page's bank payment failed), first clickcs_test_a1xS06qDozIWRk7uyzIAmfyXGQFrPT8AkjVqk0LaZJgQzooLpjJ4zjLR2C$4,000.00
Same page, second click after entering the Denver addresscs_test_a1xS06qDozIWRk7uyzIAmfyXGQFrPT8AkjVqk0LaZJgQzooLpjJ4zjLR2C (same)$4,206.00

Each session's total was read back from Stripe just before the second click, so the reuse held while the page's total ($4,206.00) differed from the plan's fee ($4,000.00). Only one session was created per page.

Screenshots, the record Stripe's webhook wrote, and how it was run

Before: the Billing card

Billing card before paying
Onboarding › Billing: One-time setup fee: $4,000.00 plus sales tax.

The Stripe page with sales tax

Stripe page with Denver tax
Stored-customer page from the PR branch's server: subtotal $4,000.00, Sales Tax (5.15%) $206.00, total $4,206.00. The note under the form says sales tax is added based on the billing address.
Stripe page with Denver tax, new customer
New-customer page, same address, same tax.

Failed bank payment, then retry on the stored customer

The first page was paid with Stripe's test account that fails after processing. The webhook stored failed with the customer id, and the card offered the button again. The retry opened a new page on the stored customer (customer_update on), which Stripe accepted.

Card after a failed payment
After the failed payment: the fee line still says plus sales tax, with Try again.

Successful bank payment

Stripe page with the success test account
Test bank account linked (Success ••••6789).
Card while clearing
Back on Propflow while the bank transfer clears.
Card after paying
After Stripe's async success event. Staff view: Subscription pending: starts at go-live.

The record on the stage TEST company after checkout.session.async_payment_succeeded (read from propflow-stage):

billingSetup: { setupFeeStatus: "paid", setupFeeCents: 400000, setupFeeTaxCents: 20600, setupFeeTotalCents: 420600,
  checkoutSessionId: "cs_test_a1xS06qDozIWRk7uyzIAmfyXGQFrPT8AkjVqk0LaZJgQzooLpjJ4zjLR2C", stripeCustomerId: "cus_… (test)" }

Stripe's own read of that session: automatic_tax.status=complete, amount_subtotal=400000, total_details.amount_tax=20600, amount_total=420600. Breakdown: Denver city 5.15% standard_rated ($206.00); Colorado state 4% product_exempt ($0). The customer's address afterwards: 1601 Blake St, Denver CO 80202. Every forwarded webhook answered 200.

How it was run, and the cleanup

  • A local dev server running the PR branch, with STRIPE_SECRET_KEY set to the test key (every session id is cs_test_), DYNAMODB_TABLE_NAME=propflow-stage and AUTH_TABLE_NAME=propflow-auth-stage. Signed in as the stage smoke user; the session row was confirmed in the stage auth table and absent from prod. Nothing was written to live Stripe or to any production table.
  • The $4,000 plan was set through the staff route on the stage TEST company. stripe listen forwarded the test webhooks.
  • Test-mode Stripe Tax mirrors live: tax settings active, default tax code txcd_10103001, Colorado state registration plus the same 65 home-rule city registrations live has (set up by the prep lane).
  • Cleanup, read back from propflow-stage: billingPlan, billingSetup and billingSetupCheckout removed from the stage TEST company. Both test sessions are complete (nothing left open). Key files deleted from the lane directory.
PropFlow Docs