Files
at-container-registry/pkg
Evan JarrettandClaude Opus 5 3263156067 billing: key entitlements on the Stripe product, not the price
Tier resolution matched a subscription's price ID against the configured
stripe_price_monthly/stripe_price_yearly, which inverts what a price change
is supposed to do. Stripe prices are immutable, so changing what a tier costs
means creating a new price, and Stripe never migrates existing subscribers off
the old one. Updating the config to the new price IDs therefore un-tiers
precisely the subscribers a price change is meant to leave alone.

They did not even drop cleanly to free. An unresolved tier logs a warning,
returns nil, and the event is recorded in stripe_processed_events -- so Stripe
answers 200, never redelivers, and a later dashboard Resend is swallowed by the
idempotency check. Reproduced against the sandbox: a subscription on a price
the config does not list granted nothing, and the event could not be replayed
afterwards.

A tier has one product and many prices over its life, so the product is the
durable key for an entitlement. Tiers gain a stripe_product field, and
resolution tries the product first, falling back to the price IDs so configs
without it keep working unchanged. Checkout still keys on price -- that
direction has to name a specific price to charge.

Verified live: a subscription on a price created outside the config, under the
Pro product, resolved to tierName=Pro tierRank=2 and landed on the hold. The
same shape with an unknown product produced the silent no-op before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxF2N3HuZ8xSkx6nkirgB
2026-08-25 16:34:26 -05:00
..
2026-05-11 19:53:13 -05:00