Skip to content

feat(fygaro): per-level daily top-up limits (L1 $125 / L2 $1000 / L3 $2500) - #482

Merged
islandbitcoin merged 4 commits into
mainfrom
feat/fygaro-daily-limits
Aug 15, 2026
Merged

feat(fygaro): per-level daily top-up limits (L1 $125 / L2 $1000 / L3 $2500)#482
islandbitcoin merged 4 commits into
mainfrom
feat/fygaro-daily-limits

Conversation

@islandbitcoin

Copy link
Copy Markdown
Contributor

What

Adds operator-tunable daily gross caps per account level to the Fygaro auto-credit gate:

Level Daily cap (gross USD, rolling 24h)
L1 $125
L2 $1,000
L3 (Business) $2,500
  • New gate in evaluateCreditGate, evaluated after over-limit: sums the account's captured Fygaro top-ups (Fiat Received + Completed, Cancelled excluded) over the trailing 24h from the ERPNext audit rows, excluding the current delivery's own row, and refuses auto-credit when gross + prior would exceed the level's cap (inclusive at the cap). Over-cap payments record-only with a reason-named ops alert and stay at Fiat Received for manual review.
  • Unknown level (0 or future) fails closed: no-daily-limit-for-level.
  • A failed history read is transient like settings-unavailable: 500 so Fygaro retries — never coerced to "no history".
  • New ErpNext.sumFygaroTopupGrossCentsSince (fails closed on bad shape / non-numeric amounts, limit_page_length: 0 so pagination can't under-count).
  • Limits exposed on unauthenticated globals.fygaroTopup (l1DailyLimit/l2DailyLimit/l3DailyLimit) so the app can cap the amount BEFORE the card is charged (companion PR in flash-mobile).
  • Settings validation now requires the three limit fields — a pre-migration ERPNext row hard-stops auto-credit instead of crediting uncapped.

Deploy order (matters)

  1. frappe-flash-admin PR first (adds l1/l2/l3_daily_limit to Fygaro Settings + a post-migrate patch seeding the defaults) — deploy via v-tag → make erp.
  2. Then this PR. If flash deploys first, auto-credit drops to manual-review-only (fail closed, no mis-credits) until the ERP fields exist.

Tests

  • fees.spec.ts: cap exceeded / exactly-at-cap inclusive / per-level selection / no-limit-for-level / history-unavailable / gate ordering.
  • payment.spec.ts: record-only + alert for daily-limit-exceeded and no-daily-limit-for-level; 500-retry for history-unavailable; own-row exclusion; history read skipped when credit disabled or settings unavailable; at-cap credit path.
  • ErpNext.spec.ts: filter/window/exclusion contract, cents summing, fail-closed paths.
  • fygaro-settings.spec.ts + globals.spec.ts: field validation and exposure.

Full unit suite: 183 suites / 1621 passed. tsc-check + eslint clean; public SDL + supergraph snapshots updated.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Nt7JxbX5W9o7viKegnXJPH

…$2500)

Adds operator-tunable daily gross caps per account level to the Fygaro
auto-credit gate, summed over a rolling 24h window from the ERPNext audit
rows (Fiat Received + Completed, excluding the current delivery's own row).
Over-cap and unknown-level payments record-only for manual review; a failed
history read is transient (500) so the provider retries. Limits are exposed
on globals.fygaroTopup so the app can cap the amount before the card is
charged.

Requires the matching frappe-flash-admin Fygaro Settings fields
(l1/l2/l3_daily_limit) deployed and saved first — a settings row without
them hard-stops auto-credit (fail closed), same as any malformed row.
bobodread876 and others added 3 commits August 14, 2026 17:30
- Filter the trailing-24h window on last_seen_at (written in UTC by this
  code) instead of Frappe's creation field, which is stored naive in the
  ERP site's configured time zone — a UTC-rendered cutoff against it
  shrank the window by the site's UTC offset, letting the full daily
  allowance recur every ~19h on a UTC-5 site.
- Scope the sum to currency = USD: non-USD payments get Fiat Received
  audit rows carrying the raw foreign-currency amount, so a 5,000 JMD
  payment counted at face value would read as $5,000 of prior gross and
  wrongly lock the account out of auto-credit for a day.
- Fail closed on a null row amount — Number(null) is 0, which silently
  under-counted instead of surfacing FygaroTopupHistoryQueryError.
- Skip the ERPNext history read when the settings auto-credit kill
  switch is off or the payment is non-USD; gate ordering fails those on
  auto-credit-disabled/non-usd before the history gates, so the
  tightened guard saves a wasted list query per webhook without ever
  producing a false history-unavailable.

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

Covers the untested translation between the payment handler and the
ErpNext history query: the fygaro: prefix on excludeRequestId (a broken
prefix would double-count the payment's own audit row and silently halve
the effective daily cap), the now-minus-24h since computation, and the
fail-closed FygaroTopupHistoryQueryError when the ERPNext client is not
configured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nt7JxbX5W9o7viKegnXJPH
@islandbitcoin
islandbitcoin merged commit 18568e5 into main Aug 15, 2026
15 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.

2 participants