x402_kit/
facilitator_client.rs

1use http::{HeaderMap, HeaderName, HeaderValue};
2use serde::{Deserialize, Serialize};
3use url::Url;
4
5use crate::{
6    facilitator::{
7        Facilitator, PaymentRequest, SettleFailed, SettleResult, SettleSuccess, SupportedResponse,
8        VerifyInvalid, VerifyResult, VerifyValid,
9    },
10    transport::{PaymentPayload, PaymentRequirements},
11};
12
13/// A remote facilitator client that communicates over HTTP.
14///
15/// You can customize the request and response types for verification and settlement
16///
17/// # Type Parameters
18///
19/// - `VReq`: The request type for verification, must be convertible from `FacilitatorPaymentRequest` and serializable.
20/// - `VRes`: The response type for verification, must be convertible into `FacilitatorVerifyResponse` and deserializable.
21/// - `SReq`: The request type for settlement, must be convertible from `FacilitatorPaymentRequest` and serializable.
22/// - `SRes`: The response type for settlement, must be convertible into `FacilitatorSettleResponse` and deserializable.
23#[derive(Debug, Clone)]
24pub struct FacilitatorClient<VReq, VRes, SReq, SRes>
25where
26    VReq: From<PaymentRequest> + Serialize,
27    VRes: IntoVerifyResponse + for<'de> Deserialize<'de>,
28    SReq: From<PaymentRequest> + Serialize,
29    SRes: IntoSettleResponse + for<'de> Deserialize<'de>,
30{
31    pub base_url: Url,
32    pub client: reqwest::Client,
33    pub supported_headers: HeaderMap,
34    pub verify_headers: HeaderMap,
35    pub settle_headers: HeaderMap,
36    pub _phantom: std::marker::PhantomData<(VReq, VRes, SReq, SRes)>,
37}
38
39pub trait IntoVerifyResponse {
40    fn into_verify_response(self) -> VerifyResult;
41}
42
43pub trait IntoSettleResponse {
44    fn into_settle_response(self) -> SettleResult;
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(rename_all = "camelCase")]
49pub struct DefaultPaymentRequest {
50    pub payment_payload: PaymentPayload,
51    pub payment_requirements: PaymentRequirements,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct DefaultVerifyResponse {
57    pub is_valid: bool,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub invalid_reason: Option<String>,
60    pub payer: Option<String>,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct DefaultSettleResponse {
66    pub success: bool,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub error_reason: Option<String>,
69    pub payer: Option<String>,
70    pub transaction: Option<String>,
71    pub network: Option<String>,
72}
73
74impl From<PaymentRequest> for DefaultPaymentRequest {
75    fn from(request: PaymentRequest) -> Self {
76        DefaultPaymentRequest {
77            payment_payload: request.payment_payload,
78            payment_requirements: request.payment_requirements,
79        }
80    }
81}
82
83impl IntoVerifyResponse for DefaultVerifyResponse {
84    fn into_verify_response(self) -> VerifyResult {
85        if self.is_valid {
86            VerifyResult::valid(VerifyValid {
87                payer: self.payer.unwrap_or_default(),
88            })
89        } else {
90            VerifyResult::invalid(VerifyInvalid {
91                invalid_reason: self.invalid_reason.unwrap_or_default(),
92                payer: self.payer,
93            })
94        }
95    }
96}
97
98impl IntoSettleResponse for DefaultSettleResponse {
99    fn into_settle_response(self) -> SettleResult {
100        if self.success {
101            SettleResult::success(SettleSuccess {
102                payer: self.payer.unwrap_or_default(),
103                transaction: self.transaction.unwrap_or_default(),
104                network: self.network.unwrap_or_default(),
105            })
106        } else {
107            SettleResult::failed(SettleFailed {
108                error_reason: self.error_reason.unwrap_or_default(),
109                payer: self.payer,
110            })
111        }
112    }
113}
114
115/// A type alias for a RemoteFacilitatorClient using the default request and response types.
116pub type StandardFacilitatorClient = FacilitatorClient<
117    DefaultPaymentRequest,
118    DefaultVerifyResponse,
119    DefaultPaymentRequest,
120    DefaultSettleResponse,
121>;
122
123impl<VReq, VRes, SReq, SRes> FacilitatorClient<VReq, VRes, SReq, SRes>
124where
125    VReq: From<PaymentRequest> + Serialize,
126    VRes: IntoVerifyResponse + for<'de> Deserialize<'de>,
127    SReq: From<PaymentRequest> + Serialize,
128    SRes: IntoSettleResponse + for<'de> Deserialize<'de>,
129{
130    pub fn new_from_url(base_url: Url) -> Self {
131        FacilitatorClient {
132            base_url,
133            client: reqwest::Client::new(),
134            supported_headers: HeaderMap::new(),
135            verify_headers: HeaderMap::new(),
136            settle_headers: HeaderMap::new(),
137            _phantom: std::marker::PhantomData,
138        }
139    }
140
141    pub fn with_verify_request_type<NewVReq>(self) -> FacilitatorClient<NewVReq, VRes, SReq, SRes>
142    where
143        NewVReq: From<PaymentRequest> + Serialize,
144    {
145        FacilitatorClient {
146            base_url: self.base_url,
147            client: self.client,
148            supported_headers: self.supported_headers,
149            verify_headers: self.verify_headers,
150            settle_headers: self.settle_headers,
151            _phantom: std::marker::PhantomData,
152        }
153    }
154
155    pub fn with_verify_response_type<NewVRes>(self) -> FacilitatorClient<VReq, NewVRes, SReq, SRes>
156    where
157        NewVRes: IntoVerifyResponse + for<'de> Deserialize<'de>,
158    {
159        FacilitatorClient {
160            supported_headers: self.supported_headers,
161            base_url: self.base_url,
162            verify_headers: self.verify_headers,
163            settle_headers: self.settle_headers,
164            client: self.client,
165            _phantom: std::marker::PhantomData,
166        }
167    }
168
169    pub fn with_settle_request_type<NewSReq>(self) -> FacilitatorClient<VReq, VRes, NewSReq, SRes>
170    where
171        NewSReq: From<PaymentRequest> + Serialize,
172    {
173        FacilitatorClient {
174            supported_headers: self.supported_headers,
175            base_url: self.base_url,
176            verify_headers: self.verify_headers,
177            settle_headers: self.settle_headers,
178            client: self.client,
179            _phantom: std::marker::PhantomData,
180        }
181    }
182
183    pub fn with_settle_response_type<NewSRes>(self) -> FacilitatorClient<VReq, VRes, SReq, NewSRes>
184    where
185        NewSRes: IntoSettleResponse + for<'de> Deserialize<'de>,
186    {
187        FacilitatorClient {
188            supported_headers: self.supported_headers,
189            base_url: self.base_url,
190            verify_headers: self.verify_headers,
191            settle_headers: self.settle_headers,
192            client: self.client,
193            _phantom: std::marker::PhantomData,
194        }
195    }
196
197    pub fn header(mut self, key: &HeaderName, value: &HeaderValue) -> Self {
198        self.supported_headers.insert(key, value.to_owned());
199        self.verify_headers.insert(key, value.to_owned());
200        self.settle_headers.insert(key, value.to_owned());
201        self
202    }
203
204    pub fn supported_header(mut self, key: &HeaderName, value: &HeaderValue) -> Self {
205        self.supported_headers.insert(key, value.to_owned());
206        self
207    }
208
209    pub fn verify_header(mut self, key: &HeaderName, value: &HeaderValue) -> Self {
210        self.verify_headers.insert(key, value.to_owned());
211        self
212    }
213
214    pub fn settle_header(mut self, key: &HeaderName, value: &HeaderValue) -> Self {
215        self.settle_headers.insert(key, value.to_owned());
216        self
217    }
218}
219
220impl
221    FacilitatorClient<
222        DefaultPaymentRequest,
223        DefaultVerifyResponse,
224        DefaultPaymentRequest,
225        DefaultSettleResponse,
226    >
227{
228    pub fn from_url(base_url: Url) -> Self {
229        FacilitatorClient::new_from_url(base_url)
230    }
231}
232
233#[derive(Debug, thiserror::Error)]
234pub enum FacilitatorClientError {
235    #[error("URL parse error: {0}")]
236    UrlParseError(#[from] url::ParseError),
237    #[error("HTTP request error: {0}")]
238    HttpRequestError(#[from] reqwest::Error),
239    #[error("JSON Serialization/Deserialization error: {0}")]
240    SerdeJsonError(#[from] serde_json::Error),
241}
242
243impl<VReq, VRes, SReq, SRes> Facilitator for FacilitatorClient<VReq, VRes, SReq, SRes>
244where
245    VReq: From<PaymentRequest> + Serialize,
246    VRes: IntoVerifyResponse + for<'de> Deserialize<'de>,
247    SReq: From<PaymentRequest> + Serialize,
248    SRes: IntoSettleResponse + for<'de> Deserialize<'de>,
249{
250    type Error = FacilitatorClientError;
251
252    async fn supported(&self) -> Result<SupportedResponse, Self::Error> {
253        let supported = self
254            .client
255            .get(self.base_url.join("supported")?)
256            .headers(self.supported_headers.clone())
257            .send()
258            .await?
259            .json()
260            .await?;
261
262        Ok(supported)
263    }
264
265    async fn verify(&self, request: PaymentRequest) -> Result<VerifyResult, Self::Error> {
266        let result = self
267            .client
268            .post(self.base_url.join("verify")?)
269            .headers(self.verify_headers.clone())
270            .json(&VReq::from(request))
271            .send()
272            .await?
273            .json::<VRes>()
274            .await?;
275
276        Ok(result.into_verify_response())
277    }
278
279    async fn settle(&self, request: PaymentRequest) -> Result<SettleResult, Self::Error> {
280        let result = self
281            .client
282            .post(self.base_url.join("settle")?)
283            .headers(self.settle_headers.clone())
284            .json(&SReq::from(request))
285            .send()
286            .await?
287            .json::<SRes>()
288            .await?;
289
290        Ok(result.into_settle_response())
291    }
292}