feat(card): inform users card runs on credit + flag USD FX abroad#2318
feat(card): inform users card runs on credit + flag USD FX abroad#2318kushagrasarathe wants to merge 5 commits into
Conversation
Two card-transparency asks from Jota: - TASK-19799: the Peanut card authorizes on the credit network, so a terminal that asks debit/credit needs 'credit' or the payment fails. Add a persistent InfoCard on the Your Card screen saying so. - TASK-19800: the card settles in USD, so a spend charged in another currency silently goes through an FX conversion. Add a drawer notice (CardForeignCurrencyNotice) mirroring LocalRailNudge, gated on a non-USD charge currency and suppressed in AR/BR where LocalRailNudge already carries the stronger 'pay a cheaper way' message.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR adds a foreign-currency notice for card spend receipts, wires it into transaction details with local-rail suppression, updates the local-rail nudge to use a shared country map and InfoCard, and adds a credit guidance card to the card screen. ChangesForeign currency receipt notice and local rail reuse
Estimated code review effort: 2 (Simple) | ~12 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
Code-analysis diffPainscore total: 5858.65 → 5869.39 (+10.74) 🆕 New findings (16)
✅ Resolved (13)
📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/components/TransactionDetails/provider-rows/LocalRailNudge.tsx (1)
20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider moving the shared country map to a dedicated module.
Exporting
LOCAL_RAIL_BY_COUNTRYsoCardForeignCurrencyNoticecan reach into it couples two independently-gated sibling components to one component's internal constant.LocalRailNudgealso gates onuseCardMarkupRate/percent, soCardForeignCurrencyNotice's reuse of this map (viaLOCAL_RAIL_BY_COUNTRY[iso2.toUpperCase()], seesrc/components/TransactionDetails/provider-rows/CardForeignCurrencyNotice.tsx:32) only checks country membership, not whetherLocalRailNudgeactually renders — a future third consumer of this map would compound the coupling. Extracting the map into a small shared constants file (e.g.local-rail-countries.ts) would make ownership explicit and avoid reaching into a sibling component's file for shared data.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/TransactionDetails/provider-rows/LocalRailNudge.tsx` at line 20, The shared country lookup is currently owned by LocalRailNudge but reused by CardForeignCurrencyNotice, which tightly couples sibling components through an exported constant. Move LOCAL_RAIL_BY_COUNTRY out of LocalRailNudge into a dedicated shared module and update both LocalRailNudge and CardForeignCurrencyNotice to import it from there, keeping the gating logic in each component separate from the shared data.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/components/TransactionDetails/provider-rows/LocalRailNudge.tsx`:
- Line 20: The shared country lookup is currently owned by LocalRailNudge but
reused by CardForeignCurrencyNotice, which tightly couples sibling components
through an exported constant. Move LOCAL_RAIL_BY_COUNTRY out of LocalRailNudge
into a dedicated shared module and update both LocalRailNudge and
CardForeignCurrencyNotice to import it from there, keeping the gating logic in
each component separate from the shared data.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4213b658-bb83-4a94-a7c1-1d5c096bfb12
📒 Files selected for processing (5)
src/components/Card/YourCardScreen.tsxsrc/components/TransactionDetails/TransactionDetailsReceipt.tsxsrc/components/TransactionDetails/__tests__/CardForeignCurrencyNotice.test.tsxsrc/components/TransactionDetails/provider-rows/CardForeignCurrencyNotice.tsxsrc/components/TransactionDetails/provider-rows/LocalRailNudge.tsx
Swap the hand-rolled Card+Icon in CardForeignCurrencyNotice for the standard InfoCard (variant=info), matching the credit note on the Your Card screen and every other info surface (KYC, Withdraw). Less code, consistent styling. Copy and gating unchanged.
Swap the hand-rolled Card+Icon in LocalRailNudge for the same InfoCard (variant=info) the FX notice and credit note use, so both drawer nudges render identically. Copy, gating, and the markup-rate logic unchanged.
|
@coderabbitai review in depth. Evaluate:
Also, write a very concise testing guide for QA |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -e
fd . -e tsx -e ts | grep -E "CardForeignCurrencyNotice|LocalRailNudge|YourCardScreen|TransactionDetailsReceipt|InfoCard"
echo "---- CardForeignCurrencyNotice.tsx ----"
cat -n src/components/TransactionDetails/provider-rows/CardForeignCurrencyNotice.tsx
echo "---- LocalRailNudge.tsx ----"
cat -n src/components/TransactionDetails/provider-rows/LocalRailNudge.tsx
echo "---- InfoCard ----"
fd -i infocard
</bash>Length of output: 7028 🏁 Script executed: #!/bin/bash
set -e
echo "---- InfoCard/index.tsx ----"
cat -n src/components/Global/InfoCard/index.tsx
echo "---- TransactionDetailsReceipt.tsx (relevant section) ----"
grep -n "LocalRailNudge\|CardForeignCurrencyNotice\|isPublic" src/components/TransactionDetails/TransactionDetailsReceipt.tsx
echo "---- full receipt context ----"
sed -n '1,200p' src/components/TransactionDetails/TransactionDetailsReceipt.tsx
echo "---- YourCardScreen.tsx context ----"
grep -n "InfoCard\|auto.renew\|credit" -i src/components/Card/YourCardScreen.tsx
echo "---- extractMerchantIso2 ----"
rg -n "extractMerchantIso2" -A 15 src/components/TransactionDetails/transaction-details.utils.ts
echo "---- test file ----"
cat -n src/components/TransactionDetails/__tests__/CardForeignCurrencyNotice.test.tsxLength of output: 20152 🏁 Script executed: #!/bin/bash
set -e
echo "---- CARD_FX_MARKUP_BY_CURRENCY ----"
rg -n "CARD_FX_MARKUP_BY_CURRENCY" -A 10 src/constants/payment.consts.ts
echo "---- useCardMarkupRate ----"
cat -n src/hooks/useCardMarkupRate.ts 2>/dev/null || fd useCardMarkupRate
echo "---- YourCardScreen.tsx full relevant block ----"
sed -n '1,120p' src/components/Card/YourCardScreen.tsx
echo "---- Icon list check for credit-card / info ----"
rg -n "'credit-card'|\"credit-card\"|'info'|\"info\"" src/components/Global/Icons/Icon.tsx | head -30
echo "---- check card.merchantCountry type / cardPayment type ----"
rg -n "merchantCountry|localCurrency" -B2 -A2 src/components/TransactionDetails/transactionTransformer.ts | head -80Length of output: 10204
Risk of new bugs / breaking existing functionality — Low
Performance impact / latency — Negligible
Code quality / DRY
Concise QA testing guide
✅ Action performedReview finished.
|
Addresses CodeRabbit: the map lived in LocalRailNudge and was reached into by CardForeignCurrencyNotice, coupling two sibling components through one component's exported internal. Move it to local-rail-countries.ts so the shared data has explicit ownership; both components import from there and keep their own gating.
Per Jota: the notice we want is NOT the local-currency spend (that's the good outcome) but the dynamic-currency-conversion trap — a spend abroad billed in USD because the terminal offered 'pay in dollars', which gives a worse rate than paying local. Fire only when the merchant billed USD (localCurrency === 'USD') AND the country is non-USD (small USD_COUNTRIES set), advising the user to pick local currency next time. Renamed CardForeignCurrencyNotice -> CardUsdAbroadNotice to match. AR/BR suppression unchanged (LocalRailNudge owns those).
|
Good catch — reworked to the DCC case. The notice now fires only when a spend abroad was billed in USD (the terminal's "pay in dollars?" trap) and the merchant is in a non-USD country, advising the user to pick the local currency next time. A spend billed in the local currency is the good outcome and shows nothing. Renamed |
TASK-19799 — card runs as credit
The Peanut card authorizes on the credit network. At a terminal that asks "debit or credit?", picking debit fails. Adds a persistent
InfoCardon the Your Card screen telling users to choose credit.src/components/Card/YourCardScreen.tsx— oneInfoCard(variantinfo) below the auto-renew banner.TASK-19800 — pick local currency abroad (DCC)
A card spend abroad can be billed in USD when the terminal offers "pay in dollars" (dynamic currency conversion). The terminal's markup is worse than Peanut's rate, so we nudge the user to choose the local currency next time. A spend already billed in the local currency is the good outcome — no notice.
CardUsdAbroadNotice.tsx— fires for acard_paybilled in USD (cardPayment.localCurrency === 'USD') whose merchant sits in a non-USD country (smallUSD_COUNTRIESset: US + territories + sovereign USD users like PA/EC/SV).LocalRailNudgealready carries the stronger "pay with the local rail" message — one nudge per receipt. ReusesLOCAL_RAIL_BY_COUNTRY, now extracted tolocal-rail-countries.ts(per CodeRabbit).LocalRailNudgeinTransactionDetailsReceipt.tsx, same!isPublicgate.localCurrency(the merchant's billing currency) already flows throughtransactionTransformer.Component consistency (drawer nudges)
Both drawer nudges render via the shared
InfoCard(variantinfo), matching the credit note:CardUsdAbroadNoticeusesInfoCard.LocalRailNudgemigrated from its hand-rolledCard+IcontoInfoCard— copy, gating, and markup-rate logic unchanged; visual parity only.Tests
CardUsdAbroadNotice.test.tsx(7 cases: USD-abroad fires; local-currency / domestic-USD / other-USD-country / unknown-country / AR-BR / non-card-spend all suppressed).LocalRailNudgesuite still green.npm run typecheckclean, prettier clean.QA (verified locally in the real UI with seeded card spends)