Skip to content

TASK-22425 feat: enable send -> bank -> Brazil -> Pix key sends - #3055

Merged
abalinda merged 1 commit into
devfrom
feat/22425-send-bank-brazil-pix
Sep 8, 2026
Merged

abalinda merged 1 commit into
devfrom
feat/22425-send-bank-brazil-pix

Conversation

@abalinda

@abalinda abalinda commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Send → Send to friends → Bank showed Brazil disabled behind a "soon" badge: the country list's enforceSupportedCountries gate only accepted Bridge countries. The Pix-key send flow already pays any third-party Pix key (Manteca QR-payment endpoint, PixKeySendView), so the gate blocked a path the product supports. This PR enables Brazil in that list and routes it to method=pix.

  • CountryList.tsx: the send→bank gate now accepts Bridge countries plus Brazil. Argentina stays disabled — its Manteca rails in this flow are own-account offramps, same ruling that keeps Mercado Pago off the send list (feat: design system release #2813).
  • AddWithdrawRouterView.tsx: the send→bank Manteca branch routes Brazil to /withdraw/manteca?method=pix&country=brazil (delegates to PixKeySendView); bank-transfer stays for the rest.

Task

TASK-22425

Risks / breaking changes

  • Frontend-only routing/gating change; no backend or contract changes.
  • The Pix-key flow itself is untouched. The capability gate (canDo('pay', { provider: 'manteca' })) is enforced downstream in /qr-pay, same as the existing Brazil-geo entry points.
  • The send-origin ?method=bank marker is dropped on the Brazil hop, same as the existing Manteca bank-transfer hop; PixKeySendView's back navigation targets /send already.

QA

  • npm test — 524 suites green, incl. new tests:
    • CountryList.test.tsx: under enforceSupportedCountries, Brazil selectable, Argentina disabled, Bridge country (Germany) selectable.
    • AddWithdrawRouterView.test.tsx: send→bank Brazil click pushes /withdraw/manteca?method=pix&country=brazil.
  • Manual: /send → Bank → country list → Brazil → Pix-key entry → BR Code handoff to /qr-pay (sandbox).

Design notes / accepted trade-offs

  • The 'brazil' literal appears twice (the CountryList gate and the AddWithdrawRouterView route). The two sites encode different decisions — which countries the send→bank list offers vs which Manteca method a country routes to — so a shared constant would couple them without making either clearer. If a second Manteca send country lands, promote a map in manteca.consts then.
  • Smells: adds none beyond the above; reveals none. The stale comment in CountryList ("bridge or manteca supported countries" over bridge-only code) is replaced by an accurate one.

Screenshots

375×667, dev build with the withdraw fixture (API faked). Assets branch pr-assets-3055 is deleted post-merge.

Send → Bank country list Brazil — now selectable Argentina — stays "Soon" Brazil → Pix key entry
country list brazil enabled argentina soon pix key send

Click-through verified live: tapping Brazil lands on /withdraw/manteca?method=pix&country=brazil (the "Send with Pix" key-entry screen).

The send->bank country list gated on bridge support only, so Brazil sat
disabled behind a soon badge even though the PIX-key send flow (Manteca
QR-payment endpoint) already pays another person's key. Enable Brazil in
that list and route it to method=pix, which delegates to PixKeySendView.
bank-transfer stays the route for the other Manteca countries: it is the
own-account offramp. Argentina stays gated, same ruling that keeps
Mercado Pago off the send list (PR #2813).
@vercel

vercel Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
peanut-wallet Ready Ready Preview Sep 8, 2026 9:32pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: f589a107-a906-4723-9c50-df81ec52ec5f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@notion-workspace

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7297.09 → 7297.33 (+0.24)
Findings: +1 net (+7 new, -6 resolved)

🆕 New findings (7)

  • critical complexity — src/components/AddWithdraw/AddWithdrawRouterView.tsx — CC 99, MI 58.24, SLOC 278
  • critical complexity — src/components/Common/CountryList.tsx — CC 55, MI 59.9, SLOC 144
  • medium high-mdd — src/components/AddWithdraw/AddWithdrawRouterView.tsx:76 — AddWithdrawRouterView: MDD 118.5 (uses across many lines from declarations)
  • medium high-mdd — src/components/Common/CountryList.tsx:66 — CountryList: MDD 38.1 (uses across many lines from declarations)
  • medium high-mdd — src/components/Common/CountryList.tsx:188 — : MDD 35.4 (uses across many lines from declarations)
  • medium method-complexity — src/components/Common/CountryList.tsx:188 — CC 15 SLOC 34
  • low high-mdd — src/components/AddWithdraw/AddWithdrawRouterView.tsx:367 — : MDD 17.3 (uses across many lines from declarations)

✅ Resolved (6)

  • src/components/AddWithdraw/AddWithdrawRouterView.tsx — CC 98, MI 58.29, SLOC 277
  • src/components/Common/CountryList.tsx — CC 54, MI 59.92, SLOC 144
  • src/components/AddWithdraw/AddWithdrawRouterView.tsx:76 — AddWithdrawRouterView: MDD 119.8 (uses across many lines from declarations)
  • src/components/Common/CountryList.tsx:66 — CountryList: MDD 37.0 (uses across many lines from declarations)
  • src/components/Common/CountryList.tsx:188 — : MDD 32.9 (uses across many lines from declarations)
  • src/components/AddWithdraw/AddWithdrawRouterView.tsx:367 — : MDD 16.5 (uses across many lines from declarations)

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • ✅ unit: 6661 ran, 0 failed, 0 skipped, 2.2m

📊 Coverage (unit)

metric %
statements 76.5%
branches 63.0%
functions 70.7%
lines 77.5%
⏱ 10 slowest test cases
time test
🐢 9.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Network failure keeps loading while retries remain, then shows the generic error
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › User KYC not approved fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_SOURCE_OVER_MONTHLY_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_USER_NOT_PROVISIONED fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_RECENT_REFUND fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › routes the KYC rejection on its wire code, and does not retry it
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › a refused idempotency key tells the user to scan again, not to contact support
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_VOLUME_NEAR_CAP fails fast with copy that names the real cause
3.4s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
3.1s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Going offline blames the connection, and reconnecting clears it for the recovered scan
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@abalinda

abalinda commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

/chip review

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

🖼 Visual diff — 6 screens moved

8 of 68 shots changed · 60 identical · baseline 971450f → head 21b9c67

worst % screen widths
13.15% avatar-picker 320, 430
0.07% badges 320, 430
0.07% home 320
0.07% unverified 320
0.03% empty-accounts 430
0.03% identity-verification 430

job summary · before/after/diff images — artifact

Fixture screenshots, no backend. Advisory — this check never blocks a merge. Posted from the default branch by ds-shots-comment.yml; the report it renders is untrusted data.

@chip-peanut-bot chip-peanut-bot Bot 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.

Chip review — no blocking findings — this is not an approval

Brazil is enabled only in the send-to-bank country gate and is routed into the existing Pix-key payment flow; Argentina and unsupported countries remain disabled, and downstream payment eligibility is still enforced.

Findings

  • MAJOR · src/components/Common/CountryList.tsx:207 · [claude-opus] Send→bank→Brazil promises third-party PIX-by-key sends that product truth says we do not offer
    What changes for users: CountryList.tsx:207 makes Brazil selectable in the send→bank country list (enforceSupportedCountries is set only for isBankFromSend, AddWithdrawRouterView.tsx:366), and AddWithdrawRouterView.tsx:386 routes it to method=pix, which app/(mobile-ui)/withdraw/manteca/page.tsx:95 delegates to PixKeySendView — an input for any person's PIX key, handed to /qr-pay. So the app now advertises Brazil as a 'send to a bank' destination and accepts an arbitrary third party's chave PIX.

Product truth disagrees on three counts:

  • product/countries.md Brazil entry: third_party_payout: false, note "First-party only, same rule as Argentina above" (Argentina's note: "Third-party payouts exist on the Bridge rails only (EU, US, MX, UK)"). Brazil is now a fourth, non-Bridge, third-party payout destination.
  • product/spending.md:38 and :243: "Pix QR scan for merchant payments. To send funds to a person, use a Peanut Link" / "To send money to someone in Brazil, share a Peanut Link." The new entry point is exactly send-to-a-person-by-PIX-key.
  • product/feedback/problems/pix-direct-payment-broken.md decision log: "2026-04 · disabled by compliance, no date to re-enable", plus the 2026-07-04 note "PIX by cellphone/CPF/email/CNPJ temporarily DISABLED". Nothing in product/ records that ruling being reversed.

Audience also widens. The pre-existing PIX-key entry point (SendRouter.view.tsx:101) is shown only to users geolocated in Brazil (useGeoFilteredPaymentOptions.ts:48). The country list applies no residence filter, so this path reaches every user; the only downstream gate is capability-based (canDo('pay', { provider: 'manteca' })). Product does sanction geo-agnostic QR pay ("Any account verified with Manteca can pay Pix QR codes, regardless of residence"), but the same file records the opposite for keys: "non-Brazilians can only pay PIX by scanning a merchant QR, not by direct transfer to a PIX key."

Which is wrong: I can't confirm from the repos that the 2026-04 compliance disable was lifted. If TASK-22425 carries that sign-off, product/ is the stale side and must be updated before this ships — countries.md (third_party_payout carve-out for the QR-rail PIX-key send), spending.md:38/243, and the pix-direct-payment-broken decision log — otherwise support answers generated from product/ will keep telling Brazilian users "own account only / use a Peanut Link" while the app offers the opposite. If the disable still stands, the gate change is the wrong side and Brazil should stay off the send→bank list.

Checked clean

  • Pinned detached checkout, merge base, trusted author, dev base, and exact PR head all match the supplied values.
  • CountryList enables Brazil only when the send-to-bank supported-country gate is active; Argentina remains disabled and Bridge countries remain selectable, with focused regression coverage.
  • Brazil selection routes to /withdraw/manteca?method=pix&country=brazil, which delegates to PixKeySendView and the shared Pix-key-to-BR-Code handoff.
  • The destination /qr-pay flow independently enforces the Manteca pay capability and region restriction before payment initialization, so this UI gate change does not bypass payment authorization.
  • The live Lexicon identifies Pix as Brazil's local payment rail, and TASK-22425's binding scope and definition of done match the implemented Brazil-only routing change.
  • All exact-head CI checks completed successfully or were intentionally skipped, including unit, typecheck, lint, format, native export, design-system checks, preview deployment, provenance, and advisory screenshots. Local focused tests were unavailable because the detached checkout has no installed dependencies.

Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.

Third opinion by claude-opus: 1 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 21b9c6739c62 · Context: repo, product · Took 14m

@abalinda

abalinda commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Re the claude-opus MAJOR (product truth vs PIX-by-key sends):

Product truth is the stale side here. Evidence:

  • The PIX-key send this PR exposes does not ride the compliance-disabled direct-transfer/offramp rail. It rides the QR-payment rail: commit 5f3ea8b (2026-06-15, "route Brazil PIX sends through the QR-payment endpoint") wraps the key into a BR Code and hands off to /qr-pay, gated by canDo('pay', { provider: 'manteca' }). That commit shipped precisely because pay-capable users "can already pay any PIX key by pasting it into the QR scanner".
  • The same PixKeySendView is live on prod today via /send → Pix for Brazil-geo users, and the QR scanner's pasted-key path is live for everyone with the Manteca pay capability. This PR widens a menu, not a capability; the downstream gate is unchanged.
  • The pix-direct-payment-broken decision log (2026-04) scopes itself: "the open problem is the in-product communication, not the capability itself". Nothing here re-enables PIX-by-CPF as a bank-account identifier on the withdraw rail.

The stale product/ + content/ pages (countries.md third_party_payout, spending.md, quick-ref.md, pay-with/pix, help/withdraw-bank, send-to/brazil, countries/brazil, and the spending-methods/pix.md generation input) are catalogued and go through a separate update-content follow-up once this ships — content and code never travel together. Filed in the PR readiness report.

@abalinda
abalinda marked this pull request as ready for review September 8, 2026 21:50
@abalinda
abalinda requested review from kushagrasarathe and a lite review from Copilot September 8, 2026 21:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chip-peanut-bot chip-peanut-bot Bot 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.

Chip review — no blocking findings — this is not an approval

The routing is internally consistent and CI is green, but enabling Brazil for every send-to-bank user conflicts with the current product and compliance rules for direct Pix-key transfers.

Findings

  • MAJOR · src/components/Common/CountryList.tsx:207 · Brazil Pix-key sends bypass the documented audience restriction
    enforceSupportedCountries is used by the send-to-bank flow without a residence filter, so this line makes Brazil selectable for a non-Brazilian user and the new route sends their arbitrary Pix key through /qr-pay. Current product truth distinguishes geo-agnostic merchant QR payments from direct key transfers: product/countries.md marks Brazil third-party payouts as unsupported, product/spending.md directs person-to-person sends to Peanut Links, and product/feedback/problems/pix-direct-payment-broken.md records Pix-by-key as compliance-disabled. Keep Brazil gated unless that decision has been reversed; if TASK-22425 carries approval, update those canonical rules and encode the approved audience before shipping so the app and support guidance agree.

  • MINOR · src/components/Common/CountryList.tsx:207 · [claude-opus] Third-party Brazil Pix sends contradict product truth (Bridge-only third-party payouts)
    This PR makes Brazil selectable in the send→bank country list and routes it to method=pix → PixKeySendView → /qr-pay, i.e. paying another person's Pix key from the Peanut balance. Product truth says the opposite in three places:

  • product/countries.md (brazil): third_party_payout: false, note "First-party only, same rule as Argentina above"; the Argentina note it inherits says "Third-party payouts exist on the Bridge rails only (EU, US, MX, UK)".

  • product/networks.md: direct_bank_transfers.regions: [united-states, europe, mexico, united-kingdom], and §"Direct Bank Transfers (Third-Party)" — "available in the EU, US, Mexico, and the UK via Bridge".

  • product/spending.md:38 and :243: "Pix QR scan for merchant payments. To send funds to a person, use a Peanut Link" / "To send money to someone in Brazil, share a Peanut Link".

The code is what's right here — the PR title states this is the intended capability, and the QR rail genuinely is geo-agnostic and pool-settled, so the flow is coherent. Product/ is what's now stale. The cost of leaving it is customer-facing: support answers and generated pages are produced from these files, so they will tell a Brazilian user to use a Peanut Link for a person-to-person Pix send that the app now offers directly, and networks.md's third-party region list will omit Brazil.

Fix: update product/countries.md (brazil — distinguish the first-party PIX_BR offramp rail from third-party Pix-key sends over the QR/pool rail), product/networks.md (the third-party send section and regions), and product/spending.md:38,243 via the update-content path. Note the split explicitly, since the same file's Argentina entry stays first-party-only and the PR deliberately keeps Argentina gated.

Checked clean

  • Pinned detached checkout, merge base, trusted author, dev base, and exact PR head all match the supplied values.
  • Brazil selection routes to /withdraw/manteca?method=pix&country=brazil, which delegates to PixKeySendView and the shared Pix-key-to-BR-Code handoff.
  • The destination /qr-pay flow retains its existing Manteca pay-capability and region-restriction gates; no API contract or payment implementation changed.
  • The existing direct Pix entry is geo-filtered to Brazil, while the changed send-to-bank country list has no equivalent residence filter and therefore widens the audience.
  • TASK-22425 requests the widened flow, but the current canonical country, spending, and Pix compliance records do not record a reversal of the direct-key restriction.
  • All exact-head CI checks completed successfully or were intentionally skipped, including unit, typecheck, lint, format, native export, design-system checks, preview deployment, provenance, and advisory screenshots. Local focused tests were unavailable because the detached checkout has no installed dependencies.

Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.

Third opinion by claude-opus: 1 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 21b9c6739c62 · Context: repo, product · Took 8m

Comment thread src/components/Common/CountryList.tsx
@abalinda
abalinda merged commit 01d502a into dev Sep 8, 2026
30 checks passed

This branch was successfully deployed

1 active deployment
Preview — 21b9c673 Deployed Sep 8, 2026 by vercel[bot]
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