-
Notifications
You must be signed in to change notification settings - Fork 6
Open
Description
When a user upgrades or downgrades their subscription (to a different product, not just adding/removing seats) via the Customer Portal, the customer.subscription.updated webhook fires and handleSubscriptionUpdated runs successfully, but the priceId field on the subscription record is not updated.
This breaks any logic that relies on priceId to determine the user's current plan tier.
Steps to Reproduce
- Create a subscription with
priceId: "price_pro_monthly" - Open Customer Portal via
createCustomerPortalSession() - Upgrade to a different plan (e.g., Team)
- Stripe fires
customer.subscription.updatedwebhook - Check the subscription record in Convex
Expected: priceId is updated to "price_team_monthly"
Actual: priceId is still "price_pro_monthly"
Root Cause
In index.ts, the customer.subscription.created handler passes priceId:
case "customer.subscription.created": {
await ctx.runMutation(component.private.handleSubscriptionCreated, {
// ...
priceId: subscription.items.data[0]?.price.id || "", // ✅ Included
});
}But `customer.subscription.updated` does not:
case "customer.subscription.updated": {
await ctx.runMutation(component.private.handleSubscriptionUpdated, {
stripeSubscriptionId: subscription.id,
status: subscription.status,
currentPeriodEnd: subscription.items.data[0]?.current_period_end || 0,
cancelAtPeriodEnd: subscription.cancel_at_period_end ?? false,
quantity: subscription.items.data[0]?.quantity ?? 1,
metadata: subscription.metadata || {},
// ❌ priceId NOT passed
});
}
Additionally, the handleSubscriptionUpdated mutation in private.ts doesn't accept priceId in its args.
Suggested Fix
index.ts- Add priceId to the updated handler:
case "customer.subscription.updated": {
await ctx.runMutation(component.private.handleSubscriptionUpdated, {
// ... existing fields ...
priceId: subscription.items.data[0]?.price.id, // Add this
});
}2. **`private.ts`** - Accept and save priceId in the mutation:
handleSubscriptionUpdated: mutation({
args: {
// ... existing args ...
priceId: v.optional(v.string()), // Add this
},
handler: async (ctx, args) => {
// ... in db.patch() ...
...(args.priceId && { priceId: args.priceId }),
},
});
Environment
@convex-dev/stripeversion: latest (- Stripe API version: 2025-11-17.clover
AtAFork and nizarlj
Metadata
Metadata
Assignees
Labels
No labels