Steps 3a/3b — new plans get a Product and Price; archive instead of delete - #807
Draft
mitchelljkotler wants to merge 1 commit into
Draft
Steps 3a/3b — new plans get a Product and Price; archive instead of delete#807mitchelljkotler wants to merge 1 commit into
mitchelljkotler wants to merge 1 commit into
Conversation
…lete Steps 3a and 3b. make_stripe_plan() created a legacy Stripe Plan -- the old API's combined product-and-price -- so every plan added through the admin between now and the cutover was one more row to fix by hand. It now creates the Product and a standard PlanPrice for the plan's interval, and lets ensure_stripe_price do the Stripe half. The row is created whatever the amount. A free plan gets a price of zero and no Stripe Price at all, which is what ensure_stripe_price does with a zero amount, and it means every plan has a price for a subscription to point at -- the condition step 3d needs. A per-user rate is now logged rather than dropped in silence. The legacy tiered Price expressed it as a second tier above minimum_users; a flat Price cannot, and in this model the blocks above a tier are a usage pack bought alongside it. Quietly ignoring the field would be a quiet mispricing. delete_stripe_plan becomes archive_stripe_plan, because Stripe offers no delete for a Price: the object is immutable and permanent once anything could have billed against it, and deactivating is the whole of what removal means. archive_price and archive_product join the PlanService interface, which had only the legacy Plan API's real delete support. The Product is archived only once no PlanPrice rows remain, so a plan keeping one variant does not lose the Product the others hang off. It fires rarely now -- retiring a plan sets Plan.archived instead of deleting the row -- but has to be right for the plans that can still be deleted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mitchelljkotler
force-pushed
the
pricing-models
branch
from
September 5, 2026 20:02
95c30e2 to
b066ff5
Compare
mitchelljkotler
force-pushed
the
pricing-models-plan-signals
branch
from
September 5, 2026 20:02
9f066ea to
8786c7a
Compare
mitchelljkotler
temporarily deployed
to
squarelet-pi-pricing-mo-mymw54
September 5, 2026 20:02
Inactive
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.
Stacks on #781. Independent of the migration work (#800/#805/#806) — needs only
PlanPriceto exist.3a —
make_stripe_plan()It created a legacy Stripe Plan, the old API's combined product-and-price. So every plan added through the admin between now and the cutover is one more row to fix by hand — the gap widens the longer it sits.
Now creates the Product and a
standardPlanPricefor the plan's interval, lettingensure_stripe_pricedo the Stripe half.ensure_stripe_pricealready does with a zero amount — and it means every plan has a price for a subscription to point at, which is what 3d needs.price_per_useris now logged, not dropped silently. The legacy tiered Price expressed it as a second tier aboveminimum_users; a flat Price can't, and in this model the blocks above a tier are a usage pack bought alongside it. Silently ignoring the field would be a quiet mispricing.3b —
delete_stripe_plan→archive_stripe_planStripe offers no delete for a Price: the object is immutable and permanent once anything could have billed against it.
archive_priceandarchive_productjoin thePlanServiceinterface, which previously had only the legacy Plan API's real delete support.The Product is archived only once no
PlanPricerows remain, so a plan keeping one variant doesn't lose the Product the others hang off.Fires rarely now that retiring a plan sets
Plan.archived(#806) instead of deleting the row, but has to be right for the plans that can still be deleted.QA
Create a plan in the admin on the preview deploy and check Stripe: one Product tagged with the slug, one recurring Price at the base price, and a
PlanPricerow on the plan. Create a free plan and confirm the row exists at zero with no Stripe Price. Create one with a per-user rate and confirm the warning is logged.🤖 Generated with Claude Code
https://claude.ai/code/session_0151rwxtvLg1vKEiz7XsJfzf