rust_x402/types/
facilitator.rs1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::HashMap;
6use std::sync::Arc;
7use std::time::Duration;
8
9pub type AuthHeadersFn =
11 dyn Fn() -> crate::Result<HashMap<String, HashMap<String, String>>> + Send + Sync;
12
13pub type AuthHeadersFnArc = Arc<AuthHeadersFn>;
15
16pub type AuthHeadersFnBox = Box<AuthHeadersFn>;
18
19#[derive(Clone)]
21pub struct FacilitatorConfig {
22 pub url: String,
24 pub timeout: Option<Duration>,
26 pub create_auth_headers: Option<AuthHeadersFnArc>,
28}
29
30impl std::fmt::Debug for FacilitatorConfig {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 f.debug_struct("FacilitatorConfig")
33 .field("url", &self.url)
34 .field("timeout", &self.timeout)
35 .field("create_auth_headers", &"<function>")
36 .finish()
37 }
38}
39
40impl FacilitatorConfig {
41 pub fn new(url: impl Into<String>) -> Self {
43 Self {
44 url: url.into(),
45 timeout: None,
46 create_auth_headers: None,
47 }
48 }
49
50 pub fn validate(&self) -> crate::Result<()> {
52 if self.url.is_empty() {
53 return Err(crate::X402Error::config("Facilitator URL cannot be empty"));
54 }
55
56 if !self.url.starts_with("http://") && !self.url.starts_with("https://") {
57 return Err(crate::X402Error::config(
58 "Facilitator URL must start with http:// or https://",
59 ));
60 }
61
62 Ok(())
63 }
64
65 pub fn with_timeout(mut self, timeout: Duration) -> Self {
67 self.timeout = Some(timeout);
68 self
69 }
70
71 pub fn with_auth_headers(mut self, creator: AuthHeadersFnBox) -> Self {
73 self.create_auth_headers = Some(Arc::from(creator));
74 self
75 }
76}
77
78impl Default for FacilitatorConfig {
79 fn default() -> Self {
80 Self::new("https://x402.org/facilitator")
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct VerifyResponse {
87 #[serde(rename = "isValid")]
89 pub is_valid: bool,
90 #[serde(rename = "invalidReason", skip_serializing_if = "Option::is_none")]
92 pub invalid_reason: Option<String>,
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub payer: Option<String>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct SettleResponse {
101 pub success: bool,
103 #[serde(rename = "errorReason", skip_serializing_if = "Option::is_none")]
105 pub error_reason: Option<String>,
106 pub transaction: String,
108 pub network: String,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub payer: Option<String>,
113}
114
115impl SettleResponse {
116 pub fn to_base64(&self) -> crate::Result<String> {
118 use base64::{engine::general_purpose, Engine as _};
119 let json = serde_json::to_string(self)?;
120 Ok(general_purpose::STANDARD.encode(json))
121 }
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct SupportedKinds {
127 pub kinds: Vec<SupportedKind>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct SupportedKind {
134 #[serde(rename = "x402Version")]
136 pub x402_version: u32,
137 pub scheme: String,
139 pub network: String,
141 #[serde(skip_serializing_if = "Option::is_none")]
143 pub metadata: Option<Value>,
144}