Skip to content

fix(apple-iap): fall back to cached IAP records on App Store failures - #20965

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

fix(apple-iap): fall back to cached IAP records on App Store failures#20965
david1alvarez wants to merge 1 commit into
mainfrom
PAY-3841

Conversation

@david1alvarez

Copy link
Copy Markdown
Contributor

Because:

  • The App Store Server API intermittently fails, and app-store-server-api surfaces those as an AppStoreError carrying one of Apple's 5xxxxxx codes.
  • AppleIapPurchaseManager.getForUser rethrew everything but its not-found cases, so an Apple outage failed EligibilityService.checkEligibility and took cart setup and checkout down with it.
  • Treating a purchase we cannot verify as absent is worse than erroring: a customer with an active Apple subscription could buy an overlapping web subscription.

This commit:

  • Adds AppleIapServiceUnavailableError, and maps Apple's server-range error codes plus the library's isRetryable and isRateLimitExceeded flags to it in AppleIapClient.convertError.
  • Adds AppleIapPurchaseManager.getStaleCachedForUser, which reads a customer's purchase records from Firestore without refreshing them against Apple.
  • Falls back to those records in EligibilityService.checkEligibility, so a purchase we cannot verify still blocks an overlapping web purchase.
  • Exports the Apple IAP error module from the payments-iap barrel, and passes the logger to EligibilityService in initSubplat.
  • Leaves the billing endpoint and the subscription management page erroring, so a customer sees a failure rather than an empty subscription list.

Closes PAY-3841

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.

Because:

* The App Store Server API intermittently fails, and app-store-server-api
  surfaces those as an AppStoreError carrying one of Apple's 5xxxxxx codes.
* AppleIapPurchaseManager.getForUser rethrew everything but its not-found
  cases, so an Apple outage failed EligibilityService.checkEligibility and
  took cart setup and checkout down with it.
* Treating a purchase we cannot verify as absent is worse than erroring: a
  customer with an active Apple subscription could buy an overlapping web
  subscription.

This commit:

* Adds AppleIapServiceUnavailableError, and maps Apple's server-range error
  codes plus the library's isRetryable and isRateLimitExceeded flags to it in
  AppleIapClient.convertError.
* Adds AppleIapPurchaseManager.getStaleCachedForUser, which reads a customer's
  purchase records from Firestore without refreshing them against Apple.
* Falls back to those records in EligibilityService.checkEligibility, so a
  purchase we cannot verify still blocks an overlapping web purchase.
* Exports the Apple IAP error module from the payments-iap barrel, and passes
  the logger to EligibilityService in initSubplat.
* Leaves the billing endpoint and the subscription management page erroring, so
  a customer sees a failure rather than an empty subscription list.

Closes PAY-3841
Copilot AI review requested due to automatic review settings August 4, 2026 21:07
@david1alvarez
david1alvarez requested a review from a team as a code owner August 4, 2026 21:07

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 improves resiliency of Apple IAP eligibility checks during intermittent App Store Server API outages by classifying Apple-side failures as a distinct error and falling back to cached Firestore purchase records for overlap protection (so an unverified Apple subscription doesn’t appear “absent” and allow an overlapping web purchase).

Changes:

  • Introduces AppleIapServiceUnavailableError and maps Apple server-range error codes (5xxxxxx) plus app-store-server-api retry/rate-limit signals to it in AppleIapClient.
  • Adds AppleIapPurchaseManager.getStaleCachedForUser to read cached Apple purchase records from Firestore without calling Apple.
  • Updates EligibilityService.checkEligibility to fall back to cached Apple purchases when Apple is unavailable, and wires a logger through initSubplat; adds unit test coverage for the new behaviors.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/fxa-auth-server/lib/payments/initSubplat.ts Passes a logger into EligibilityService initialization to support new warning logging on Apple outages.
libs/payments/iap/src/lib/apple/apple-iap.error.ts Adds AppleIapServiceUnavailableError to represent Apple-side/transient failures with structured info.
libs/payments/iap/src/lib/apple/apple-iap.client.ts Detects Apple-side/transient App Store errors and converts them to AppleIapServiceUnavailableError.
libs/payments/iap/src/lib/apple/apple-iap.client.spec.ts Adds tests ensuring retryable/server/rate-limit App Store errors map to AppleIapServiceUnavailableError and non-retryable client errors do not.
libs/payments/iap/src/lib/apple/apple-iap-purchase.manager.ts Adds getStaleCachedForUser to return Firestore-cached purchases without contacting Apple.
libs/payments/iap/src/lib/apple/apple-iap-purchase.manager.spec.ts Adds tests for getStaleCachedForUser and verifies AppleIapServiceUnavailableError is propagated to callers.
libs/payments/iap/src/index.ts Exports Apple IAP error types from the library barrel for cross-package consumption.
libs/payments/eligibility/src/lib/eligibility.service.ts Falls back to cached Apple purchases when Apple is unavailable (and logs a warning), preventing overlapping web purchases from slipping through.
libs/payments/eligibility/src/lib/eligibility.service.spec.ts Adds tests covering the fallback-to-cache behavior and confirming non-retryable Apple errors are rethrown.

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