1use std::sync::Arc;
4
5use r402_facilitator::{DynFacilitator, Facilitator};
6use r402_protocol::extension::{Extension, ExtensionRegistry};
7use r402_protocol::network::{ChainId, ChainIdPattern};
8use r402_protocol::payment::{PaymentRequirements, SupportedResponse, V2};
9
10use crate::hooks::{
11 CancelReason, DynResourceServerHooks, PaymentHookContext, ResourceServerHooks,
12 VerifiedPaymentCanceledContext, WirePaymentPayload,
13};
14use crate::payment_flow::{
15 PaymentFlowError, PaymentFlowName, PaymentFlowScheme, ResolvedPaymentFlow, SettlePhase,
16 resolve_payment_flow,
17};
18use crate::scheme::{DynSchemeNetworkServer, FacilitatorSupportError, SchemeNetworkServer};
19
20pub struct ResourceServer {
22 pub(crate) facilitator: Arc<dyn DynFacilitator>,
23 pub(crate) hooks: Vec<Arc<dyn DynResourceServerHooks>>,
24 pub(crate) schemes: Vec<(ChainIdPattern, Arc<dyn DynSchemeNetworkServer>)>,
25 pub(crate) extensions: ExtensionRegistry,
26}
27
28impl Clone for ResourceServer {
29 fn clone(&self) -> Self {
30 Self {
31 facilitator: Arc::clone(&self.facilitator),
32 hooks: self.hooks.clone(),
33 schemes: self.schemes.clone(),
34 extensions: self.extensions.clone(),
35 }
36 }
37}
38
39impl std::fmt::Debug for ResourceServer {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 f.debug_struct("ResourceServer")
42 .field("hooks", &self.hooks.len())
43 .field("schemes", &self.schemes.len())
44 .field("extensions", &self.extensions.len())
45 .finish_non_exhaustive()
46 }
47}
48
49impl ResourceServer {
50 #[must_use]
52 pub fn new<F>(facilitator: Arc<F>) -> Self
53 where
54 F: Facilitator + 'static,
55 {
56 let erased: Arc<dyn DynFacilitator> = facilitator;
57 Self {
58 facilitator: erased,
59 hooks: Vec::new(),
60 schemes: Vec::new(),
61 extensions: ExtensionRegistry::new(),
62 }
63 }
64
65 #[must_use]
67 pub fn from_dyn(facilitator: Arc<dyn DynFacilitator>) -> Self {
68 Self {
69 facilitator,
70 hooks: Vec::new(),
71 schemes: Vec::new(),
72 extensions: ExtensionRegistry::new(),
73 }
74 }
75
76 #[must_use]
78 pub fn with_hook(mut self, hook: impl ResourceServerHooks + 'static) -> Self {
79 self.hooks.push(Arc::new(hook));
80 self
81 }
82
83 pub fn add_hook(&mut self, hook: impl ResourceServerHooks + 'static) {
85 self.hooks.push(Arc::new(hook));
86 }
87
88 #[must_use]
90 pub fn hook_count(&self) -> usize {
91 self.hooks.len()
92 }
93
94 #[must_use]
96 pub fn facilitator(&self) -> Arc<dyn DynFacilitator> {
97 Arc::clone(&self.facilitator)
98 }
99
100 #[must_use]
102 pub fn with_scheme(
103 mut self,
104 network: ChainIdPattern,
105 scheme: impl SchemeNetworkServer + 'static,
106 ) -> Self {
107 self.register_scheme(network, scheme);
108 self
109 }
110
111 pub fn register_scheme(
113 &mut self,
114 network: ChainIdPattern,
115 scheme: impl SchemeNetworkServer + 'static,
116 ) {
117 self.schemes.push((network, Arc::new(scheme)));
118 }
119
120 #[must_use]
124 pub fn registered_scheme(
125 &self,
126 scheme: &str,
127 network: &ChainId,
128 ) -> Option<&dyn DynSchemeNetworkServer> {
129 let mut patterned: Option<&dyn DynSchemeNetworkServer> = None;
130 for (pattern, server) in &self.schemes {
131 if server.scheme() != scheme || !pattern.matches(network) {
132 continue;
133 }
134 if matches!(pattern, ChainIdPattern::Exact { .. }) {
135 return Some(server.as_ref());
136 }
137 if patterned.is_none() {
138 patterned = Some(server.as_ref());
139 }
140 }
141 patterned
142 }
143
144 pub(crate) fn resolved_payment_flow(
151 &self,
152 requirements: &PaymentRequirements,
153 ) -> Result<ResolvedPaymentFlow, PaymentFlowError> {
154 let Some(scheme) =
155 self.registered_scheme(requirements.scheme.as_str(), &requirements.network)
156 else {
157 return Err(PaymentFlowError::UnregisteredScheme {
158 scheme: requirements.scheme.to_string(),
159 network: requirements.network.to_string(),
160 });
161 };
162 resolve_payment_flow(
163 &PaymentFlowScheme {
164 scheme: scheme.scheme(),
165 default_asset_transfer_method: scheme.default_asset_transfer_method(),
166 payment_flows: scheme.payment_flows(),
167 },
168 requirements,
169 )
170 }
171
172 pub fn get_payment_flow(
179 &self,
180 requirements: &PaymentRequirements,
181 ) -> Result<PaymentFlowName, PaymentFlowError> {
182 Ok(self.resolved_payment_flow(requirements)?.payment_flow)
183 }
184
185 pub async fn has_settle_on_cancel(&self, requirements: &PaymentRequirements) -> bool {
189 let Some(scheme) =
190 self.registered_scheme(requirements.scheme.as_str(), &requirements.network)
191 else {
192 return false;
193 };
194 let ctx = VerifiedPaymentCanceledContext {
195 payment: PaymentHookContext {
196 payload: WirePaymentPayload::new(requirements.clone(), serde_json::Value::Null),
197 requirements: requirements.clone(),
198 },
199 reason: CancelReason::HandlerFailed,
200 error: None,
201 response_status: None,
202 settled_phases: vec![SettlePhase::BeforeHandler],
203 };
204 scheme.settle_on_cancel(&ctx).await.is_some()
205 }
206
207 #[must_use]
209 pub fn with_extension(mut self, extension: impl Extension + 'static) -> Self {
210 self.extensions.register(extension);
211 self
212 }
213}
214
215pub fn validate_accepts_against_supported(
225 server: &ResourceServer,
226 accepts: &[PaymentRequirements],
227 supported: &SupportedResponse,
228) -> Result<(), FacilitatorSupportError> {
229 for requirements in accepts {
230 let scheme = requirements.scheme.as_str();
231 let network = requirements.network.to_string();
232 let kind = supported.kinds.iter().find(|kind| {
233 V2 == kind.x402_version
234 && kind.scheme.as_str() == scheme
235 && kind.network.as_str() == network
236 });
237 let Some(kind) = kind else {
238 return Err(FacilitatorSupportError::KindMissing {
239 scheme: requirements.scheme.clone(),
240 network: requirements.network.clone(),
241 });
242 };
243 let Some(registered) = server.registered_scheme(scheme, &requirements.network) else {
244 continue;
245 };
246 registered.validate_facilitator_support(
247 &requirements.network,
248 kind,
249 &supported.extensions,
250 )?;
251 }
252 Ok(())
253}