fix(subscriptions): read previous price from both webhook API shapes - #21122
Open
david1alvarez wants to merge 1 commit into
Open
fix(subscriptions): read previous price from both webhook API shapes#21122david1alvarez wants to merge 1 commit into
david1alvarez wants to merge 1 commit into
Conversation
Because: * The Stripe webhook endpoint's own api_version sets inbound payload shape, so the version flip has to be revertible without breaking parsing. * Basil removed Subscription.plan, which is what drives our upgrade and downgrade emails. This commit: * Reads the previous price from previous_attributes.plan, items.data[].plan or items.data[].price, and only when the price id moved. * Tags both Stripe webhook receivers with stripe_api_version. Addresses: PAY-3900
Contributor
There was a problem hiding this comment.
Pull request overview
Adds backward-compatible Stripe webhook handling during the API-version transition.
Changes:
- Normalizes previous plan/price webhook shapes.
- Prevents renewal events from triggering upgrade emails.
- Adds Stripe API-version telemetry and subscription tests.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
stripe-webhook.ts |
Tags legacy webhook telemetry with API version. |
stripe.ts |
Derives previous pricing across Stripe payload shapes. |
stripe.spec.ts |
Tests payload normalization and renewal filtering. |
stripe-webhooks.service.ts |
Tags NestJS webhook telemetry with API version. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+2448
to
+2449
| interval: planOldDiff.interval ?? planNew.interval, | ||
| interval_count: planOldDiff.interval_count ?? planNew.interval_count, |
| request.payload, | ||
| request.headers['stripe-signature'] | ||
| ); | ||
| Sentry.setTag('stripe_api_version', event.api_version); |
Comment on lines
+33
to
+36
| Sentry.setTag( | ||
| 'stripe_api_version', | ||
| webhookEventResponse.event.api_version | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Because
payloads don't match the shapes our code reads. We need to flip it and be able
to roll back.
Subscription.plan, which is the only trigger for upgrade anddowngrade emails — after the flip those emails would silently stop.
renewal, so treating any item change as a price change would email every
subscriber an upgrade notice each cycle.
This pull request
previous_attributes.plan(acacia) or thesubscription item's
plan/price(basil), normalizing a price into plan shape once.as anyonprevious_attributeswith type annotations.stripe_api_version.Issue that this pull request solves
Addresses: PAY-3900
Checklist
Put an
xin the boxes that applyHow to review (Optional)
planOldinextractSubscriptionUpdateEventDetailsForEmail.stripe.tsblock, then the four new cases instripe.spec.ts.planOldis the only thing that sends anupgrade/downgrade email, and the id comparison is what keeps renewals out of that path.
Screenshots (Optional)
Please attach the screenshots of the changes made in case of change in user interface.
Other information (Optional)
Firestore sync migration, and removing the dual-format code are still open — hence
Addresses:rather thanCloses:.mirror is written only from
stripe.*.retrieve, so its shape follows the SDK pin andthe flip can't pollute it. Steps 3 and 4 may be smaller than scoped.