Files
smirk-backend-core/src/api/openapi.rs
T
such-gitea 9536dd099c feat(premium): invoice through more than one processor
Premium could invoice through exactly one processor, so an operator
running BTCPay for BTC, LTC and Grin could not also take Monero or
Wownero: BTCPay has no Wownero support, and this operator runs Monero
through its own checkout rather than the BTCPay plugin.

xmrcheckout and wowcheckout implement the same two Greenfield invoice
routes, accept the same `token` auth and report the same statuses, so
the existing adapter serves them unchanged. What was missing was more
than one of them. Each is now an optional rail with its own URL, store
and key (PAYMENT_XMRCHECKOUT_* and PAYMENT_WOWCHECKOUT_*), all three
values or none, and a half-configured rail refuses to start and names
itself rather than advertising a coin that cannot be paid.

The invoice row already recorded which provider minted it; activation
now polls that one, never whichever is primary today. An invoice whose
rail was switched off is refused and left unconsumed, so turning the
rail back on lets the user redeem what they paid. A client that names
a rail the instance does not offer is refused rather than handed an
invoice in a different coin; a client that names none gets the
primary, which is how every client before this behaved.

Registration is untouched and keeps the primary. /capabilities lists
the rails and their assets, primary first, from config alone, so it
never depends on a processor being reachable. PAYMENT_ASSETS labels
the primary. Checkout rails also get an exact confirmationsRequired,
which BTCPay Server is not sent since it only has speedPolicy buckets.
2026-09-26 10:52:50 -04:00

239 lines
11 KiB
Rust

//! OpenAPI specification assembly.
//!
//! `ApiDoc` aggregates the annotated handlers into the spec that is the single
//! source of truth for the API contract and the wallet's generated TypeScript
//! client. Add a path here once its handler carries `#[utoipa::path(...)]` and
//! its request/response types derive `utoipa::ToSchema`.
use utoipa::OpenApi;
#[derive(OpenApi)]
#[openapi(
info(
title = "smirk-backend-core",
version = "0.3.0",
description = "Open, self-hostable backend for the Smirk non-custodial multi-chain wallet."
),
modifiers(&SecurityAddon),
paths(
crate::api::health::health,
crate::api::capabilities::capabilities,
crate::api::prices::prices,
crate::api::auth::pow_challenge,
crate::api::auth::extension_register,
crate::api::auth::check_restore,
crate::api::auth::payment_invoice,
crate::api::auth::refresh_token,
crate::api::auth::logout,
crate::api::auth::get_me,
crate::api::auth::nostr_login,
crate::api::auth::nostr_register_challenge,
crate::api::auth::nostr_register,
crate::api::auth::nostr_link_challenge,
crate::api::auth::nostr_link,
crate::api::website::website_challenge,
crate::api::website::website_verify,
crate::api::users::set_username,
crate::api::users::get_my_username,
crate::api::users::lookup_username,
crate::api::users::register_key,
crate::api::users::get_user_keys,
crate::api::users::get_user_key_for_asset,
crate::api::wallet::btc_ltc::balance,
crate::api::wallet::btc_ltc::utxos,
crate::api::wallet::btc_ltc::history,
crate::api::wallet::btc_ltc::balance_multi,
crate::api::wallet::btc_ltc::utxos_multi,
crate::api::wallet::btc_ltc::history_multi,
crate::api::wallet::btc_ltc::tip,
crate::api::wallet::btc_ltc::fee,
crate::api::wallet::btc_ltc::broadcast,
crate::api::wallet::xmr_wow::register,
crate::api::wallet::xmr_wow::provision_subaddrs,
crate::api::wallet::xmr_wow::balance,
crate::api::wallet::xmr_wow::history,
crate::api::wallet::xmr_wow::unspent_outs,
crate::api::wallet::xmr_wow::random_outs,
crate::api::wallet::xmr_wow::submit_tx,
crate::api::wallet::xmr_wow::height,
crate::api::wallet::xmr_wow::confirmations,
crate::api::wallet::grin::scan,
crate::api::wallet::grin::height,
crate::api::wallet::grin::broadcast,
crate::api::wallet::grin::address_user,
crate::api::wallet::grin_relay::create,
crate::api::wallet::grin_relay::pending,
crate::api::wallet::grin_relay::get_relay,
crate::api::wallet::grin_relay::respond,
crate::api::wallet::grin_relay::finalize,
crate::api::wallet::grin_relay::cancel,
crate::api::wallet::heights::heights,
crate::api::nip05::well_known_nostr,
crate::api::premium::invoice,
crate::api::premium::activate,
crate::api::premium::status,
crate::api::tips::create_social_tip,
crate::api::tips::get_sent_social_tips,
crate::api::tips::get_received_social_tips,
crate::api::tips::get_claimable_social_tips,
crate::api::tips::get_public_social_tip,
crate::api::tips::cancel_social_tip,
crate::api::tips::attach_funding,
crate::api::tips::claim_social_tip,
crate::api::tips::confirm_sweep,
crate::api::tips::clawback_social_tip,
),
components(schemas(
crate::api::health::HealthResponse,
crate::api::capabilities::CapabilitiesResponse,
crate::api::capabilities::ChainCapabilities,
crate::api::capabilities::ChainCapability,
crate::api::capabilities::FeatureCapabilities,
crate::api::capabilities::RestoreCapability,
crate::api::capabilities::RegistrationCapability,
crate::api::capabilities::MessagingCapability,
crate::api::capabilities::PremiumCapability,
crate::api::capabilities::PremiumPlanInfo,
crate::api::capabilities::PremiumRailInfo,
crate::api::capabilities::FeedCapability,
crate::api::prices::PricesResponse,
crate::api::auth::AuthResponse,
crate::api::auth::UserInfo,
crate::api::auth::AssetPublicKey,
crate::api::auth::ExtensionRegisterRequest,
crate::api::auth::CheckRestoreRequest,
crate::api::auth::CheckRestoreResponse,
crate::api::auth::PaymentInvoiceRequest,
crate::api::auth::PaymentInvoiceResponse,
crate::api::auth::RefreshTokenRequest,
crate::api::auth::LogoutRequest,
crate::api::auth::LogoutResponse,
crate::api::auth::NostrLinkChallengeResponse,
crate::api::auth::NostrLinkRequest,
crate::api::auth::NostrLinkResponse,
crate::api::auth::NostrRegisterRequest,
crate::api::website::WebsiteChallengeRequest,
crate::api::website::WebsiteChallengeResponse,
crate::api::website::AssetSignature,
crate::api::website::WebsiteVerifyRequest,
crate::api::users::SetUsernameRequest,
crate::api::users::SetUsernameResponse,
crate::api::users::MyUsernameResponse,
crate::api::users::PublicKeysInfo,
crate::api::users::LookupUsernameResponse,
crate::api::users::RegisterKeyRequest,
crate::api::users::UserKeyInfo,
crate::api::users::UserKeysResponse,
crate::api::wallet::btc_ltc::AddressRequest,
crate::api::wallet::btc_ltc::BalanceResponse,
crate::api::wallet::btc_ltc::Utxo,
crate::api::wallet::btc_ltc::UtxosResponse,
crate::api::wallet::btc_ltc::HistoryEntry,
crate::api::wallet::btc_ltc::HistoryResponse,
crate::api::wallet::btc_ltc::MultiAddressRequest,
crate::api::wallet::btc_ltc::MultiBalanceResponse,
crate::api::wallet::btc_ltc::TaggedUtxo,
crate::api::wallet::btc_ltc::MultiUtxosResponse,
crate::api::wallet::btc_ltc::TaggedHistoryEntry,
crate::api::wallet::btc_ltc::MultiHistoryResponse,
crate::api::wallet::btc_ltc::AssetRequest,
crate::api::wallet::btc_ltc::TipResponse,
crate::api::wallet::btc_ltc::FeeRequest,
crate::api::wallet::btc_ltc::FeeResponse,
crate::api::wallet::btc_ltc::BroadcastRequest,
crate::api::wallet::btc_ltc::BroadcastResponse,
crate::api::wallet::xmr_wow::ViewRequest,
crate::api::wallet::xmr_wow::RegisterRequest,
crate::api::wallet::xmr_wow::ProvisionRequest,
crate::api::wallet::xmr_wow::ProvisionResponse,
crate::api::wallet::xmr_wow::RandomOutsRequest,
crate::api::wallet::xmr_wow::SubmitRequest,
crate::api::wallet::xmr_wow::LwsAssetRequest,
crate::api::wallet::xmr_wow::ConfirmationsRequest,
crate::api::wallet::xmr_wow::LwsBalanceResponse,
crate::api::wallet::xmr_wow::SubaddrIndexDto,
crate::api::wallet::xmr_wow::SpentOutputDto,
crate::api::wallet::xmr_wow::TxDto,
crate::api::wallet::xmr_wow::LwsHistoryResponse,
crate::api::wallet::xmr_wow::UnspentOutputDto,
crate::api::wallet::xmr_wow::UnspentOutsResponse,
crate::api::wallet::xmr_wow::RandomOutputDto,
crate::api::wallet::xmr_wow::AmountOutsDto,
crate::api::wallet::xmr_wow::RandomOutsResponse,
crate::api::wallet::xmr_wow::HeightResponse,
crate::api::wallet::xmr_wow::ConfirmationsResponse,
crate::api::wallet::xmr_wow::OkResponse,
crate::api::wallet::grin::GrinScanRequest,
crate::api::wallet::grin::GrinOutput,
crate::api::wallet::grin::GrinScanResponse,
crate::api::wallet::grin::GrinHeightResponse,
crate::api::wallet::grin::GrinBroadcastRequest,
crate::api::wallet::grin::GrinBroadcastResponse,
crate::api::wallet::grin::GrinAddressUserResponse,
crate::api::wallet::grin_relay::CreateRelayRequest,
crate::api::wallet::grin_relay::RespondRelayRequest,
crate::api::wallet::grin_relay::FinalizeRelayRequest,
crate::api::wallet::grin_relay::SlateIdRequest,
crate::api::wallet::grin_relay::RelayEntry,
crate::api::wallet::grin_relay::PendingRelaysResponse,
crate::api::wallet::heights::HeightsResponse,
crate::api::nip05::WellKnownResponse,
crate::api::premium::InvoiceReq,
crate::api::premium::InvoiceResp,
crate::api::premium::ActivateReq,
crate::api::premium::StatusResp,
crate::api::tips::CreateSocialTipRequest,
crate::api::tips::CreateSocialTipResponse,
crate::api::tips::SentTip,
crate::api::tips::SocialTipsResponse,
crate::api::tips::PublicTipInfo,
crate::api::tips::CancelTipResponse,
crate::api::tips::AttachFundingRequest,
crate::api::tips::ClaimTipResponse,
crate::api::tips::ConfirmSweepRequest,
crate::api::tips::ConfirmSweepResponse,
crate::api::tips::ClawbackTipResponse,
)),
tags(
(name = "system", description = "Service health and metadata."),
(name = "prices", description = "Fiat price feed (cached; per-feed configurable; feature-flagged)."),
(name = "auth", description = "Wallet authentication: registration, restore, refresh, sessions, Nostr identity."),
(name = "btc_ltc", description = "Bitcoin/Litecoin chain access via Electrum (balance, UTXOs, history, tip, fee, broadcast)."),
(name = "xmr_wow", description = "Monero/Wownero chain access via LWS (register, balance, history, unspent/random outs, submit, height, confirmations)."),
(name = "grin", description = "Grin view-only chain access (rewind_hash scan, tip height, broadcast)."),
(name = "grin_relay", description = "Grin slatepack relay: async non-custodial mailbox for interactive Grin transfers (feature-flagged)."),
(name = "premium", description = "Premium subscription: mint a tier invoice, activate on settlement, read status (feature-flagged)."),
(name = "tips", description = "Public social tips: create/fund/claim/clawback share-URL tips (feature-flagged).")
)
)]
pub struct ApiDoc;
/// Registers the `bearer_auth` security scheme (HTTP Bearer / JWT). Routes that
/// resolve a session via `extract_user_id_from_token` annotate themselves with
/// `security(("bearer_auth" = []))`; public routes (auth bootstrap, lookups,
/// system) carry no `security` and are documented as open. Without this the
/// generated spec/client could not tell which endpoints require a token.
struct SecurityAddon;
impl utoipa::Modify for SecurityAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
use utoipa::openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme};
let components = openapi
.components
.as_mut()
.expect("components are registered by the derive");
components.add_security_scheme(
"bearer_auth",
SecurityScheme::Http(
HttpBuilder::new()
.scheme(HttpAuthScheme::Bearer)
.bearer_format("JWT")
.description(Some(
"Session access token issued by the /auth/* endpoints.",
))
.build(),
),
);
}
}