Migrates payment processing to Fava
Removes direct journal entry creation in favor of using Fava for accounting. This change centralizes accounting logic in Fava, improving auditability and consistency. It replaces direct database interactions for recording payments and settlements with calls to the Fava client. The changes also refactor balance retrieval to fetch data from Fava.
This commit is contained in:
parent
e3acc53e20
commit
efc09aa5ce
1 changed files with 163 additions and 188 deletions
351
views_api.py
351
views_api.py
|
|
@ -18,13 +18,11 @@ from .crud import (
|
||||||
create_account,
|
create_account,
|
||||||
create_account_permission,
|
create_account_permission,
|
||||||
create_balance_assertion,
|
create_balance_assertion,
|
||||||
create_journal_entry,
|
|
||||||
create_manual_payment_request,
|
create_manual_payment_request,
|
||||||
db,
|
db,
|
||||||
delete_account_permission,
|
delete_account_permission,
|
||||||
delete_balance_assertion,
|
delete_balance_assertion,
|
||||||
get_account,
|
get_account,
|
||||||
get_account_balance,
|
|
||||||
get_account_by_name,
|
get_account_by_name,
|
||||||
get_account_permission,
|
get_account_permission,
|
||||||
get_account_permissions,
|
get_account_permissions,
|
||||||
|
|
@ -32,7 +30,6 @@ from .crud import (
|
||||||
get_all_accounts,
|
get_all_accounts,
|
||||||
get_all_journal_entries,
|
get_all_journal_entries,
|
||||||
get_all_manual_payment_requests,
|
get_all_manual_payment_requests,
|
||||||
get_all_user_balances,
|
|
||||||
get_all_user_wallet_settings,
|
get_all_user_wallet_settings,
|
||||||
get_balance_assertion,
|
get_balance_assertion,
|
||||||
get_balance_assertions,
|
get_balance_assertions,
|
||||||
|
|
@ -40,7 +37,6 @@ from .crud import (
|
||||||
get_journal_entry,
|
get_journal_entry,
|
||||||
get_manual_payment_request,
|
get_manual_payment_request,
|
||||||
get_or_create_user_account,
|
get_or_create_user_account,
|
||||||
get_user_balance,
|
|
||||||
get_user_manual_payment_requests,
|
get_user_manual_payment_requests,
|
||||||
get_user_permissions,
|
get_user_permissions,
|
||||||
get_user_permissions_with_inheritance,
|
get_user_permissions_with_inheritance,
|
||||||
|
|
@ -1045,8 +1041,19 @@ async def api_generate_payment_invoice(
|
||||||
# Get castle wallet ID
|
# Get castle wallet ID
|
||||||
castle_wallet_id = await check_castle_wallet_configured()
|
castle_wallet_id = await check_castle_wallet_configured()
|
||||||
|
|
||||||
# Get user's balance to calculate fiat metadata
|
# Get user's balance from Fava to calculate fiat metadata
|
||||||
user_balance = await get_user_balance(target_user_id)
|
from .fava_client import get_fava_client
|
||||||
|
|
||||||
|
fava = get_fava_client()
|
||||||
|
balance_data = await fava.get_user_balance(target_user_id)
|
||||||
|
|
||||||
|
# Build UserBalance object for compatibility
|
||||||
|
user_balance = UserBalance(
|
||||||
|
user_id=target_user_id,
|
||||||
|
balance=balance_data["balance"],
|
||||||
|
accounts=[],
|
||||||
|
fiat_balances=balance_data["fiat_balances"]
|
||||||
|
)
|
||||||
|
|
||||||
# Calculate proportional fiat amount for this invoice
|
# Calculate proportional fiat amount for this invoice
|
||||||
invoice_extra = {"tag": "castle", "user_id": target_user_id}
|
invoice_extra = {"tag": "castle", "user_id": target_user_id}
|
||||||
|
|
@ -1147,36 +1154,47 @@ async def api_record_payment(
|
||||||
detail="Payment metadata missing user_id. Cannot determine which user to credit.",
|
detail="Payment metadata missing user_id. Cannot determine which user to credit.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if payment already recorded (idempotency)
|
# Check if payment already recorded in Fava (idempotency)
|
||||||
from .crud import get_journal_entry_by_reference
|
from .fava_client import get_fava_client
|
||||||
existing = await get_journal_entry_by_reference(data.payment_hash)
|
from .beancount_format import format_payment_entry
|
||||||
if existing:
|
import httpx
|
||||||
# Payment already recorded, return existing entry
|
|
||||||
balance = await get_user_balance(target_user_id)
|
fava = get_fava_client()
|
||||||
return {
|
|
||||||
"journal_entry_id": existing.id,
|
# Query Fava for existing entry with this payment hash link
|
||||||
"new_balance": balance.balance,
|
query = f"SELECT * WHERE links ~ 'ln-{data.payment_hash[:16]}'"
|
||||||
"message": "Payment already recorded",
|
try:
|
||||||
}
|
async with httpx.AsyncClient(timeout=5.0) as client:
|
||||||
|
response = await client.get(
|
||||||
|
f"{fava.base_url}/query",
|
||||||
|
params={"query_string": query}
|
||||||
|
)
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
if result.get('data', {}).get('rows'):
|
||||||
|
# Payment already recorded, return existing entry
|
||||||
|
balance_data = await fava.get_user_balance(target_user_id)
|
||||||
|
return {
|
||||||
|
"journal_entry_id": f"fava-exists-{data.payment_hash[:16]}",
|
||||||
|
"new_balance": balance_data["balance"],
|
||||||
|
"message": "Payment already recorded",
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not check Fava for duplicate payment: {e}")
|
||||||
|
# Continue anyway - Fava/Beancount will catch duplicate if it exists
|
||||||
|
|
||||||
# Convert amount from millisatoshis to satoshis
|
# Convert amount from millisatoshis to satoshis
|
||||||
amount_sats = payment.amount // 1000
|
amount_sats = payment.amount // 1000
|
||||||
|
|
||||||
# Extract fiat metadata from invoice (if present)
|
# Extract fiat metadata from invoice (if present)
|
||||||
line_metadata = {}
|
fiat_currency = None
|
||||||
|
fiat_amount = None
|
||||||
if payment.extra and isinstance(payment.extra, dict):
|
if payment.extra and isinstance(payment.extra, dict):
|
||||||
fiat_currency = payment.extra.get("fiat_currency")
|
fiat_currency = payment.extra.get("fiat_currency")
|
||||||
fiat_amount = payment.extra.get("fiat_amount")
|
fiat_amount_str = payment.extra.get("fiat_amount")
|
||||||
fiat_rate = payment.extra.get("fiat_rate")
|
if fiat_amount_str:
|
||||||
btc_rate = payment.extra.get("btc_rate")
|
from decimal import Decimal
|
||||||
|
fiat_amount = Decimal(str(fiat_amount_str))
|
||||||
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)
|
# Get user's receivable account (what user owes)
|
||||||
user_receivable = await get_or_create_user_account(
|
user_receivable = await get_or_create_user_account(
|
||||||
|
|
@ -1190,47 +1208,31 @@ async def api_record_payment(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Lightning account not found"
|
status_code=HTTPStatus.NOT_FOUND, detail="Lightning account not found"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create journal entry to record payment
|
# Format payment entry and submit to Fava
|
||||||
# DR Assets:Bitcoin:Lightning, CR Assets:Receivable (User)
|
entry = format_payment_entry(
|
||||||
# This reduces what the user owes
|
user_id=target_user_id,
|
||||||
|
payment_account=lightning_account.name,
|
||||||
# Add meta information for audit trail
|
payable_or_receivable_account=user_receivable.name,
|
||||||
entry_meta = {
|
amount_sats=amount_sats,
|
||||||
"source": "lightning_payment",
|
|
||||||
"created_via": "record_payment",
|
|
||||||
"payment_hash": data.payment_hash,
|
|
||||||
"payer_user_id": target_user_id,
|
|
||||||
}
|
|
||||||
|
|
||||||
entry_data = CreateJournalEntry(
|
|
||||||
description=f"Lightning payment from user {target_user_id[:8]}",
|
description=f"Lightning payment from user {target_user_id[:8]}",
|
||||||
reference=data.payment_hash,
|
entry_date=datetime.now().date(),
|
||||||
flag=JournalEntryFlag.CLEARED, # Payment is immediately cleared
|
is_payable=False, # User paying castle (receivable settlement)
|
||||||
meta=entry_meta,
|
fiat_currency=fiat_currency,
|
||||||
lines=[
|
fiat_amount=fiat_amount,
|
||||||
CreateEntryLine(
|
payment_hash=data.payment_hash,
|
||||||
account_id=lightning_account.id,
|
reference=data.payment_hash
|
||||||
amount=amount_sats, # Positive = debit (asset increase)
|
|
||||||
description="Lightning payment received",
|
|
||||||
metadata=line_metadata,
|
|
||||||
),
|
|
||||||
CreateEntryLine(
|
|
||||||
account_id=user_receivable.id,
|
|
||||||
amount=-amount_sats, # Negative = credit (asset decrease - receivable settled)
|
|
||||||
description="Payment applied to balance",
|
|
||||||
metadata=line_metadata,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
entry = await create_journal_entry(entry_data, target_user_id)
|
# Submit to Fava
|
||||||
|
result = await fava.add_entry(entry)
|
||||||
|
logger.info(f"Payment entry submitted to Fava: {result.get('data', 'Unknown')}")
|
||||||
|
|
||||||
# Get updated balance
|
# Get updated balance from Fava
|
||||||
balance = await get_user_balance(target_user_id)
|
balance_data = await fava.get_user_balance(target_user_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"journal_entry_id": entry.id,
|
"journal_entry_id": f"fava-{datetime.now().timestamp()}",
|
||||||
"new_balance": balance.balance,
|
"new_balance": balance_data["balance"],
|
||||||
"message": "Payment recorded successfully",
|
"message": "Payment recorded successfully",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1257,32 +1259,34 @@ async def api_pay_user(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Lightning account not found"
|
status_code=HTTPStatus.NOT_FOUND, detail="Lightning account not found"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create journal entry
|
# Format payment entry and submit to Fava
|
||||||
# DR Liabilities:Payable (User), CR Assets:Bitcoin:Lightning
|
# DR Liabilities:Payable (User), CR Assets:Bitcoin:Lightning
|
||||||
entry_data = CreateJournalEntry(
|
from .fava_client import get_fava_client
|
||||||
|
from .beancount_format import format_payment_entry
|
||||||
|
|
||||||
|
fava = get_fava_client()
|
||||||
|
|
||||||
|
entry = format_payment_entry(
|
||||||
|
user_id=user_id,
|
||||||
|
payment_account=lightning_account.name,
|
||||||
|
payable_or_receivable_account=user_payable.name,
|
||||||
|
amount_sats=amount,
|
||||||
description=f"Payment to user {user_id[:8]}",
|
description=f"Payment to user {user_id[:8]}",
|
||||||
lines=[
|
entry_date=datetime.now().date(),
|
||||||
CreateEntryLine(
|
is_payable=True, # Castle paying user
|
||||||
account_id=user_payable.id,
|
reference=f"PAY-{user_id[:8]}"
|
||||||
amount=amount, # Positive = debit (liability decrease)
|
|
||||||
description="Payment made to user",
|
|
||||||
),
|
|
||||||
CreateEntryLine(
|
|
||||||
account_id=lightning_account.id,
|
|
||||||
amount=-amount, # Negative = credit (asset decrease)
|
|
||||||
description="Lightning payment sent",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
entry = await create_journal_entry(entry_data, wallet.wallet.id)
|
# Submit to Fava
|
||||||
|
result = await fava.add_entry(entry)
|
||||||
|
logger.info(f"Payment submitted to Fava: {result.get('data', 'Unknown')}")
|
||||||
|
|
||||||
# Get updated balance
|
# Get updated balance from Fava
|
||||||
balance = await get_user_balance(user_id)
|
balance_data = await fava.get_user_balance(user_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"journal_entry": entry.dict(),
|
"journal_entry_id": f"fava-{datetime.now().timestamp()}",
|
||||||
"new_balance": balance.balance,
|
"new_balance": balance_data["balance"],
|
||||||
"message": "Payment recorded successfully",
|
"message": "Payment recorded successfully",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1351,14 +1355,16 @@ async def api_settle_receivable(
|
||||||
detail=f"Payment account '{account_name}' not found. Please create it first.",
|
detail=f"Payment account '{account_name}' not found. Please create it first.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create journal entry
|
# Format settlement entry and submit to Fava
|
||||||
# DR Cash/Bank (asset increased), CR Accounts Receivable (asset decreased)
|
# DR Cash/Bank (asset increased), CR Accounts Receivable (asset decreased)
|
||||||
# This records that user paid their debt
|
# This records that user paid their debt
|
||||||
|
from .fava_client import get_fava_client
|
||||||
# Determine the amount to record in the journal
|
from .beancount_format import format_payment_entry
|
||||||
# IMPORTANT: Always record in satoshis to match the receivable account balance
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
fava = get_fava_client()
|
||||||
|
|
||||||
|
# Determine amount and currency
|
||||||
if data.currency:
|
if data.currency:
|
||||||
# Fiat currency payment (e.g., EUR, USD)
|
# Fiat currency payment (e.g., EUR, USD)
|
||||||
# Use the sats equivalent for the journal entry to match the receivable
|
# Use the sats equivalent for the journal entry to match the receivable
|
||||||
|
|
@ -1368,68 +1374,51 @@ async def api_settle_receivable(
|
||||||
detail="amount_sats is required when settling with fiat currency"
|
detail="amount_sats is required when settling with fiat currency"
|
||||||
)
|
)
|
||||||
amount_in_sats = data.amount_sats
|
amount_in_sats = data.amount_sats
|
||||||
line_metadata = {
|
fiat_currency = data.currency.upper()
|
||||||
"fiat_currency": data.currency.upper(),
|
fiat_amount = data.amount
|
||||||
"fiat_amount": str(data.amount.quantize(Decimal("0.001"))),
|
|
||||||
"fiat_rate": float(data.amount_sats) / float(data.amount) if data.amount > 0 else 0,
|
|
||||||
"btc_rate": float(data.amount) / float(data.amount_sats) * 100_000_000 if data.amount_sats > 0 else 0,
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
# Satoshi payment
|
# Satoshi payment
|
||||||
amount_in_sats = int(data.amount)
|
amount_in_sats = int(data.amount)
|
||||||
line_metadata = {}
|
fiat_currency = None
|
||||||
|
fiat_amount = None
|
||||||
|
|
||||||
# Add payment hash for lightning payments
|
# Format payment entry
|
||||||
if data.payment_hash:
|
entry = format_payment_entry(
|
||||||
line_metadata["payment_hash"] = data.payment_hash
|
user_id=data.user_id,
|
||||||
|
payment_account=payment_account.name,
|
||||||
# Add transaction ID for on-chain Bitcoin payments
|
payable_or_receivable_account=user_receivable.name,
|
||||||
if data.txid:
|
amount_sats=amount_in_sats,
|
||||||
line_metadata["txid"] = data.txid
|
|
||||||
|
|
||||||
# Add meta information for audit trail
|
|
||||||
entry_meta = {
|
|
||||||
"source": "manual_settlement",
|
|
||||||
"payment_method": data.payment_method,
|
|
||||||
"settled_by": wallet.wallet.user,
|
|
||||||
"payer_user_id": data.user_id,
|
|
||||||
}
|
|
||||||
if data.currency:
|
|
||||||
entry_meta["currency"] = data.currency
|
|
||||||
|
|
||||||
entry_data = CreateJournalEntry(
|
|
||||||
description=data.description,
|
description=data.description,
|
||||||
reference=data.reference or f"MANUAL-{data.user_id[:8]}",
|
entry_date=datetime.now().date(),
|
||||||
flag=JournalEntryFlag.CLEARED, # Manual payments are immediately cleared
|
is_payable=False, # User paying castle (receivable settlement)
|
||||||
meta=entry_meta,
|
fiat_currency=fiat_currency,
|
||||||
lines=[
|
fiat_amount=fiat_amount,
|
||||||
CreateEntryLine(
|
payment_hash=data.payment_hash,
|
||||||
account_id=payment_account.id,
|
reference=data.reference or f"MANUAL-{data.user_id[:8]}"
|
||||||
amount=amount_in_sats, # Positive = debit (asset increase)
|
|
||||||
description=f"Payment received via {data.payment_method}",
|
|
||||||
metadata=line_metadata,
|
|
||||||
),
|
|
||||||
CreateEntryLine(
|
|
||||||
account_id=user_receivable.id,
|
|
||||||
amount=-amount_in_sats, # Negative = credit (asset decrease - receivable settled)
|
|
||||||
description="Receivable settled",
|
|
||||||
metadata=line_metadata,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
entry = await create_journal_entry(entry_data, wallet.wallet.id)
|
# Add additional metadata to entry
|
||||||
|
if "meta" not in entry:
|
||||||
|
entry["meta"] = {}
|
||||||
|
entry["meta"]["payment-method"] = data.payment_method
|
||||||
|
entry["meta"]["settled-by"] = wallet.wallet.user
|
||||||
|
if data.txid:
|
||||||
|
entry["meta"]["txid"] = data.txid
|
||||||
|
|
||||||
# Get updated balance
|
# Submit to Fava
|
||||||
balance = await get_user_balance(data.user_id)
|
result = await fava.add_entry(entry)
|
||||||
|
logger.info(f"Receivable settlement submitted to Fava: {result.get('data', 'Unknown')}")
|
||||||
|
|
||||||
|
# Get updated balance from Fava
|
||||||
|
balance_data = await fava.get_user_balance(data.user_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"journal_entry_id": entry.id,
|
"journal_entry_id": f"fava-{datetime.now().timestamp()}",
|
||||||
"user_id": data.user_id,
|
"user_id": data.user_id,
|
||||||
"amount_settled": float(data.amount),
|
"amount_settled": float(data.amount),
|
||||||
"currency": data.currency,
|
"currency": data.currency,
|
||||||
"payment_method": data.payment_method,
|
"payment_method": data.payment_method,
|
||||||
"new_balance": balance.balance,
|
"new_balance": balance_data["balance"],
|
||||||
"message": f"Receivable settled successfully via {data.payment_method}",
|
"message": f"Receivable settled successfully via {data.payment_method}",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1496,10 +1485,16 @@ async def api_pay_user(
|
||||||
detail=f"Payment account '{account_name}' not found. Please create it first.",
|
detail=f"Payment account '{account_name}' not found. Please create it first.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Determine the amount to record in the journal
|
# Format payment entry and submit to Fava
|
||||||
# IMPORTANT: Always record in satoshis to match the payable account balance
|
# DR Accounts Payable (liability decreased), CR Cash/Lightning/Bank (asset decreased)
|
||||||
|
# This records that castle paid its debt
|
||||||
|
from .fava_client import get_fava_client
|
||||||
|
from .beancount_format import format_payment_entry
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
fava = get_fava_client()
|
||||||
|
|
||||||
|
# Determine amount and currency
|
||||||
if data.currency:
|
if data.currency:
|
||||||
# Fiat currency payment (e.g., EUR, USD)
|
# Fiat currency payment (e.g., EUR, USD)
|
||||||
# Use the sats equivalent for the journal entry to match the payable
|
# Use the sats equivalent for the journal entry to match the payable
|
||||||
|
|
@ -1509,71 +1504,51 @@ async def api_pay_user(
|
||||||
detail="amount_sats is required when paying with fiat currency"
|
detail="amount_sats is required when paying with fiat currency"
|
||||||
)
|
)
|
||||||
amount_in_sats = data.amount_sats
|
amount_in_sats = data.amount_sats
|
||||||
line_metadata = {
|
fiat_currency = data.currency.upper()
|
||||||
"fiat_currency": data.currency.upper(),
|
fiat_amount = data.amount
|
||||||
"fiat_amount": str(data.amount.quantize(Decimal("0.001"))),
|
|
||||||
"fiat_rate": float(data.amount_sats) / float(data.amount) if data.amount > 0 else 0,
|
|
||||||
"btc_rate": float(data.amount) / float(data.amount_sats) * 100_000_000 if data.amount_sats > 0 else 0,
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
# Satoshi payment
|
# Satoshi payment
|
||||||
amount_in_sats = int(data.amount)
|
amount_in_sats = int(data.amount)
|
||||||
line_metadata = {}
|
fiat_currency = None
|
||||||
|
fiat_amount = None
|
||||||
|
|
||||||
# Add payment hash for lightning payments
|
# Format payment entry
|
||||||
if data.payment_hash:
|
entry = format_payment_entry(
|
||||||
line_metadata["payment_hash"] = data.payment_hash
|
user_id=data.user_id,
|
||||||
|
payment_account=payment_account.name,
|
||||||
# Add transaction ID for on-chain Bitcoin payments
|
payable_or_receivable_account=user_payable.name,
|
||||||
if data.txid:
|
amount_sats=amount_in_sats,
|
||||||
line_metadata["txid"] = data.txid
|
|
||||||
|
|
||||||
# Create journal entry
|
|
||||||
# DR Accounts Payable (liability decreased), CR Cash/Lightning/Bank (asset decreased)
|
|
||||||
# This records that castle paid its debt
|
|
||||||
|
|
||||||
entry_meta = {
|
|
||||||
"source": "manual_payment" if data.payment_method != "lightning" else "lightning_payment",
|
|
||||||
"payment_method": data.payment_method,
|
|
||||||
"paid_by": wallet.wallet.user,
|
|
||||||
"payee_user_id": data.user_id,
|
|
||||||
}
|
|
||||||
if data.currency:
|
|
||||||
entry_meta["currency"] = data.currency
|
|
||||||
|
|
||||||
entry_data = CreateJournalEntry(
|
|
||||||
description=data.description or f"Payment to user via {data.payment_method}",
|
description=data.description or f"Payment to user via {data.payment_method}",
|
||||||
reference=data.reference or f"PAY-{data.user_id[:8]}",
|
entry_date=datetime.now().date(),
|
||||||
flag=JournalEntryFlag.CLEARED, # Payments are immediately cleared
|
is_payable=True, # Castle paying user (payable settlement)
|
||||||
meta=entry_meta,
|
fiat_currency=fiat_currency,
|
||||||
lines=[
|
fiat_amount=fiat_amount,
|
||||||
CreateEntryLine(
|
payment_hash=data.payment_hash,
|
||||||
account_id=user_payable.id,
|
reference=data.reference or f"PAY-{data.user_id[:8]}"
|
||||||
amount=amount_in_sats, # Positive = debit (liability decrease)
|
|
||||||
description="Payable settled",
|
|
||||||
metadata=line_metadata,
|
|
||||||
),
|
|
||||||
CreateEntryLine(
|
|
||||||
account_id=payment_account.id,
|
|
||||||
amount=-amount_in_sats, # Negative = credit (asset decrease)
|
|
||||||
description=f"Payment sent via {data.payment_method}",
|
|
||||||
metadata=line_metadata,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
entry = await create_journal_entry(entry_data, wallet.wallet.id)
|
# Add additional metadata to entry
|
||||||
|
if "meta" not in entry:
|
||||||
|
entry["meta"] = {}
|
||||||
|
entry["meta"]["payment-method"] = data.payment_method
|
||||||
|
entry["meta"]["paid-by"] = wallet.wallet.user
|
||||||
|
if data.txid:
|
||||||
|
entry["meta"]["txid"] = data.txid
|
||||||
|
|
||||||
# Get updated balance
|
# Submit to Fava
|
||||||
balance = await get_user_balance(data.user_id)
|
result = await fava.add_entry(entry)
|
||||||
|
logger.info(f"Payable payment submitted to Fava: {result.get('data', 'Unknown')}")
|
||||||
|
|
||||||
|
# Get updated balance from Fava
|
||||||
|
balance_data = await fava.get_user_balance(data.user_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"journal_entry_id": entry.id,
|
"journal_entry_id": f"fava-{datetime.now().timestamp()}",
|
||||||
"user_id": data.user_id,
|
"user_id": data.user_id,
|
||||||
"amount_paid": float(data.amount),
|
"amount_paid": float(data.amount),
|
||||||
"currency": data.currency,
|
"currency": data.currency,
|
||||||
"payment_method": data.payment_method,
|
"payment_method": data.payment_method,
|
||||||
"new_balance": balance.balance,
|
"new_balance": balance_data["balance"],
|
||||||
"message": f"User paid successfully via {data.payment_method}",
|
"message": f"User paid successfully via {data.payment_method}",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue