Improve transactionality, align invoice model with frontend

This commit is contained in:
Jon Staab
2026-05-29 13:37:11 -07:00
parent ae3e1c316e
commit 0018a5d4f3
12 changed files with 305 additions and 309 deletions
+3 -48
View File
@@ -1,53 +1,11 @@
use std::sync::Arc;
use axum::extract::{Path, State};
use serde::Serialize;
use crate::api::{Api, AuthedPubkey};
use crate::models::{Intent, Invoice};
use crate::query;
use crate::web::{ApiResult, internal, not_found, ok};
/// An invoice for the client, with its lifecycle flattened to a derived `status`
/// ("open" | "paid" | "void") alongside the underlying timestamps, plus the
/// Stripe PaymentIntents that settled it (empty unless requested).
#[derive(Serialize)]
pub struct InvoiceResponse {
pub id: String,
pub tenant_pubkey: String,
pub status: String,
pub period_start: i64,
pub period_end: i64,
pub created_at: i64,
pub paid_at: Option<i64>,
pub voided_at: Option<i64>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub intents: Vec<Intent>,
}
impl From<Invoice> for InvoiceResponse {
fn from(i: Invoice) -> Self {
let status = if i.is_open() {
"open"
} else if i.paid_at.is_some() {
"paid"
} else {
"void"
};
InvoiceResponse {
status: status.to_string(),
id: i.id,
tenant_pubkey: i.tenant_pubkey,
period_start: i.period_start,
period_end: i.period_end,
created_at: i.created_at,
paid_at: i.paid_at,
voided_at: i.voided_at,
intents: Vec::new(),
}
}
}
/// The tenant's most recent invoice, after first materializing any outstanding
/// line items into a fresh one — so the frontend can collect payment right after
/// a change (e.g. creating a relay). Payment isn't attempted here; the caller
@@ -65,7 +23,7 @@ pub async fn get_tenant_latest_invoice(
let invoice = query::get_latest_invoice(&pubkey).await.map_err(internal)?;
ok(invoice.map(InvoiceResponse::from))
ok(invoice)
}
pub async fn get_invoice(
@@ -80,10 +38,7 @@ pub async fn get_invoice(
api.require_admin_or_tenant(&auth, &invoice.tenant_pubkey)?;
let mut response = InvoiceResponse::from(invoice);
response.intents = query::list_intents_for_invoice(&id).await.map_err(internal)?;
ok(response)
ok(invoice)
}
/// Return a payable Lightning invoice (bolt11) for an invoice, minting one if
@@ -102,7 +57,7 @@ pub async fn get_invoice_bolt11(
let bolt11 = api
.billing
.ensure_and_reconcile_bolt11(&invoice_id)
.ensure_and_reconcile_bolt11(&invoice)
.await
.map_err(internal)?;