Some lightning invoice refactoring
Docker / build-and-push-image (backend, backend, coracle/caravel-backend) (push) Failing after 0s
Docker / build-and-push-image (frontend, frontend, coracle/caravel-frontend) (push) Failing after 1s

This commit is contained in:
Jon Staab
2026-05-21 17:19:35 -07:00
parent a998c9b833
commit e7c0e6fdbe
4 changed files with 75 additions and 157 deletions
+18 -35
View File
@@ -3,7 +3,8 @@ use sqlx::{Sqlite, SqlitePool, Transaction};
use tokio::sync::broadcast;
use crate::models::{
Activity, RELAY_STATUS_ACTIVE, RELAY_STATUS_DELINQUENT, RELAY_STATUS_INACTIVE, Relay, Tenant,
Activity, LightningInvoice, RELAY_STATUS_ACTIVE, RELAY_STATUS_DELINQUENT,
RELAY_STATUS_INACTIVE, Relay, Tenant,
};
#[derive(Clone)]
@@ -298,22 +299,29 @@ impl Command {
// Invoices
/// Insert a freshly minted pending bolt11 for an invoice. Returns `false` if
/// a row already exists (lost an insert race), in which case the caller
/// should read and use the existing row's bolt11.
pub async fn insert_pending_lightning_invoice(
/// Upsert the pending bolt11 for an invoice, returning the resulting row. On
/// conflict the stored bolt11/expiry are replaced — this is how an expired
/// invoice is regenerated — except once the invoice is paid, when the
/// `status = 'pending'` guard makes the update a no-op and `None` is
/// returned so the caller can fall back to reading the settled row.
pub async fn insert_lightning_invoice(
&self,
stripe_invoice_id: &str,
tenant_pubkey: &str,
bolt11: &str,
expires_at: i64,
) -> Result<bool> {
) -> Result<Option<LightningInvoice>> {
let now = chrono::Utc::now().timestamp();
let result = sqlx::query(
let row = sqlx::query_as::<_, LightningInvoice>(
"INSERT INTO lightning_invoice
(stripe_invoice_id, tenant_pubkey, bolt11, status, expires_at, created_at, updated_at)
VALUES (?, ?, ?, 'pending', ?, ?, ?)
ON CONFLICT(stripe_invoice_id) DO NOTHING",
ON CONFLICT(stripe_invoice_id) DO UPDATE SET
bolt11 = excluded.bolt11,
expires_at = excluded.expires_at,
updated_at = excluded.updated_at
WHERE status = 'pending'
RETURNING *",
)
.bind(stripe_invoice_id)
.bind(tenant_pubkey)
@@ -321,35 +329,10 @@ impl Command {
.bind(expires_at)
.bind(now)
.bind(now)
.execute(&self.pool)
.fetch_optional(&self.pool)
.await?;
Ok(result.rows_affected() > 0)
}
/// Replace the stored bolt11 for a still-pending invoice whose previous
/// invoice expired. No-op once the invoice is paid, so this can never
/// overwrite a settled invoice.
pub async fn regenerate_lightning_invoice(
&self,
stripe_invoice_id: &str,
bolt11: &str,
expires_at: i64,
) -> Result<()> {
let now = chrono::Utc::now().timestamp();
sqlx::query(
"UPDATE lightning_invoice
SET bolt11 = ?, expires_at = ?, updated_at = ?
WHERE stripe_invoice_id = ? AND status = 'pending'",
)
.bind(bolt11)
.bind(expires_at)
.bind(now)
.bind(stripe_invoice_id)
.execute(&self.pool)
.await?;
Ok(())
Ok(row)
}
/// Mark a pending invoice paid, recording which method settled it. The