1use std::time::Duration;
14
15use r402::proto::{
16 PaymentPayload, PaymentRequirements, SettleResponse, SupportedResponse, VerifyResponse,
17};
18use r402::scheme::{BoxFuture, SchemeError};
19use r402::server::FacilitatorClient;
20use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
21use serde::Serialize;
22
23use crate::constants::DEFAULT_FACILITATOR_URL;
24
25#[derive(Debug, Clone, Default)]
29pub struct AuthHeaders {
30 pub verify: HeaderMap,
32 pub settle: HeaderMap,
34 pub supported: HeaderMap,
36}
37
38pub trait AuthProvider: Send + Sync {
45 fn get_auth_headers(&self) -> AuthHeaders;
47}
48
49#[derive(Debug, Clone)]
54pub struct StaticAuthProvider {
55 headers: HeaderMap,
56}
57
58impl StaticAuthProvider {
59 #[must_use]
61 pub fn new(headers: HeaderMap) -> Self {
62 Self { headers }
63 }
64
65 #[must_use]
71 pub fn bearer(token: &str) -> Self {
72 let mut headers = HeaderMap::new();
73 let value = HeaderValue::from_str(&format!("Bearer {token}")).expect("valid bearer token");
74 headers.insert(reqwest::header::AUTHORIZATION, value);
75 Self { headers }
76 }
77}
78
79impl AuthProvider for StaticAuthProvider {
80 fn get_auth_headers(&self) -> AuthHeaders {
81 AuthHeaders {
82 verify: self.headers.clone(),
83 settle: self.headers.clone(),
84 supported: self.headers.clone(),
85 }
86 }
87}
88
89pub struct CallbackAuthProvider<F> {
96 create_headers: F,
97}
98
99impl<F> std::fmt::Debug for CallbackAuthProvider<F> {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 f.debug_struct("CallbackAuthProvider")
102 .finish_non_exhaustive()
103 }
104}
105
106impl<F> CallbackAuthProvider<F>
107where
108 F: Fn() -> AuthHeaders + Send + Sync,
109{
110 pub fn new(create_headers: F) -> Self {
112 Self { create_headers }
113 }
114}
115
116impl<F> AuthProvider for CallbackAuthProvider<F>
117where
118 F: Fn() -> AuthHeaders + Send + Sync,
119{
120 fn get_auth_headers(&self) -> AuthHeaders {
121 (self.create_headers)()
122 }
123}
124
125pub struct FacilitatorConfig {
130 pub url: String,
132
133 pub timeout: Duration,
135
136 pub auth_provider: Option<Box<dyn AuthProvider>>,
138
139 pub http_client: Option<reqwest::Client>,
142
143 pub identifier: Option<String>,
146}
147
148impl Default for FacilitatorConfig {
149 fn default() -> Self {
150 Self {
151 url: DEFAULT_FACILITATOR_URL.to_owned(),
152 timeout: Duration::from_secs(30),
153 auth_provider: None,
154 http_client: None,
155 identifier: None,
156 }
157 }
158}
159
160impl FacilitatorConfig {
161 #[must_use]
163 pub fn new(url: impl Into<String>) -> Self {
164 Self {
165 url: url.into(),
166 ..Self::default()
167 }
168 }
169
170 #[must_use]
172 pub fn with_timeout(mut self, timeout: Duration) -> Self {
173 self.timeout = timeout;
174 self
175 }
176
177 #[must_use]
179 pub fn with_auth(mut self, provider: impl AuthProvider + 'static) -> Self {
180 self.auth_provider = Some(Box::new(provider));
181 self
182 }
183
184 #[must_use]
186 pub fn with_http_client(mut self, client: reqwest::Client) -> Self {
187 self.http_client = Some(client);
188 self
189 }
190
191 #[must_use]
193 pub fn with_identifier(mut self, id: impl Into<String>) -> Self {
194 self.identifier = Some(id.into());
195 self
196 }
197}
198
199impl std::fmt::Debug for FacilitatorConfig {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 f.debug_struct("FacilitatorConfig")
202 .field("url", &self.url)
203 .field("timeout", &self.timeout)
204 .field("has_auth_provider", &self.auth_provider.is_some())
205 .field("has_http_client", &self.http_client.is_some())
206 .field("identifier", &self.identifier)
207 .finish()
208 }
209}
210
211#[derive(Debug, Serialize)]
213#[serde(rename_all = "camelCase")]
214struct FacilitatorRequestBody {
215 x402_version: u32,
216 payment_payload: serde_json::Value,
217 payment_requirements: serde_json::Value,
218}
219
220pub struct HttpFacilitatorClient {
238 url: String,
239 identifier: String,
240 auth_provider: Option<Box<dyn AuthProvider>>,
241 client: reqwest::Client,
242}
243
244impl HttpFacilitatorClient {
245 pub fn new(config: FacilitatorConfig) -> Self {
247 let url = config.url.trim_end_matches('/').to_owned();
248 let identifier = config.identifier.unwrap_or_else(|| url.clone());
249
250 let client = config.http_client.unwrap_or_else(|| {
251 reqwest::Client::builder()
252 .timeout(config.timeout)
253 .redirect(reqwest::redirect::Policy::limited(10))
254 .build()
255 .expect("failed to build reqwest::Client")
256 });
257
258 Self {
259 url,
260 identifier,
261 auth_provider: config.auth_provider,
262 client,
263 }
264 }
265
266 #[must_use]
268 pub fn default_cdp() -> Self {
269 Self::new(FacilitatorConfig::default())
270 }
271
272 #[must_use]
274 pub fn url(&self) -> &str {
275 &self.url
276 }
277
278 #[must_use]
280 pub fn identifier(&self) -> &str {
281 &self.identifier
282 }
283
284 fn verify_headers(&self) -> HeaderMap {
286 let mut headers = HeaderMap::new();
287 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
288 if let Some(auth) = &self.auth_provider {
289 let auth_headers = auth.get_auth_headers();
290 headers.extend(auth_headers.verify);
291 }
292 headers
293 }
294
295 fn settle_headers(&self) -> HeaderMap {
297 let mut headers = HeaderMap::new();
298 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
299 if let Some(auth) = &self.auth_provider {
300 let auth_headers = auth.get_auth_headers();
301 headers.extend(auth_headers.settle);
302 }
303 headers
304 }
305
306 fn supported_headers(&self) -> HeaderMap {
308 let mut headers = HeaderMap::new();
309 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
310 if let Some(auth) = &self.auth_provider {
311 let auth_headers = auth.get_auth_headers();
312 headers.extend(auth_headers.supported);
313 }
314 headers
315 }
316
317 fn build_request_body(
319 version: u32,
320 payload: &serde_json::Value,
321 requirements: &serde_json::Value,
322 ) -> FacilitatorRequestBody {
323 FacilitatorRequestBody {
324 x402_version: version,
325 payment_payload: payload.clone(),
326 payment_requirements: requirements.clone(),
327 }
328 }
329
330 async fn verify_http(
332 &self,
333 version: u32,
334 payload_value: serde_json::Value,
335 requirements_value: serde_json::Value,
336 ) -> Result<VerifyResponse, SchemeError> {
337 let body = Self::build_request_body(version, &payload_value, &requirements_value);
338
339 let response = self
340 .client
341 .post(format!("{}/verify", self.url))
342 .headers(self.verify_headers())
343 .json(&body)
344 .send()
345 .await
346 .map_err(|e| -> SchemeError {
347 format!("Facilitator verify request failed: {e}").into()
348 })?;
349
350 let status = response.status();
351 if !status.is_success() {
352 let text = response.text().await.unwrap_or_default();
353 return Err(format!("Facilitator verify failed ({status}): {text}").into());
354 }
355
356 let result: VerifyResponse = response.json().await.map_err(|e| -> SchemeError {
357 format!("Facilitator verify response parse error: {e}").into()
358 })?;
359
360 Ok(result)
361 }
362
363 async fn settle_http(
365 &self,
366 version: u32,
367 payload_value: serde_json::Value,
368 requirements_value: serde_json::Value,
369 ) -> Result<SettleResponse, SchemeError> {
370 let body = Self::build_request_body(version, &payload_value, &requirements_value);
371
372 let response = self
373 .client
374 .post(format!("{}/settle", self.url))
375 .headers(self.settle_headers())
376 .json(&body)
377 .send()
378 .await
379 .map_err(|e| -> SchemeError {
380 format!("Facilitator settle request failed: {e}").into()
381 })?;
382
383 let status = response.status();
384 if !status.is_success() {
385 let text = response.text().await.unwrap_or_default();
386 return Err(format!("Facilitator settle failed ({status}): {text}").into());
387 }
388
389 let result: SettleResponse = response.json().await.map_err(|e| -> SchemeError {
390 format!("Facilitator settle response parse error: {e}").into()
391 })?;
392
393 Ok(result)
394 }
395
396 pub async fn verify_from_bytes(
404 &self,
405 payload_bytes: &[u8],
406 requirements_bytes: &[u8],
407 ) -> Result<VerifyResponse, SchemeError> {
408 let version = r402::proto::helpers::detect_version_bytes(payload_bytes)
409 .map_err(|e| -> SchemeError { e.to_string().into() })?;
410 let payload_value: serde_json::Value = serde_json::from_slice(payload_bytes)
411 .map_err(|e| -> SchemeError { e.to_string().into() })?;
412 let requirements_value: serde_json::Value = serde_json::from_slice(requirements_bytes)
413 .map_err(|e| -> SchemeError { e.to_string().into() })?;
414
415 self.verify_http(version, payload_value, requirements_value)
416 .await
417 }
418
419 pub async fn settle_from_bytes(
427 &self,
428 payload_bytes: &[u8],
429 requirements_bytes: &[u8],
430 ) -> Result<SettleResponse, SchemeError> {
431 let version = r402::proto::helpers::detect_version_bytes(payload_bytes)
432 .map_err(|e| -> SchemeError { e.to_string().into() })?;
433 let payload_value: serde_json::Value = serde_json::from_slice(payload_bytes)
434 .map_err(|e| -> SchemeError { e.to_string().into() })?;
435 let requirements_value: serde_json::Value = serde_json::from_slice(requirements_bytes)
436 .map_err(|e| -> SchemeError { e.to_string().into() })?;
437
438 self.settle_http(version, payload_value, requirements_value)
439 .await
440 }
441}
442
443impl std::fmt::Debug for HttpFacilitatorClient {
444 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445 f.debug_struct("HttpFacilitatorClient")
446 .field("url", &self.url)
447 .field("identifier", &self.identifier)
448 .field("has_auth_provider", &self.auth_provider.is_some())
449 .finish_non_exhaustive()
450 }
451}
452
453impl FacilitatorClient for HttpFacilitatorClient {
454 fn verify<'a>(
455 &'a self,
456 payload: &'a PaymentPayload,
457 requirements: &'a PaymentRequirements,
458 ) -> BoxFuture<'a, Result<VerifyResponse, SchemeError>> {
459 Box::pin(async move {
460 let payload_value = serde_json::to_value(payload)
461 .map_err(|e| -> SchemeError { format!("Serialize payload: {e}").into() })?;
462 let requirements_value = serde_json::to_value(requirements)
463 .map_err(|e| -> SchemeError { format!("Serialize requirements: {e}").into() })?;
464
465 self.verify_http(2, payload_value, requirements_value).await
466 })
467 }
468
469 fn settle<'a>(
470 &'a self,
471 payload: &'a PaymentPayload,
472 requirements: &'a PaymentRequirements,
473 ) -> BoxFuture<'a, Result<SettleResponse, SchemeError>> {
474 Box::pin(async move {
475 let payload_value = serde_json::to_value(payload)
476 .map_err(|e| -> SchemeError { format!("Serialize payload: {e}").into() })?;
477 let requirements_value = serde_json::to_value(requirements)
478 .map_err(|e| -> SchemeError { format!("Serialize requirements: {e}").into() })?;
479
480 self.settle_http(2, payload_value, requirements_value).await
481 })
482 }
483
484 fn get_supported(&self) -> BoxFuture<'_, Result<SupportedResponse, SchemeError>> {
485 Box::pin(async move {
486 let response = self
487 .client
488 .get(format!("{}/supported", self.url))
489 .headers(self.supported_headers())
490 .send()
491 .await
492 .map_err(|e| -> SchemeError {
493 format!("Facilitator get_supported request failed: {e}").into()
494 })?;
495
496 let status = response.status();
497 if !status.is_success() {
498 let text = response.text().await.unwrap_or_default();
499 return Err(format!("Facilitator get_supported failed ({status}): {text}").into());
500 }
501
502 let result: SupportedResponse = response.json().await.map_err(|e| -> SchemeError {
503 format!("Facilitator get_supported response parse error: {e}").into()
504 })?;
505
506 Ok(result)
507 })
508 }
509}