Adds fiat currency metadata to payments
Adds fiat currency information to payment invoices and ledger entries. This allows for tracking the fiat value of transactions and provides a more complete financial picture. Calculates the fiat amount proportionally based on the user's balance and includes the fiat currency, amount, and exchange rates in the invoice's extra data. This data is then extracted and added to the ledger entry's metadata when recording the payment.
This commit is contained in:
parent
8f35788e1a
commit
3add13075c
2 changed files with 77 additions and 1 deletions
19
tasks.py
19
tasks.py
|
|
@ -161,6 +161,23 @@ async def on_invoice_paid(payment: Payment) -> None:
|
|||
# Convert amount from millisatoshis to satoshis
|
||||
amount_sats = payment.amount // 1000
|
||||
|
||||
# Extract fiat metadata from invoice (if present)
|
||||
from decimal import Decimal
|
||||
line_metadata = {}
|
||||
if payment.extra:
|
||||
fiat_currency = payment.extra.get("fiat_currency")
|
||||
fiat_amount = payment.extra.get("fiat_amount")
|
||||
fiat_rate = payment.extra.get("fiat_rate")
|
||||
btc_rate = payment.extra.get("btc_rate")
|
||||
|
||||
if fiat_currency and fiat_amount:
|
||||
line_metadata = {
|
||||
"fiat_currency": fiat_currency,
|
||||
"fiat_amount": str(fiat_amount),
|
||||
"fiat_rate": fiat_rate,
|
||||
"btc_rate": btc_rate,
|
||||
}
|
||||
|
||||
# Get user's receivable account (what user owes)
|
||||
user_receivable = await get_or_create_user_account(
|
||||
user_id, AccountType.ASSET, "Accounts Receivable"
|
||||
|
|
@ -193,12 +210,14 @@ async def on_invoice_paid(payment: Payment) -> None:
|
|||
debit=amount_sats,
|
||||
credit=0,
|
||||
description="Lightning payment received",
|
||||
metadata=line_metadata,
|
||||
),
|
||||
CreateEntryLine(
|
||||
account_id=user_receivable.id,
|
||||
debit=0,
|
||||
credit=amount_sats,
|
||||
description="Payment applied to balance",
|
||||
metadata=line_metadata,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
|
|
|||
59
views_api.py
59
views_api.py
|
|
@ -585,13 +585,52 @@ async def api_generate_payment_invoice(
|
|||
# Get castle wallet ID
|
||||
castle_wallet_id = await check_castle_wallet_configured()
|
||||
|
||||
# Get user's balance to calculate fiat metadata
|
||||
user_balance = await get_user_balance(target_user_id)
|
||||
|
||||
# Calculate proportional fiat amount for this invoice
|
||||
invoice_extra = {"tag": "castle", "user_id": target_user_id}
|
||||
|
||||
if user_balance.fiat_balances:
|
||||
# Simple single-currency solution: use the first (and should be only) currency
|
||||
currencies = list(user_balance.fiat_balances.keys())
|
||||
|
||||
if len(currencies) > 1:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail=f"User has multiple currencies ({', '.join(currencies)}). Please settle to a single currency first.",
|
||||
)
|
||||
|
||||
if len(currencies) == 1:
|
||||
fiat_currency = currencies[0]
|
||||
total_fiat_balance = user_balance.fiat_balances[fiat_currency]
|
||||
total_sat_balance = abs(user_balance.balance) # Use absolute value
|
||||
|
||||
if total_sat_balance > 0:
|
||||
# Calculate proportional fiat amount for this invoice
|
||||
# fiat_amount = (invoice_amount / total_sats) * total_fiat
|
||||
from decimal import Decimal
|
||||
proportion = Decimal(data.amount) / Decimal(total_sat_balance)
|
||||
invoice_fiat_amount = abs(total_fiat_balance) * proportion
|
||||
|
||||
# Calculate fiat rate (sats per fiat unit)
|
||||
fiat_rate = float(data.amount) / float(invoice_fiat_amount) if invoice_fiat_amount > 0 else 0
|
||||
btc_rate = float(invoice_fiat_amount) / float(data.amount) * 100_000_000 if data.amount > 0 else 0
|
||||
|
||||
invoice_extra.update({
|
||||
"fiat_currency": fiat_currency,
|
||||
"fiat_amount": str(invoice_fiat_amount.quantize(Decimal("0.001"))),
|
||||
"fiat_rate": fiat_rate,
|
||||
"btc_rate": btc_rate,
|
||||
})
|
||||
|
||||
# Create invoice on castle wallet
|
||||
invoice_data = CreateInvoice(
|
||||
out=False,
|
||||
amount=data.amount,
|
||||
memo=f"Payment from user {target_user_id[:8]} to Castle",
|
||||
unit="sat",
|
||||
extra={"tag": "castle", "user_id": target_user_id},
|
||||
extra=invoice_extra,
|
||||
)
|
||||
|
||||
payment = await create_payment_request(castle_wallet_id, invoice_data)
|
||||
|
|
@ -663,6 +702,22 @@ async def api_record_payment(
|
|||
# Convert amount from millisatoshis to satoshis
|
||||
amount_sats = payment.amount // 1000
|
||||
|
||||
# Extract fiat metadata from invoice (if present)
|
||||
line_metadata = {}
|
||||
if payment.extra and isinstance(payment.extra, dict):
|
||||
fiat_currency = payment.extra.get("fiat_currency")
|
||||
fiat_amount = payment.extra.get("fiat_amount")
|
||||
fiat_rate = payment.extra.get("fiat_rate")
|
||||
btc_rate = payment.extra.get("btc_rate")
|
||||
|
||||
if fiat_currency and fiat_amount:
|
||||
line_metadata = {
|
||||
"fiat_currency": fiat_currency,
|
||||
"fiat_amount": str(fiat_amount),
|
||||
"fiat_rate": fiat_rate,
|
||||
"btc_rate": btc_rate,
|
||||
}
|
||||
|
||||
# Get user's receivable account (what user owes)
|
||||
user_receivable = await get_or_create_user_account(
|
||||
target_user_id, AccountType.ASSET, "Accounts Receivable"
|
||||
|
|
@ -698,12 +753,14 @@ async def api_record_payment(
|
|||
debit=amount_sats,
|
||||
credit=0,
|
||||
description="Lightning payment received",
|
||||
metadata=line_metadata,
|
||||
),
|
||||
CreateEntryLine(
|
||||
account_id=user_receivable.id,
|
||||
debit=0,
|
||||
credit=amount_sats,
|
||||
description="Payment applied to balance",
|
||||
metadata=line_metadata,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue