Payment method minimum amount + exchange precision & FX rates (#170)#185
Merged
Conversation
…#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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
min_amount+min_amount_currencyonpayment_method_config(migration + model + insert/update queries)PricingEngine::enforce_min_amountruns in the subscription renewal path against the gross total (net + tax + processing fee), converting the configured minimum into the payment currencyGET /api/v1/payment/methodsand admin config create/update/get, with validation that currency is required when a minimum is setExchange module improvements (needed for cross-currency minimums)
TickerRate::convertnow doesf64math on smallest-unit values instead of routing throughf32, which lost a unit (e.g. €1.00 → 999,999 msat instead of 1,000,000).ratestaysf32to avoid a DBpayment.rateschema ripple; cast internally. Corrected the oldconverttest whose expectations were themselves imprecise.fetch_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 pricesPricingEngine::convert_currencyresolves any pair directly, inverse, or via a BTC/EUR cross rate; also fixes the old processing-fee base-currency conversion TODOTests
New regression tests:
convert_precise_no_f32_loss,convert_currency_cross_via_btc,convert_currency_direct_fiat_fx, and 4min_amount_*enforcement tests. All suites green (lnvps_db35,lnvps_api213,lnvps_api_admin24, pluslnvps_api_common).Docs
Updated
ADMIN_API_ENDPOINTS.md,API_DOCUMENTATION.md, andAPI_CHANGELOG.md.