PRC-16Recommended
Keep old prices as archived catalogue prices with an end date
When a plan or price changes, keep existing subscribers on the archived price with a written end date, or no end date by decision. Move them in batches by a job that checks each notice first.
How to do it
Grandfathering is a promise, so record it. Archive the old price in the versioned catalogue so no new customer can choose it. Store on each price whether it is grandfathered and until when. Map each old plan to the new one, including its entitlements, so nobody loses a feature by accident. A move that raises the price needs the notice from Tell subscribers before a price rise. Move subscribers at their renewal date, never mid-period. List every archived price with its subscriber count each quarter, and retire one only when the count reaches zero.
Examples
TypeScript example
export async function migrateGrandfathered(db, oldPriceId: string, newPriceId: string, now = new Date()) {
const due = await db.subscriptions({ priceId: oldPriceId, renewsBefore: now })
for (const sub of due) {
const notice = await db.priceNotice(sub.id, newPriceId)
if (!notice || !mayMoveToNewPrice(notice, sub.region)) continue
await db.scheduleMove(sub.id, newPriceId, sub.currentPeriodEnd)
}
}
Python example
def migrate_grandfathered(db, old_price_id: str, new_price_id: str, now) -> None:
for sub in db.subscriptions(price_id=old_price_id, renews_before=now):
notice = db.price_notice(sub.id, new_price_id)
if notice is None or not may_move_to_new_price(notice, sub.region):
continue
db.schedule_move(sub.id, new_price_id, sub.current_period_end)
How to check
A test archives a price with 3 subscribers. A new checkout with that price is refused. The migration job moves only subscribers whose sent notice fits its window, at their renewal date. Each keeps every entitlement the old plan gave.
Related rules
- PRC-1 Keep prices in a versioned catalogue in Pricing, plans and entitlements
- PRC-2 Tell subscribers before a price rise in Pricing, plans and entitlements
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/pricing/plan-changes-trials-and-free-tiers/grandfathered-prices">Agent Wranglers pricing, plans and entitlements playbook</a>. In code you write, add a comment next to it with the same link.