fix(billing): /sync Stripe expansion depth + observable error logging#3850
Merged
Conversation
Hotfix on top of #3829. The customer.retrieve expand 'subscriptions.data.items.data.price.product' is 6 levels deep and exceeds Stripe's 4-level expansion limit. /sync threw on every org with a Stripe customer and returned a generic "Failed to sync from Stripe" with no log line, so the regression was invisible. - Drop the deep expand on customers.retrieve (just confirms not deleted) - Fetch subscriptions separately via stripe.subscriptions.list with 'data.items.data.price.product' expand — only 4 levels deep from the subscriptions resource, within Stripe's limit - Log the actual error on the catch path Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The /sync endpoint now fetches subscriptions separately from customer.retrieve (see prior commit) so it can expand price.product 4 levels deep without exceeding Stripe's customer-retrieve depth limit. Update the test fixture to mock subscriptions.list with the same fake sub data. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| }); | ||
|
|
||
| const { FAKE_INVOICE, mockInvoicesList, mockCustomersRetrieve, mockProductsRetrieve } = mocks; | ||
| const { FAKE_INVOICE, mockInvoicesList, mockCustomersRetrieve, mockSubscriptionsList, mockProductsRetrieve } = mocks; |
| }); | ||
|
|
||
| const { FAKE_INVOICE, mockInvoicesList, mockCustomersRetrieve, mockProductsRetrieve } = mocks; | ||
| const { FAKE_INVOICE, mockInvoicesList, mockCustomersRetrieve, mockSubscriptionsList, mockProductsRetrieve } = mocks; |
| }); | ||
|
|
||
| const { FAKE_INVOICE, mockInvoicesList, mockCustomersRetrieve, mockProductsRetrieve } = mocks; | ||
| const { FAKE_INVOICE, mockInvoicesList, mockCustomersRetrieve, mockSubscriptionsList, mockProductsRetrieve } = mocks; |
5 tasks
bokelley
added a commit
that referenced
this pull request
May 2, 2026
…r-sub (#3853) Hotfix on top of #3850. The previous attempt expanded 'data.items.data.price.product' on subscriptions.list, which is 5 levels deep and still over Stripe's 4-level expansion limit. /sync kept failing. New approach: list subs without product expansion. The lookup_key fast path covers the modern cohort with zero extra round-trips. When no sub has a recognized lookup_key, fall back to retrieving the product per candidate via stripe.products.retrieve to check metadata.category= membership — Adzymic/Advertible/Bidcliq/Equativ-shape recovers through this path. New pickMembershipSubWithProductFetch async helper. Sync pickMembershipSub is unchanged for invariant callers that walk every sub on the account and can't afford per-sub round-trips. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Hotfix on top of #3829. I asked Stripe to expand
subscriptions.data.items.data.price.productoncustomers.retrieve, which is 6 levels deep and exceeds Stripe's 4-level expansion limit. Every /sync call against an org with a Stripe customer threw and returned a generic "Failed to sync from Stripe" with no log line — the regression was invisible.Caught after merging #3829 when re-running /sync against Adzymic returned
success: false.What changed
customers.retrieve— that call now just confirms the customer isn't deleted.stripe.subscriptions.list({ customer, expand: ['data.items.data.price.product'] }). That path is only 4 levels deep from the subscriptions resource, within Stripe's limit. The expansion is what makesisMembershipSub's metadata fallback work for founding-era prices.Test plan
npm run typecheckcleanstripe_subscription_id,subscription_price_lookup_key,membership_tierGET /api/admin/integrity/check/every-entitled-org-has-resolvable-tierto confirm Adzymic clears🤖 Generated with Claude Code