Skip to content

fix(payments): expand discount coupons on cached invoices and subscriptions - #21008

Open
david1alvarez wants to merge 1 commit into
mainfrom
PAY-3893
Open

fix(payments): expand discount coupons on cached invoices and subscriptions#21008
david1alvarez wants to merge 1 commit into
mainfrom
PAY-3893

Conversation

@david1alvarez

Copy link
Copy Markdown
Contributor

Because:

  • The SDK 22 upgrade rewrote discount reads to discounts[0].source.coupon without requesting the expansion, so a cached invoice holding a discount id threw Cannot read properties of undefined (reading 'coupon') on /v1/account.
  • Expanding discounts does not expand the coupon under it, so reads asking only for discounts dropped promotion details.

This commit:

  • Requests discounts.source.coupon on the reads and Firestore writes that asked only for discounts.
  • Adds discountsNeedExpansion, and re-fetches a discount that arrives as an id rather than dropping it.
  • Reports the discount amount with null coupon fields when a coupon is still unreadable.
  • Types source.coupon as expanded or null, never an id.

Closes PAY-3893

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).
  • I have manually reviewed all AI generated code.

Copilot AI lite review requested due to automatic review settings August 11, 2026 23:33
@david1alvarez
david1alvarez requested a review from a team as a code owner August 11, 2026 23:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Stripe discount/coupon handling after the SDK 22 upgrade by consistently expanding discounts.source.coupon on reads/cached objects and by hardening downstream formatting/DTO code paths so unexpanded discounts don’t crash /v1/account.

Changes:

  • Expand discounts.source.coupon in Stripe reads and Firestore invoice fetch/write paths (and related preview invoice calls).
  • Add logic to detect when discounts/coupons are not expanded and re-fetch from Stripe rather than dropping promotion details.
  • Make formatter/DTO consumers resilient to unexpanded discount/coupon fields by returning null coupon fields while preserving the discount amount.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/fxa-shared/payments/stripe.ts Adds discountsNeedExpansion and uses it to decide when cached invoices need a Stripe re-fetch with discounts.source.coupon expanded.
packages/fxa-shared/payments/stripe-firestore.ts Expands discounts.source.coupon when fetching invoices to insert into Firestore.
packages/fxa-auth-server/lib/payments/stripe.ts Ensures invoice/subscription discount coupon expansion and adds subscription re-fetch path when discount data is unexpanded.
packages/fxa-auth-server/lib/payments/stripe.spec.ts Updates expectations for expanded fields and adds test coverage for re-fetch behavior when cached discounts/coupons are unexpanded.
packages/fxa-auth-server/lib/payments/stripe-formatter.ts Makes invoice preview discount formatting tolerant of unexpanded discount/coupon fields while keeping the amount.
packages/fxa-auth-server/lib/payments/stripe-formatter.spec.ts Adds tests for preserving discount amount when discount or coupon is an unexpanded id.
packages/fxa-auth-server/lib/payments/stripe-firestore.spec.ts Updates invoice retrieve expectations to include discounts.source.coupon expansion.
libs/payments/stripe/src/lib/stripe.client.types.ts Updates StripeDiscount typing so source.coupon is expanded or null (never an id).
libs/payments/stripe/src/lib/stripe.client.ts Updates invoice retrieve/preview calls to expand discounts.source.coupon.
libs/payments/stripe/src/lib/stripe.client.spec.ts Updates tests to expect discounts.source.coupon expansion and adds preview expansion coverage.
libs/payments/metrics-aggregator/src/lib/payments-metrics-aggregator.service.ts Makes coupon id access nullable-safe (coupon?.id).
libs/payments/management/src/lib/subscriptionManagement.service.ts Makes promotion name access nullable-safe (coupon?.name).
libs/payments/customer/src/lib/util/stripeInvoiceToFirstInvoicePreviewDTO.ts Makes discount coupon duration/name access nullable-safe (coupon?.duration / coupon?.name).
libs/payments/customer/src/lib/subscription.manager.ts Makes coupon id comparisons nullable-safe (coupon?.id).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/fxa-shared/payments/stripe.ts
Comment on lines 621 to 630
const invoice = await this.stripeFirestore.retrieveInvoice(resource);
if (!discountsNeedExpansion(invoice.discounts)) {
// @ts-ignore
return invoice;
}
// @ts-ignore
return invoice;
return this.stripe.invoices.retrieve(resource, {
expand: ['discounts.source.coupon'],
});
} catch (err) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current plan is for this to live in a followup ticket (to be filed, pending review)

…ptions

Because:
* The SDK 22 upgrade rewrote discount reads to `discounts[0].source.coupon`
  without requesting the expansion, so a cached invoice holding a discount id
  threw `Cannot read properties of undefined (reading 'coupon')` on
  `/v1/account`.
* Expanding `discounts` does not expand the coupon under it, so reads asking
  only for `discounts` dropped promotion details.

This commit:
* Requests `discounts.source.coupon` on the reads and Firestore writes that
  asked only for `discounts`.
* Adds discountsNeedExpansion, and re-fetches a discount that arrives as an id
  rather than dropping it.
* Reports the discount amount with null coupon fields when a coupon is still
  unreadable.
* Types `source.coupon` as expanded or null, never an id.

Closes PAY-3893
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants