1use std::collections::HashMap;
4use std::future::Future;
5
6use compact_str::CompactString;
7use r402_facilitator::BoxFuture;
8use r402_protocol::error::FacilitatorError;
9use r402_protocol::network::ChainId;
10use r402_protocol::payment::{
11 PaymentRequired, PaymentRequirements, ResourceInfo, SupportedPaymentKind, SupportedResponse,
12};
13use serde_json::{Map, Value};
14
15use crate::hooks::{
16 SettleContext, SettleResultContext, VerifiedPaymentCanceledContext, WirePaymentPayload,
17};
18use crate::payment_flow::PaymentFlowConfig;
19
20#[derive(Debug)]
22#[non_exhaustive]
23pub struct SchemePaymentRequiredContext<'a> {
24 pub requirements: &'a [PaymentRequirements],
26 pub payment_payload: Option<&'a WirePaymentPayload>,
28 pub resource: &'a ResourceInfo,
30 pub error: Option<&'a str>,
32 pub payment_required_response: &'a PaymentRequired,
34 pub supported: &'a SupportedResponse,
36 pub network: &'a ChainId,
38}
39
40impl<'a> SchemePaymentRequiredContext<'a> {
41 #[must_use]
43 pub const fn new(
44 requirements: &'a [PaymentRequirements],
45 resource: &'a ResourceInfo,
46 payment_required_response: &'a PaymentRequired,
47 supported: &'a SupportedResponse,
48 network: &'a ChainId,
49 ) -> Self {
50 Self {
51 requirements,
52 payment_payload: None,
53 resource,
54 error: None,
55 payment_required_response,
56 supported,
57 network,
58 }
59 }
60}
61
62pub trait SchemeNetworkServer: Send + Sync {
66 fn scheme(&self) -> &str;
68
69 fn default_asset_transfer_method(&self) -> &str;
71
72 fn payment_flows(&self) -> &HashMap<String, PaymentFlowConfig>;
74
75 fn dynamic_extra_fields(&self) -> &[&str] {
77 &[]
78 }
79
80 fn enrich_payment_required_response<'a>(
82 &'a self,
83 _ctx: &'a SchemePaymentRequiredContext<'a>,
84 ) -> impl Future<Output = Option<Vec<PaymentRequirements>>> + Send + 'a {
85 async { None }
86 }
87
88 fn enrich_settlement_payload<'a>(
93 &'a self,
94 _ctx: &'a SettleContext,
95 ) -> impl Future<Output = Result<Option<Map<String, Value>>, FacilitatorError>> + Send + 'a
96 {
97 async { Ok(None) }
98 }
99
100 fn enrich_settlement_response<'a>(
102 &'a self,
103 _ctx: &'a SettleResultContext,
104 ) -> impl Future<Output = Option<Map<String, Value>>> + Send + 'a {
105 async { None }
106 }
107
108 fn settle_on_cancel<'a>(
112 &'a self,
113 _ctx: &'a VerifiedPaymentCanceledContext,
114 ) -> impl Future<Output = Option<PaymentRequirements>> + Send + 'a {
115 async { None }
116 }
117
118 fn settles_on_cancel(&self) -> bool {
122 false
123 }
124
125 fn validate_facilitator_support(
131 &self,
132 network: &ChainId,
133 kind: &SupportedPaymentKind,
134 facilitator_extensions: &[CompactString],
135 ) -> Result<(), FacilitatorSupportError> {
136 let _ = (network, kind, facilitator_extensions);
137 Ok(())
138 }
139}
140
141#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
143pub enum FacilitatorSupportError {
144 #[error("{scheme} on {network}: facilitator does not advertise this kind")]
146 KindMissing {
147 scheme: CompactString,
149 network: ChainId,
151 },
152 #[error("{scheme} on {network}: missing extra.feePayer")]
154 MissingFeePayer {
155 scheme: CompactString,
157 network: ChainId,
159 },
160 #[error("{scheme} on {network}: invalid extra.feePayer")]
162 InvalidFeePayer {
163 scheme: CompactString,
165 network: ChainId,
167 },
168 #[error("{scheme} on {network}: missing extra.receiverAuthorizer")]
170 MissingReceiverAuthorizer {
171 scheme: CompactString,
173 network: ChainId,
175 },
176 #[error("{scheme} on {network}: extra.receiverAuthorizer is the zero address")]
178 ZeroReceiverAuthorizer {
179 scheme: CompactString,
181 network: ChainId,
183 },
184 #[error("{scheme} on {network}: invalid extra.receiverAuthorizer")]
186 InvalidReceiverAuthorizer {
187 scheme: CompactString,
189 network: ChainId,
191 },
192}
193
194impl FacilitatorSupportError {
195 #[must_use]
197 pub const fn reason(&self) -> &'static str {
198 match self {
199 Self::KindMissing { .. } => "kind_missing",
200 Self::MissingFeePayer { .. } => "missing_fee_payer",
201 Self::InvalidFeePayer { .. } => "invalid_fee_payer",
202 Self::MissingReceiverAuthorizer { .. } => "missing_receiver_authorizer",
203 Self::ZeroReceiverAuthorizer { .. } => "zero_receiver_authorizer",
204 Self::InvalidReceiverAuthorizer { .. } => "invalid_receiver_authorizer",
205 }
206 }
207}
208
209pub trait DynSchemeNetworkServer: Send + Sync {
211 fn scheme(&self) -> &str;
213
214 fn default_asset_transfer_method(&self) -> &str;
216
217 fn payment_flows(&self) -> &HashMap<String, PaymentFlowConfig>;
219
220 fn dynamic_extra_fields(&self) -> &[&str];
222
223 fn enrich_payment_required_response<'a>(
225 &'a self,
226 ctx: &'a SchemePaymentRequiredContext<'a>,
227 ) -> BoxFuture<'a, Option<Vec<PaymentRequirements>>>;
228
229 fn enrich_settlement_payload<'a>(
231 &'a self,
232 ctx: &'a SettleContext,
233 ) -> BoxFuture<'a, Result<Option<Map<String, Value>>, FacilitatorError>>;
234
235 fn enrich_settlement_response<'a>(
237 &'a self,
238 ctx: &'a SettleResultContext,
239 ) -> BoxFuture<'a, Option<Map<String, Value>>>;
240
241 fn settle_on_cancel<'a>(
243 &'a self,
244 ctx: &'a VerifiedPaymentCanceledContext,
245 ) -> BoxFuture<'a, Option<PaymentRequirements>>;
246
247 fn settles_on_cancel(&self) -> bool;
249
250 fn validate_facilitator_support(
256 &self,
257 network: &ChainId,
258 kind: &SupportedPaymentKind,
259 facilitator_extensions: &[CompactString],
260 ) -> Result<(), FacilitatorSupportError>;
261}
262
263impl<T: SchemeNetworkServer + ?Sized> DynSchemeNetworkServer for T {
264 fn scheme(&self) -> &str {
265 <Self as SchemeNetworkServer>::scheme(self)
266 }
267
268 fn default_asset_transfer_method(&self) -> &str {
269 <Self as SchemeNetworkServer>::default_asset_transfer_method(self)
270 }
271
272 fn payment_flows(&self) -> &HashMap<String, PaymentFlowConfig> {
273 <Self as SchemeNetworkServer>::payment_flows(self)
274 }
275
276 fn dynamic_extra_fields(&self) -> &[&str] {
277 <Self as SchemeNetworkServer>::dynamic_extra_fields(self)
278 }
279
280 fn enrich_payment_required_response<'a>(
281 &'a self,
282 ctx: &'a SchemePaymentRequiredContext<'a>,
283 ) -> BoxFuture<'a, Option<Vec<PaymentRequirements>>> {
284 Box::pin(<Self as SchemeNetworkServer>::enrich_payment_required_response(self, ctx))
285 }
286
287 fn enrich_settlement_payload<'a>(
288 &'a self,
289 ctx: &'a SettleContext,
290 ) -> BoxFuture<'a, Result<Option<Map<String, Value>>, FacilitatorError>> {
291 Box::pin(<Self as SchemeNetworkServer>::enrich_settlement_payload(
292 self, ctx,
293 ))
294 }
295
296 fn enrich_settlement_response<'a>(
297 &'a self,
298 ctx: &'a SettleResultContext,
299 ) -> BoxFuture<'a, Option<Map<String, Value>>> {
300 Box::pin(<Self as SchemeNetworkServer>::enrich_settlement_response(
301 self, ctx,
302 ))
303 }
304
305 fn settle_on_cancel<'a>(
306 &'a self,
307 ctx: &'a VerifiedPaymentCanceledContext,
308 ) -> BoxFuture<'a, Option<PaymentRequirements>> {
309 Box::pin(<Self as SchemeNetworkServer>::settle_on_cancel(self, ctx))
310 }
311
312 fn settles_on_cancel(&self) -> bool {
313 <Self as SchemeNetworkServer>::settles_on_cancel(self)
314 }
315
316 fn validate_facilitator_support(
317 &self,
318 network: &ChainId,
319 kind: &SupportedPaymentKind,
320 facilitator_extensions: &[CompactString],
321 ) -> Result<(), FacilitatorSupportError> {
322 <Self as SchemeNetworkServer>::validate_facilitator_support(
323 self,
324 network,
325 kind,
326 facilitator_extensions,
327 )
328 }
329}