I-033 — Airwallex merchant enrichment silently degraded¶
Symptom¶
Card purchases on the Airwallex transaction list show the raw fallback label "ISSUING_CAPTURE (CARD_PURCHASE)" instead of the enriched description ("Purchase at Google Cloud, SGP, (Company Card, ****4687)") they used to show.
Root cause (proven from production runtime logs, 2026-07-15 traffic)¶
Airwallex's wallet feed (financial_transactions) carries no merchant info
(description: null on card rows). Enrichment comes from a second call to the
Issuing API (/api/v1/issuing/transactions), matched by date+amount+currency —
both in the Bank page (/api/airwallex/card-transactions → BankDashboard)
and at accounting-import time (/api/airwallex/sync). An unmatched card row
silently falls back to the raw type labels.
Three facts combined into a time bomb:
1. /api/airwallex/card-transactions fetched only page 0 (100 rows) of the
2-year window — prod logs show count: 100, hasMore: true.
2. The account now has 130 card transactions (the sync's single 200-row page
still got all of them — itemCount: 130, hasMore: false).
3. Airwallex returns issuing transactions OLDEST-first — provable from the
first row's card_transaction_lifecycle_id, a UUIDv7 whose embedded
timestamp decodes to 2025-01-24 (the account's earliest card era).
So page 0 = the oldest 100; the newest ~30 card transactions were invisible to the matcher, and every recent purchase lost its merchant name. It "used to work" because it armed itself the day the card-tx count crossed 100. The credentials were fine and the code unchanged since it worked (verified). The accounting-sync path still enriched correctly (130 < its 200 page size) but had the same class of bug one threshold later.
Fix¶
Paginate the full window everywhere (same class of fix as T-149 for OCBC):
- lib/airwallex/client.ts: new getAllFinancialTransactions() (raw rows, all
pages); getAllCardTransactions() already existed but was unused by these paths.
- pages/api/airwallex/card-transactions.ts: fetches ALL pages via
getAllCardTransactions (was single page-0 call); hasMore now always false.
- pages/api/airwallex/sync.ts: both fetches now use the paginating helpers
(was single 200-row pages — would truncate at >200 rows).
Display-only residual (not fixed here): the Bank page transaction list itself
(/api/airwallex/transactions?pageSize=100) still shows only the newest 100
financial rows — cosmetic pagination, separate from enrichment.
Verification¶
Heavy tsc --noEmit clean. Live check is owner-gated (needs prod creds +
deploy): after deploying, recent card purchases on Bank Access > Airwallex
should regain merchant names. NOTE: merged ≠ deployed — auto-deploy is off.
Decision log¶
2026-07-16 — ✅ Read AGENTS.md (incl. the 2026-07-05 deployment guardrail). Diagnosed from production runtime logs via the Vercel API (no code had changed; proved the truncation + oldest-first ordering). Ticket-only fix on the Airwallex fetch layer; owner approved shipping ("Yes" to the pagination fix, 2026-07-16). Source: Bank Access (Diagnosticcs) · https://claude.ai/code/session_01S4ztAsDZfR97qXjhPdwLn4