Skip to content

Steps 3a/3b — new plans get a Product and Price; archive instead of delete - #807

Draft
mitchelljkotler wants to merge 1 commit into
pricing-modelsfrom
pricing-models-plan-signals
Draft

Steps 3a/3b — new plans get a Product and Price; archive instead of delete#807
mitchelljkotler wants to merge 1 commit into
pricing-modelsfrom
pricing-models-plan-signals

Conversation

@mitchelljkotler

Copy link
Copy Markdown
Member

Stacks on #781. Independent of the migration work (#800/#805/#806) — needs only PlanPrice to 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 standard PlanPrice for the plan's interval, letting 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, which is what ensure_stripe_price already does with a zero amount — and it means every plan has a price for a subscription to point at, which is what 3d needs.
  • A price_per_user is now logged, not dropped silently. The legacy tiered Price expressed it as a second tier above minimum_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_planarchive_stripe_plan

Stripe offers no delete for a Price: the object is immutable and permanent once anything could have billed against it. archive_price and archive_product join the PlanService interface, which previously 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 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 PlanPrice row 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

@allanlasser
allanlasser temporarily deployed to squarelet-pi-pricing-mo-mymw54 September 4, 2026 20:46 Inactive
…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
mitchelljkotler force-pushed the pricing-models-plan-signals branch from 9f066ea to 8786c7a Compare September 5, 2026 20:02
@mitchelljkotler
mitchelljkotler temporarily deployed to squarelet-pi-pricing-mo-mymw54 September 5, 2026 20:02 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants