Charge $1 for the first month instead of a free trial#176
Merged
Conversation
A $0 trial only runs a weak card authorization, so invalid cards slip through and only fail once the real charge fires days later. Applying a $11-off, duration=once Stripe coupon at checkout charges $1 for real on the first invoice instead, which validates the card immediately, then reverts to the full monthly price on the next invoice with no manual swap needed.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Two hardening fixes for the paid first month: - FirstMonthCheckoutDiscount throws when the paid first month is enabled but STRIPE_FIRST_MONTH_COUPON_ID is unset, instead of silently charging every new customer the full price with no discount. - Guard workspace store() with the same active-subscription check create() already applies, so a direct POST can't bootstrap a second billable workspace and inflate checkout quantity past the fixed first-month coupon.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
The fixed amount_off coupon only nets $1 for a quantity of one, and the offer is meant for genuinely new signups. A lapsed account that kept several workspaces and re-subscribes through onboarding would otherwise be charged N*price - amount_off (not $1), and a returning customer could whittle down to one workspace to claim the discount again. Apply the coupon only when the paid first month is enabled, the account bills a single workspace, and it has never subscribed before; every other checkout falls back to allowing promotion codes at the full price.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
A raw subscriptions()->exists() check treated a leftover incomplete / incomplete_expired row — which Cashier persists when a first payment fails or a 3DS challenge is abandoned — as a prior subscription, so a genuinely new customer retrying after a failed first attempt lost the $1 coupon and was charged full price. Exclude those never-started statuses so only a subscription that actually started (active, canceled, etc.) marks the account as a returning customer.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Replaces the 7/8-day free trial at signup checkout with a real $1 charge on the first invoice, so a genuine card authorization+capture validates the card up front instead of a $0 trial authorization that lets invalid cards slip through until the real charge fires days later.
How it's built
amount_off=1100,currency=usd,duration=once) is applied to the checkout session instead oftrialDays(). It discounts only the first invoice ($12 → $1); the next invoice bills the full $12 automatically — no manual swap/cron job needed.app/Support/Billing/FirstMonthCheckoutDiscount.php— applies the coupon whenrequire_card_for_trial=true(the default, card-required flow). The separate no-card generic-trial flow (require_card_for_trial=false) is untouched and keepsallowPromotionCodes()instead, since Stripe rejects a Checkout Session that sets bothdiscountsandallow_promotion_codes.config/cashier.php/.env.example— newSTRIPE_FIRST_MONTH_COUPON_IDconfig.isOnTrial()/trial-dependent consumer (AccountPolicy,EnsureAccountReady,BillingCycle, the PostHog usage sync job) — no changes needed, they all react dynamically to the subscription no longer entering atrialingstate.Testing
Unit test covering both branches of
FirstMonthCheckoutDiscount(coupon applied vs. promotion codes allowed), exercised against a realSubscriptionBuilderwith no network calls. Full backend suite green (2476 passed).Deploy note
The coupon (
TRIAL1USD, $11 off, once, USD) exists in Stripe test mode already. The equivalent live coupon has been created — setSTRIPE_FIRST_MONTH_COUPON_IDto its live ID in the production.envbefore this ships.