Skip to content

feat(topup): show net "you'll receive" and enforce $10 minimum on card top-ups - #688

Merged
islandbitcoin merged 2 commits into
mainfrom
feat/topup-fee-disclosure
Aug 11, 2026
Merged

feat(topup): show net "you'll receive" and enforce $10 minimum on card top-ups#688
islandbitcoin merged 2 commits into
mainfrom
feat/topup-fee-disclosure

Conversation

@islandbitcoin

Copy link
Copy Markdown
Contributor

What & why

Fast-follow to the merged backend fee feature (lnflash/flash#473), which added a nullable Globals.fygaroTopup field carrying the Fygaro processor + Flash fee params and the top-up minimum. This wires the app to consume them so a card top-up is transparent about fees and enforces a real minimum.

Two user-facing changes on the card top-up amount screen (TopupDetails, paymentType: "card"):

  1. Fee disclosure — "You'll receive $X.XX" (net after fees) is shown live as the user types, computed client-side from the fee params. Reuses the visual/interaction pattern of the cashout settlement-amount preview shipped in feat(cashout): show the real settlement amount on the cashout entry screen #683.
  2. $10 minimum — the old >= $1.00 card check is replaced with fygaroTopup.minimumAmount, and the invalid-amount alert now states the real minimum.

Net is computed as:

processorFee = gross * processorFeePercent/100 + processorFeeFixed
flashFee     = gross * flashFeePercent/100     + flashFeeFixed
net          = max(0, gross - processorFee - flashFee)

e.g. $10 gross with 2.99% + $0.49 processor and 2% flash → $9.01.

Null-degradation

fygaroTopup is null when the instance's Fygaro settings are unavailable. The app handles that gracefully:

  • the "You'll receive" line is hidden (never shows a guessed/wrong number), and
  • the minimum falls back to $10 rather than silently dropping to the old $1.

Both are card-only — bank-transfer / bridge flows keep the existing $1 floor and show no net line.

GraphQL field consumed

Added fygaroTopup { minimumAmount processorFeePercent processorFeeFixed flashFeePercent flashFeeFixed } to the existing transferFlags globals query (the one that already surfaces bridgeEnabled, fetched by the topup/cashout entry screen — so the data is cache-warm on arrival to TopupDetails).

Since this worktree has no backend access to re-introspect, the FygaroTopupInfo type and the fygaroTopup: FygaroTopupInfo field were hand-added to the committed app/graphql/public-schema.graphql to match flash#473, then codegen was regenerated (yarn dev:codegen) — generated.ts / generated.gql are in the diff. graphql-inspector validates clean.

i18n

New keys (TopupDetails.youllReceive, TopupDetails.feeNote, and a parameterized TopupDetails.minimumAmount) added to app/i18n/en/index.ts and to all 23 locale files under raw-i18n/translations/ (English placeholder, per convention). yarn update-translations and check:translation-drift both clean.

Tests

app/screens/topup-cashout-flow/__tests__/TopupDetails.test.tsx extended (globals hook mocked the way CardPayment mocks its query hook):

  • $5 card amount is rejected with the real-minimum alert (incl. the null-fallback path)
  • $10 gross renders "You'll receive $9.01"
  • net line hidden when fygaroTopup is null and for bank transfers
  • pure estimateTopupNet unit tests (sample + negative clamp)

yarn check-code (tsc + translations + codegen + graphql-check) passes; yarn test for the flow is green (23/23).

🤖 Generated with Claude Code

https://claude.ai/code/session_01NcEjF6SS3Ci5D4CzZqdPjb

Dread and others added 2 commits August 10, 2026 20:35
…d top-ups

Fast-follow to the merged backend fee feature (lnflash/flash#473), which exposes
Globals.fygaroTopup with the processor/Flash fee params and the top-up minimum.

- Add `fygaroTopup { minimumAmount processorFeePercent processorFeeFixed
  flashFeePercent flashFeeFixed }` to the `transferFlags` globals query that the
  topup/cashout entry screen already fetches, so the data is cache-warm on
  arrival. The field + the `FygaroTopupInfo` type were hand-added to
  public-schema.graphql (no backend access to re-introspect) and codegen was
  regenerated.
- TopupDetails now shows "You'll receive $X.XX" (net after fees) live as the
  user types, mirroring the cashout settlement-amount preview (#683). Card-only;
  the line is hidden when `fygaroTopup` is null so a wrong number is never shown.
- Raise/enforce the card minimum to `fygaroTopup.minimumAmount` (fallback $10
  when null); the invalid-amount alert now states the real minimum.
- New i18n keys added to en + all 23 locale files. Tests cover the $10 floor,
  the $5 rejection (incl. null fallback), and the $9.01 net for a $10 gross.

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

Address code-review findings on the "you'll receive" top-up disclosure:

- estimateTopupNet now computes in integer cents, rounding each fee
  component (processor %+fixed, flash %+fixed) to the nearest cent before
  subtracting, exactly as the backend does (flash#473). The prior
  float-dollar math with a single final round diverged by a cent for a
  large fraction of non-round amounts (e.g. $10.25 showed $9.25 while the
  backend credits $9.24), over-promising the headline number.
- The net preview is now gated on the enforced minimum, so a below-floor
  amount (e.g. $5) no longer shows a concrete receive figure that
  Continue will immediately refuse.
- Tests: added a non-round unit case (10.25 → $9.24) and a preview case
  (10.25 → $9.24, not $9.25) that fail under the old float math, plus a
  below-minimum hide case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QwZjNcKfkMhrBE33HcUVN
@islandbitcoin
islandbitcoin merged commit 9285fe7 into main Aug 11, 2026
10 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