Skip to content

fix(topup): send Fygaro custom_reference so card top-ups are attributable - #685

Merged
islandbitcoin merged 4 commits into
mainfrom
feat/fygaro-topup-reference
Aug 8, 2026
Merged

fix(topup): send Fygaro custom_reference so card top-ups are attributable#685
islandbitcoin merged 4 commits into
mainfrom
feat/fygaro-topup-reference

Conversation

@islandbitcoin

@islandbitcoin islandbitcoin commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Why

Fygaro's checkout ignores the client_reference query parameter — verified against Fygaro's production JS bundles, which read only custom_reference, external_reference, and amount from the URL (their helpdesk doc advertising client_reference is stale). Every card top-up made through the app has therefore arrived with a blank reference in Fygaro's dashboard, forcing manual PayPal-email archaeology to figure out who to credit (most recently a $10 top-up on Aug 7 that had to be traced through Kratos/Mongo by buyer email).

What

  • Rename client_referencecustom_reference in the Fygaro payment URL and URL-encode the username (CardPayment.tsx)
  • Build the payment URL only after the username resolves; drop the || "user" fallback that could produce an unattributable charge. If the account query settles with no username, show the error screen instead of loading the WebView
  • Record the target wallet in client_note (wallet:USD) — recorded on the Fygaro payment for ops/audit (informational; the backend, feat(fygaro): payment webhook — record, notify, and flag-gated auto-credit flash#472, credits the USD cash wallet regardless)
  • Restrict card top-ups to the USD wallet: the BTC option is no longer offered for card payments (still offered for bridge transfers in advance mode), with a notice shown; TopupDetails pins wallet: "USD" on the CardPayment navigation
  • New i18n string TopupDetails.usdOnlyNotice (translations regenerated)

Tests

New unit specs (this flow previously had zero coverage):

  • CardPayment.test.tsx — URL uses custom_reference (and never client_reference), username URL-encoding, client_note wallet, WebView withheld while the username loads, error screen when no username exists, success-URL navigation carries amount + wallet
  • TopupDetails.test.tsx — USD-only for card even in advance mode (BTC still offered for bridge), continue always navigates with wallet: "USD"

yarn tsc:check clean, yarn test full suite green, eslint clean on touched files (remaining repo lint errors are pre-existing in files this PR doesn't touch).

🤖 Generated with Claude Code

https://claude.ai/code/session_01NcEjF6SS3Ci5D4CzZqdPjb

…able

Fygaro's checkout ignores the client_reference query parameter (verified
against its production JS bundles) — the parameter it records is
custom_reference. Every card top-up made through the app has therefore
arrived with a blank reference, making manual crediting a guessing game.

- rename client_reference -> custom_reference and URL-encode the username
- build the payment URL only after the username resolves; drop the
  `|| "user"` fallback that could produce an unattributable charge, and
  show the error screen if no username exists
- record the target wallet in client_note for the future webhook
- restrict card top-ups to the USD wallet (BTC option no longer offered
  for card payments; notice shown instead)
- add unit tests for URL construction, username gating, and the USD-only
  wallet restriction

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NcEjF6SS3Ci5D4CzZqdPjb
Dread and others added 3 commits August 7, 2026 23:57
…curacy

- handleNavigationStateChange now ignores the payment-button URL itself:
  it embeds the user-controlled username (custom_reference), so a
  username containing "success"/"failed"/"error"/"cancelled" could fake
  a payment outcome the moment the page loads (false success screen or
  an instant failure alert). The button path is a shared constant so the
  URL and the guard can't drift apart. Predates this PR but this flow
  owns the line that feeds it.
- Retry on the no-username error screen now refetches the account query
  (cache-first never re-asks the server, and there is no WebView to
  reload) instead of dead-ending on the same error forever.
- client_note comment corrected: it is recorded on the Fygaro payment
  for ops/audit; the backend credits the USD cash wallet regardless.
- tests: trigger-word username never fires a navigation callback off our
  own URL; Retry invokes refetch when the username never resolved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NcEjF6SS3Ci5D4CzZqdPjb
…ring guard

The previous fix guarded out any URL containing the payment-button path —
but Fygaro's checkout returns to the payment-button URL itself with
?success=0|1 after external-processor flows (PayPal), so the guard
swallowed the real success/failure redirect: users would pay and never
see the success screen.

Outcome detection now parses the URL: the ?success=1|0 query param is the
explicit signal (read even on the /pb/ path), and keyword matching is
restricted to host+path so the user-controlled username in the query
string (custom_reference) still can't fake an outcome.

Tests now use the real return shape (/pb/<id>/?success=1 and ?success=0)
alongside the trigger-word-username no-op case, closing the gap that let
the regression through.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NcEjF6SS3Ci5D4CzZqdPjb
check:translation-drift requires every en.json key in all 23 locales;
English placeholder text per the existing convention for new keys.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NcEjF6SS3Ci5D4CzZqdPjb
@islandbitcoin islandbitcoin reopened this Aug 8, 2026
@islandbitcoin
islandbitcoin merged commit 45e10e3 into main Aug 8, 2026
19 checks passed
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.

1 participant