diff --git a/backend/src/billing.rs b/backend/src/billing.rs index 448bd59..90c968c 100644 --- a/backend/src/billing.rs +++ b/backend/src/billing.rs @@ -44,6 +44,9 @@ impl BillingService { if let Err(err) = self.bill_tenant(&tenant).await { tracing::error!(tenant = tenant.pubkey, error = %err, "billing failed"); } + if let Err(err) = self.suspend_if_delinquent(&tenant).await { + tracing::error!(tenant = tenant.pubkey, error = %err, "grace period enforcement failed"); + } } Ok(()) } @@ -105,6 +108,32 @@ impl BillingService { Ok(()) } + async fn suspend_if_delinquent(&self, tenant: &Tenant) -> Result<()> { + if tenant.status != "active" { + return Ok(()); + } + + let invoices = self.repo.list_invoices_by_tenant(&tenant.pubkey).await?; + let latest = match invoices.first() { + Some(invoice) => invoice, + None => return Ok(()), + }; + + if latest.status != "pending" { + return Ok(()); + } + + let created_at = parse_timestamp(&latest.created_at)?; + let deadline = created_at + chrono::Duration::days(7); + if Utc::now() < deadline { + return Ok(()); + } + + self.repo.update_tenant_status(&tenant.pubkey, "suspended").await?; + self.repo.suspend_relays_for_tenant(&tenant.pubkey).await?; + Ok(()) + } + async fn make_invoice(&self, amount: i64) -> Result { if self.platform_nwc_url.trim().is_empty() { return Err(anyhow!("NWC_URL is required to generate invoices")); diff --git a/backend/src/repo.rs b/backend/src/repo.rs index 23354a9..6a10682 100644 --- a/backend/src/repo.rs +++ b/backend/src/repo.rs @@ -122,6 +122,14 @@ impl Repo { Ok(()) } + pub async fn suspend_relays_for_tenant(&self, tenant: &str) -> Result<()> { + sqlx::query("UPDATE relays SET status = 'suspended' WHERE tenant = ? AND status = 'active'") + .bind(tenant) + .execute(&self.pool) + .await?; + Ok(()) + } + pub async fn get_relay(&self, id: &str) -> Result> { let relay = sqlx::query_as::<_, Relay>( "SELECT id, tenant, name, subdomain, schema, icon, description, plan, status FROM relays WHERE id = ?",