Skip to content

Payment method minimum amount + exchange precision & FX rates (#170)#185

Merged
v0l merged 2 commits into
masterfrom
feat/payment-min-amount-170
Jul 21, 2026
Merged

Payment method minimum amount + exchange precision & FX rates (#170)#185
v0l merged 2 commits into
masterfrom
feat/payment-min-amount-170

Conversation

@v0l

@v0l v0l commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Fixes #170.

Payment method minimum amount

Adds a configurable minimum processable amount per payment method config so uneconomic small charges are rejected — e.g. Revolut's flat 20c base fee would otherwise dominate a tiny payment. Lightning has no minimum.

  • DB: min_amount + min_amount_currency on payment_method_config (migration + model + insert/update queries)
  • Enforcement: PricingEngine::enforce_min_amount runs in the subscription renewal path against the gross total (net + tax + processing fee), converting the configured minimum into the payment currency
  • API: exposed on public GET /api/v1/payment/methods and admin config create/update/get, with validation that currency is required when a minimum is set

Exchange module improvements (needed for cross-currency minimums)

  • Integer-precise conversionTickerRate::convert now does f64 math on smallest-unit values instead of routing through f32, which lost a unit (e.g. €1.00 → 999,999 msat instead of 1,000,000). rate stays f32 to avoid a DB payment.rate schema ripple; cast internally. Corrected the old convert test whose expectations were themselves imprecise.
  • Fiat FX ratesfetch_fiat_fx_rates() pulls base-EUR fiat rates from frankfurter.app (ECB, free, no key); both the in-memory and Redis services fetch + cache them alongside mempool BTC prices
  • Cross-rate resolutionPricingEngine::convert_currency resolves any pair directly, inverse, or via a BTC/EUR cross rate; also fixes the old processing-fee base-currency conversion TODO

Tests

New regression tests: convert_precise_no_f32_loss, convert_currency_cross_via_btc, convert_currency_direct_fiat_fx, and 4 min_amount_* enforcement tests. All suites green (lnvps_db 35, lnvps_api 213, lnvps_api_admin 24, plus lnvps_api_common).

Docs

Updated ADMIN_API_ENDPOINTS.md, API_DOCUMENTATION.md, and API_CHANGELOG.md.

v0l added 2 commits July 21, 2026 10:48
…#170)

Add a configurable minimum processable amount per payment method config
so uneconomic small charges are rejected (e.g. Revolut's flat 20c base
fee dominating a tiny payment). Lightning has no minimum.

- DB: add min_amount / min_amount_currency to payment_method_config
  (migration + model + insert/update queries)
- Enforce in the subscription renewal path against the gross total
  (net + tax + processing fee), converting the configured minimum into
  the payment currency
- Expose on the public GET /api/v1/payment/methods and admin payment
  method config create/update/get + docs

Exchange module improvements needed for cross-currency minimums:
- Integer-precise conversion (f64 on smallest-unit values) instead of
  routing through f32, which lost a unit (e.g. EUR1.00 -> 999,999 msat)
- Pull fiat FX rates (base EUR, frankfurter.app/ECB) alongside BTC
  prices; PricingEngine::convert_currency resolves any pair directly,
  inverse, or via a BTC/EUR cross rate
- Properly convert the processing-fee base into the tx currency

Includes regression tests for precision, cross-currency conversion and
min-amount enforcement.
… EUR

The FX fetch hardcoded EUR as the base and a fixed currency list. Instead
fetch FX anchored on each company's own base_currency, covering only the
distinct billing currencies actually in use, and only when there is more
than one (otherwise every conversion is fiat<->BTC, already covered by
mempool prices).

- fetch_fiat_fx_rates now takes (base, symbols); BTC/self are skipped
- fetch_fx_for_currencies fetches every ordered pair among a currency set
- the rate updater derives the set from list_companies().base_currency
- removed the hardcoded EUR-base FX call from the generic fetch_rates
@v0l
v0l merged commit 0948ae2 into master Jul 21, 2026
8 checks passed
@v0l
v0l deleted the feat/payment-min-amount-170 branch July 21, 2026 10:06
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.

Payment method min amount

1 participant