Skip to main content

r402_core/scheme/
server.rs

1//! Server-side scheme abstractions.
2//!
3//! A resource server (seller) uses a [`SchemeServer`] to translate
4//! human-readable prices like `"$0.01"` into a full
5//! [`PaymentRequirements`] with scheme-specific `extra` data filled in.
6
7use compact_str::CompactString;
8
9use crate::chain::ChainId;
10use crate::scheme::sealed::Sealed;
11use crate::wire::PaymentRequirements;
12
13/// A resolved asset amount ready for insertion into
14/// [`PaymentRequirements`].
15#[derive(Debug, Clone)]
16pub struct AssetAmount {
17    /// Token asset address / mint (wire-level string).
18    pub asset: CompactString,
19    /// Amount in the token's smallest unit, stringified.
20    pub amount: CompactString,
21}
22
23/// Errors emitted by a [`SchemeServer`].
24#[derive(Debug, thiserror::Error)]
25#[non_exhaustive]
26pub enum SchemeServerError {
27    /// The price could not be parsed.
28    #[error("invalid price: {0}")]
29    InvalidPrice(String),
30    /// The chain or asset is not configured.
31    #[error("unsupported chain or asset: {0}")]
32    UnsupportedChain(String),
33    /// Any other server-side failure.
34    #[error("{0}")]
35    Other(String),
36}
37
38/// Server-side scheme interface.
39///
40/// Sealed: only crates inside this workspace may implement it.
41pub trait SchemeServer: super::SchemeId + Sealed + Send + Sync {
42    /// Parses a human-readable price into the scheme's internal amount form.
43    ///
44    /// # Errors
45    ///
46    /// Returns [`SchemeServerError`] when the price or chain is not supported.
47    fn parse_price(&self, price: &str, network: &ChainId)
48    -> Result<AssetAmount, SchemeServerError>;
49
50    /// Fills in scheme-specific `extra` data on the requirements.
51    ///
52    /// Default implementation returns the input unchanged.
53    fn enhance_requirements(&self, requirements: PaymentRequirements) -> PaymentRequirements {
54        requirements
55    }
56
57    /// Builds a complete [`PaymentRequirements`] from a price.
58    ///
59    /// # Errors
60    ///
61    /// Returns [`SchemeServerError`] when the price cannot be resolved.
62    fn build_requirements(
63        &self,
64        price: &str,
65        network: &ChainId,
66        pay_to: &str,
67        max_timeout_seconds: u64,
68    ) -> Result<PaymentRequirements, SchemeServerError> {
69        let AssetAmount { asset, amount } = self.parse_price(price, network)?;
70        let base = PaymentRequirements {
71            scheme: CompactString::from(self.scheme()),
72            network: network.clone(),
73            amount,
74            pay_to: CompactString::from(pay_to),
75            max_timeout_seconds,
76            asset,
77            extra: None,
78        };
79        Ok(self.enhance_requirements(base))
80    }
81}