From 4e68bb25620259378de76d2a25b11468f08a9aac Mon Sep 17 00:00:00 2001 From: Matthew Meszaros Date: Sun, 27 Sep 2026 11:10:46 +0200 Subject: [PATCH] feat: sell Warmbly to businesses only by requiring the buying company's legal name on the subscription and credit-pack Stripe Checkout sessions with a business-only and tax-exclusive notice beside the pay button, adding business-use and tax clauses to section 06 of the terms of service, and updating the billing guide to match --- docs/content/docs/guides/billing.mdx | 2 +- internal/app/stripe/service.go | 20 ++++++++++++++++++++ site/src/pages/terms.astro | 8 +++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/content/docs/guides/billing.mdx b/docs/content/docs/guides/billing.mdx index 85f2cd095..0f1381016 100644 --- a/docs/content/docs/guides/billing.mdx +++ b/docs/content/docs/guides/billing.mdx @@ -41,7 +41,7 @@ When you open something your plan does not include, the dashboard opens a full-s Promo codes can be applied in the chooser before checkout. -Stripe calculates applicable sales tax, VAT, or GST from the billing address entered at checkout. Where supported, a buyer purchasing for a company can add the company's legal name and business tax ID. Stripe includes those details on invoices and applies reverse charge or another zero-tax treatment where the jurisdiction and tax ID allow it. +Warmbly is sold to businesses only, so checkout asks for your company's legal name. Add your VAT or other business tax ID there too if your company has one. Stripe puts both on your invoices. Prices exclude taxes: where Warmbly is required to charge sales tax, VAT, or GST, Stripe adds it based on your billing address, and where it does not, your business accounts for any tax due in its own country, including under the reverse charge. Only the workspace owner can change the plan. Other members see the same comparison with a note to ask the owner. Which roles can do what is covered in [Team and roles](/guides/team-roles/). diff --git a/internal/app/stripe/service.go b/internal/app/stripe/service.go index e62f2cdbf..0b1be212e 100644 --- a/internal/app/stripe/service.go +++ b/internal/app/stripe/service.go @@ -248,6 +248,8 @@ func (s *stripeService) CreateCheckoutSession(ctx context.Context, userID uuid.U AutomaticTax: &stripe.CheckoutSessionAutomaticTaxParams{Enabled: stripe.Bool(true)}, BillingAddressCollection: stripe.String(string(stripe.CheckoutSessionBillingAddressCollectionRequired)), TaxIDCollection: &stripe.CheckoutSessionTaxIDCollectionParams{Enabled: stripe.Bool(true)}, + NameCollection: businessNameCollection(), + CustomText: businessOnlyNotice(), LineItems: []*stripe.CheckoutSessionLineItemParams{ { Price: stripe.String(priceID), @@ -416,6 +418,8 @@ func (s *stripeService) CreateCreditCheckoutSession(ctx context.Context, userID, AutomaticTax: &stripe.CheckoutSessionAutomaticTaxParams{Enabled: stripe.Bool(true)}, BillingAddressCollection: stripe.String(string(stripe.CheckoutSessionBillingAddressCollectionRequired)), TaxIDCollection: &stripe.CheckoutSessionTaxIDCollectionParams{Enabled: stripe.Bool(true)}, + NameCollection: businessNameCollection(), + CustomText: businessOnlyNotice(), LineItems: []*stripe.CheckoutSessionLineItemParams{ {Price: stripe.String(priceID), Quantity: stripe.Int64(1)}, }, @@ -1658,3 +1662,19 @@ func mapStripeStatus(status stripe.SubscriptionStatus) models.SubscriptionStatus return models.SubscriptionStatusIncomplete } } + +// businessOnlyCheckoutNotice sits beside the pay button; it states the terms' business-only and tax clauses. +const businessOnlyCheckoutNotice = "Warmbly is sold to businesses for business use only. Prices exclude taxes: where we do not charge VAT, your business accounts for it under the reverse charge." + +// businessNameCollection requires the buying company's legal name on every checkout. +func businessNameCollection() *stripe.CheckoutSessionNameCollectionParams { + return &stripe.CheckoutSessionNameCollectionParams{ + Business: &stripe.CheckoutSessionNameCollectionBusinessParams{Enabled: stripe.Bool(true), Optional: stripe.Bool(false)}, + } +} + +func businessOnlyNotice() *stripe.CheckoutSessionCustomTextParams { + return &stripe.CheckoutSessionCustomTextParams{ + Submit: &stripe.CheckoutSessionCustomTextSubmitParams{Message: stripe.String(businessOnlyCheckoutNotice)}, + } +} diff --git a/site/src/pages/terms.astro b/site/src/pages/terms.astro index bd9dd8780..dfea959ac 100644 --- a/site/src/pages/terms.astro +++ b/site/src/pages/terms.astro @@ -26,7 +26,7 @@ const sections = [ title="Terms of Service | Warmbly" description="The terms that govern your use of Warmbly: acceptable use for cold email sending, billing, account responsibilities, and service limitations." effective="2026-05-27" - updated="2026-05-27" + updated="2026-09-27" sections={sections} > @@ -92,9 +92,15 @@ const sections = [

06. Plans, billing, and refunds

+

+ Warmbly is sold to businesses for use in their trade or profession, not to consumers. By subscribing or buying credits you confirm that you are acting for business purposes, and you give us your company's legal name and, where it has one, its VAT or other business tax number. +

Paid plans are billed in advance on a recurring basis through Stripe. Fees, included usage, and billing cadence are shown on the pricing page and reflected in your invoice at the time of purchase.

+

+ Fees are exclusive of taxes. Where the law requires us to charge VAT, sales tax, or a similar tax, we add it to your invoice. Where we do not, you are responsible for accounting for any tax due in your country, including under the reverse charge. +

Subscriptions renew automatically until cancelled. You can cancel at any time from the billing area of your workspace, and your plan will remain active until the end of the current billing period. Except where required by law, fees already paid are non-refundable.