Skip to content

fix(billing): resolve plan via priceId map, not price.metadata.planId (P0 — silent prod downgrade) #3742

Description

@PierreBrisorgueil

Problem

modules/billing/services/billing.webhook.service.js customer.subscription.updated handler calls resolvePlan(subscription) which reads price.metadata.planId. price.metadata.planId is empty in real Stripe webhook payloads — Stripe only populates metadata on the Product object, not on the Price object surfaced through the Subscription update event.

Result: every paid org's plan gets reset to `free` on every Stripe `customer.subscription.updated` event. Every devkit downstream running Stripe billing (pierreb, comes, montaine, ism) is silently affected in production today.

Trawl fixed this in comes-io/trawl_node#1250 by building a priceId → plan map from config.stripe.prices and using that map instead. This issue ports the fix upstream so every downstream benefits via /update-stack.

Fix

Port from trawl:

  1. buildPriceIdToPlanMap() — builds Map<priceId, planName> from config.stripe.prices at boot
  2. Rewritten resolvePlan(subscription) — looks up plan via priceId map first; falls back to price.metadata.planId for legacy test fixtures; final fallback free with warn log
  3. cancelAt/cancelAtPeriodEnd sync on customer.subscription.updated (writes to fields added in feat(billing): subscription cancelAt + cancelAtPeriodEnd lifecycle fields #3741)
  4. handleSubscriptionCreated safety-net handler — race-safe Dashboard/Payment Link subscription creation
  5. retryWithBackoff + isNonTransientStripeError — new lib files billing.retry.js + billing.stripe-errors.js (port verbatim from trawl)

Strip trawl-specific analytics.capture('subscription_changed') calls.

Labels

P0, billing, stripe

References

  • Alignment plan: 2026-05-30-trawl-devkit-perfect-alignment.md Task C.2
  • Trawl fix: comes-io/trawl_node#1250

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions