feat(billing): collect business name and VAT ID at Stripe checkout
Summary
- Turn on Stripe tax ID collection in Checkout. Business buyers can now enter their legal business name and VAT (or other tax) ID, and both show on every subscription invoice.
- Stop org renames from overwriting the Stripe customer name once the customer has a legal business name.
Why / Context
A customer asked to upgrade to Pro but needs invoices with their company name, address and VAT number. Checkout already requires a billing address, but there was no field for a business name or tax ID.
How It Works
-
getCheckoutSessionParamsaddstax_id_collection: { enabled: true }. The tax ID stays optional (Stripe's defaultrequired: "never"), so individual buyers see only an unchecked "I'm purchasing as a business" box. -
For an existing customer, Stripe requires
customer_update.name: "auto"when tax IDs are collected. The better-auth plugin only setsaddress: "auto"for org customers, so we set{ name: "auto", address: "auto" }. Our params are applied after the plugin's, so ours win. We only send it when the org has a customer ID, because Stripe rejectscustomer_updatewithout one. -
Checkout saves the business name to
customer.nameandcustomer.business_name, and the tax ID tocustomer.tax_ids(Stripe docs). -
Org renames sync the name to
customer.namefrom two places:afterUpdateOrganizationand theorganization.updatetRPC procedure. Both now call onesyncCustomerNamehelper inpackages/auth/src/stripe.ts, which skips the sync when the customer has abusiness_nameor any tax ID. The tax ID check covers names set through the billing portal, which may not setbusiness_name. -
customer_update.name: "auto"also applies when the buyer leaves the business box unchecked, so Checkout replaces the customer name with the cardholder name.onSubscriptionCompletenow callssyncCustomerNameto put the org name back, with the same business name / tax ID guard.
Manual QA Checklist
superset-home ran these end to end in Stripe test mode at 635bf4a2 (real Checkout pages, test clocks for renewals). All pass. Report: https://app.superset.sh/page/pr-8452-review-stripe-checkout-vat-id-gyk651
- Upgrade checkout shows "I'm purchasing as a business". Enter a business name and
DE123456789, then pay. - The Stripe customer has
nameandbusiness_nameset to the business name, and the tax ID is saved. - The first invoice shows the business name, billing address and VAT ID.
- Settings → Billing shows the tax ID and name (not covered by the test run).
- Renaming the org does not change the Stripe customer name.
- Checkout without the business box checked works as before, and a rename still syncs the name to Stripe.
Testing
biome check packages/auth/src/server.ts: clean.bun run typecheckinpackages/auth: no errors in the changed lines. It reports 2 errors elsewhere in the file, atserver.ts:426(api-key plugin) andserver.ts:1740(signJWT). Both come from two copies of@better-auth/core@1.6.22in my local install, not from this change.
Known Limitations
- For a buyer who skips the business fields, the first invoice says "Bill to: " instead of the org name. Stripe finalizes it during Checkout, before
onSubscriptionCompleteruns. Renewals use the org name again. - This change does not compute or charge tax. We don't use Stripe Tax, so no VAT is added and nothing is reverse-charged. The VAT ID only appears on the invoice.
- Customers who already pay can't add a VAT ID through this change. For them, turn on editing of tax IDs, billing address and name in the Customer portal settings in the Stripe Dashboard.
- Each org rename now calls the Stripe API once more (one customer retrieve).
Summary by cubic
Enables optional tax ID collection at Stripe checkout so business buyers can add their legal business name and VAT (or other tax) ID, which appear on invoices. Stops org renames from overwriting the Stripe customer name once a business name is set, and restores the org name when a checkout skips the business fields.
Bug Fixes
- Both org rename paths now use one
syncCustomerNamehelper that skips the sync when the customer has a business name or tax ID, so names set through the billing portal are kept. - On subscription complete,
syncCustomerNamerestores the org name thatcustomer_update.name: "auto"replaced with the cardholder name for non-business buyers. - Skips tax ID collection entirely for existing customers who already have a tax ID, since Stripe hides the form but still overwrites their company name.
Known Limitations
- Doesn't compute or charge tax; Stripe Tax isn't used, so the VAT ID only appears on the invoice.
- Existing customers can't add a VAT ID through this change; enable tax ID editing in the Customer portal settings instead.
Summary by CodeRabbit
- New Features
- Checkout requests tax ID information when a customer doesn’t already have one. When applicable, Stripe can also update the customer’s name and address automatically.
- Improvements
- Eligible organization name changes are synchronized with Stripe, while existing business names and customers with tax IDs are preserved.
- After eligible non-enterprise subscriptions complete, customer names are synchronized with Stripe. Synchronization errors are logged without interrupting the remaining processing.