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
| # | Condition | Severity |
|---|---|---|
| 1 | has_paid_access = true but no active Entitlements grant in Stripe | high (migration gap) |
| 2 | Active Entitlements grant but has_paid_access = false | medium (stale grant or DB not flipped) |
| 3 | Customer has duplicate grants for the same feature | medium (manual + subscription collision) |
| 4 | has_paid_access = true but no stripe_customer_id | high (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→ ReadEntitlements→ Read
Migration playbook
See Entitlements migration guide for the full playbook.
Exit codes
| Code | Meaning |
|---|---|
| 0 | pass or warn (without --strict) |
| 1 | fail (high severity) |
| 2 | config / credential error |