invoices: multi-crypto payment support
All checks were successful
Build and Deploy to Production / test (push) Successful in 18s
Build and Deploy to Production / build (push) Successful in 49s
Build and Deploy to Production / deploy (push) Successful in 31s

One asset per invoice from a registry (lib/crypto_asset.rb): BTC, LTC,
ETH, XMR, SOL, ALGO, USDT/USDC on ERC-20/TRC-20/BEP-20/Solana/Algorand.
Migration 003 generalizes the ltc_* columns to crypto_* + crypto_coin
(existing LTC invoices backfilled). QR payload adapts per asset: BIP21
amount URIs where supported, bare address for tokens with a network
hint on the PDF. Default addresses come from the CRYPTO_ADDRESSES
secret (JSON code=>address); LTC_ADDRESS still works as legacy.
This commit is contained in:
Sergei Poljanski 2026-07-03 02:01:57 +04:00
commit 0fbb6ee809
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
20 changed files with 410 additions and 143 deletions

33
lib/crypto_qr.rb Normal file
View file

@ -0,0 +1,33 @@
require 'rqrcode'
require_relative 'crypto_asset'
# Renders a crypto payment QR as PNG bytes for embedding in the PDF.
# The encoded payload depends on the asset (see CryptoAsset): a payment URI
# with amount, a bare scheme URI, or just the address.
module CryptoQr
module_function
# `amount` is a BigDecimal or nil (address-only QR).
def uri(coin, address, amount)
asset = CryptoAsset[coin] or return address
return address unless asset[:scheme]
base = "#{asset[:scheme]}:#{address}"
return base if amount.nil? || asset[:amount_param].nil?
# Trim trailing zeros so the wallet shows a clean amount; keep up to 8 dp.
amt = amount.round(8).to_s('F').sub(/\.?0+$/, '')
"#{base}?#{asset[:amount_param]}=#{amt}"
end
# Returns PNG bytes (binary string), sized for a crisp ~120pt PDF placement.
def png(coin, address, amount, size: 480)
qr = RQRCode::QRCode.new(uri(coin, address, amount), level: :m)
qr.as_png(
bit_depth: 1,
border_modules: 2,
color: 'black',
fill: 'white',
module_px_size: 6,
resize_exactly_to: size
).to_s
end
end