Skip to main content

Entitlements migration contract

Verifies a migration from local DB has_paid_access flags to Stripe Entitlements. Catches every direction of drift so you know exactly when the migration is complete.

Why this exists

Stripe Entitlements makes Stripe the source of truth for feature access. The migration is the risky part — N users with has_paid_access = true in your DB need the corresponding Entitlements grant in Stripe. Miss one, and a paying customer loses access. ProdVerdict catches both directions.

Rules

#ConditionSeverity
1has_paid_access = true but no active Entitlements grant in Stripehigh (migration gap)
2Active Entitlements grant but has_paid_access = falsemedium (stale grant or DB not flipped)
3Customer has duplicate grants for the same featuremedium (manual + subscription collision)
4has_paid_access = true but no stripe_customer_idhigh (blocks migration — needs backfill)

Rule 4 only runs when require_stripe_customer_id: true (default).

CLI

npx prodverdict check entitlements-migration
npx prodverdict check entitlements-migration --config ./prodverdict.yml
npx prodverdict check entitlements-migration --format json # pipe to backfill script

Example config

version: 1
contracts:
- type: entitlements-migration
database:
url_env: DATABASE_URL
users_table: users
columns:
id: id
stripe_customer_id: stripe_customer_id
has_paid_access: has_paid_access
plan: plan
entitlements:
secret_env: STRIPE_SECRET_KEY
require_stripe_customer_id: true
severity: high
fix: "Grant the entitlement in Stripe, then flip the DB flag post-migration."

Required Stripe permissions

Create a restricted key at https://dashboard.stripe.com/apikeys:

  • Customers → Read
  • Entitlements → Read

Migration playbook

See Entitlements migration guide for the full playbook.

Exit codes

CodeMeaning
0pass or warn (without --strict)
1fail (high severity)
2config / credential error