x402_kit/
facilitator_client.rs

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