Bill against the new Stripe Prices - #792
Closed
mitchelljkotler wants to merge 1 commit into
Closed
Conversation
stripe_items() sent item.plan.stripe_id -- the legacy identifier built from the slug -- so every PlanPrice and every Stripe Price made by consolidate_stripe_products was inert. Stripe held both the old Plans and the new Prices, and squarelet billed against the old ones. Lines now bill against their PlanPrice's Stripe Price, falling back to the plan's legacy id. The fallback is not a transitional convenience that disappears once the backfill finishes: it covers the three populations that keep a null plan_price by design -- per-user subscribers awaiting decomposition, deferred slugs, and every signup until the purchase flow records a price. It goes away in 3e, once nothing can be null. Subscription.free moves with it, and has to. It read Plan.free, which is computed from base_price and price_per_user -- not from the price the line is actually on. A comped PlanPrice sits on a *paid* plan, so a subscription with one paid line and one comped line was not free, went to Stripe, and would have sent a blank plan id for the comped line, since a comped price has no Stripe Price at all. Both now ask the line, through SubscriptionItem.is_free, and stripe_items drops free lines rather than naming an object that does not exist. This has to precede the decomposition: the pack plans are created through a historical model, so the make_stripe_plan signal never fires for them and they have no legacy Stripe Plan to fall back to. A pack line cannot be created until this exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
stripe_items() sent item.plan.stripe_id -- the legacy identifier built from the slug -- so every PlanPrice and every Stripe Price made by consolidate_stripe_products was inert. Stripe held both the old Plans and the new Prices, and squarelet billed against the old ones.
Lines now bill against their PlanPrice's Stripe Price, falling back to the plan's legacy id. The fallback is not a transitional convenience that disappears once the backfill finishes: it covers the three populations that keep a null plan_price by design -- per-user subscribers awaiting decomposition, deferred slugs, and every signup until the purchase flow records a price. It goes away in 3e, once nothing can be null.
Subscription.free moves with it, and has to. It read Plan.free, which is computed from base_price and price_per_user -- not from the price the line is actually on. A comped PlanPrice sits on a paid plan, so a subscription with one paid line and one comped line was not free, went to Stripe, and would have sent a blank plan id for the comped line, since a comped price has no Stripe Price at all. Both now ask the line, through SubscriptionItem.is_free, and stripe_items drops free lines rather than naming an object that does not exist.
This has to precede the decomposition: the pack plans are created through a historical model, so the make_stripe_plan signal never fires for them and they have no legacy Stripe Plan to fall back to. A pack line cannot be created until this exists.