Wire scoped API keys and add advisory settle-amount tripwire
Scoped API keys (P1): migrate 58 admin endpoints from require_admin to
require_scope so ks_ keys with Read-only/License-issuer/Support/Full-admin roles
work as intended. 12 sensitive endpoints stay master-key-only (issuer key,
provider connect/disconnect, web password, api-key CRUD, db-info, operator-name,
per-license tier change). require_scope is re-exported from api::admin so both
auth gates import from one place. Adds role-boundary tests.
Settle-amount tripwire (P1): get_invoice_status now returns
ProviderInvoiceSnapshot { status, amount }. On a confirmed settle,
audit_settle_amount (shared by the webhook and reconcile issue paths) compares
the provider-reported sat amount against the invoice's amount_sats and, on drift,
logs a warning + writes an invoice.amount_mismatch audit row, then issues anyway.
Advisory by design: a hard gate would fight an operator's BTCPay payment
tolerance, and Settled already implies paid-in-full. SAT-only — skips non-SAT
settles (fiat subscription renewals) and unparseable amounts.
This commit is contained in:
@@ -20,8 +20,8 @@ use keysat::api::AppState;
|
||||
use keysat::config::Config;
|
||||
use keysat::license_self::Tier;
|
||||
use keysat::payment::{
|
||||
CreateInvoiceParams, CreatedInvoiceHandle, PaymentProvider, ProviderInvoiceStatus,
|
||||
ProviderKind, ProviderWebhookEvent,
|
||||
CreateInvoiceParams, CreatedInvoiceHandle, PaymentProvider, ProviderInvoiceSnapshot,
|
||||
ProviderInvoiceStatus, ProviderKind, ProviderWebhookEvent,
|
||||
};
|
||||
use keysat::subscriptions;
|
||||
use serde_json::{json, Value};
|
||||
@@ -133,8 +133,11 @@ impl PaymentProvider for MockProvider {
|
||||
checkout_url: format!("http://mock.test/checkout/{n}"),
|
||||
})
|
||||
}
|
||||
async fn get_invoice_status(&self, _id: &str) -> Result<ProviderInvoiceStatus> {
|
||||
Ok(ProviderInvoiceStatus::Pending)
|
||||
async fn get_invoice_status(&self, _id: &str) -> Result<ProviderInvoiceSnapshot> {
|
||||
Ok(ProviderInvoiceSnapshot {
|
||||
status: ProviderInvoiceStatus::Pending,
|
||||
amount: None,
|
||||
})
|
||||
}
|
||||
fn validate_webhook(&self, _h: &HeaderMap, _b: &[u8]) -> Result<ProviderWebhookEvent> {
|
||||
anyhow::bail!("not exercised by renewal-worker tests")
|
||||
|
||||
Reference in New Issue
Block a user