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#[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
115pub 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}