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 RemoteFacilitatorClient<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 DefaultRemoteFacilitatorClient = RemoteFacilitatorClient<
117 DefaultPaymentRequest,
118 DefaultVerifyResponse,
119 DefaultPaymentRequest,
120 DefaultSettleResponse,
121>;
122
123impl<VReq, VRes, SReq, SRes> RemoteFacilitatorClient<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 RemoteFacilitatorClient {
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>(
142 self,
143 ) -> RemoteFacilitatorClient<NewVReq, VRes, SReq, SRes>
144 where
145 NewVReq: From<PaymentRequest> + Serialize,
146 {
147 RemoteFacilitatorClient {
148 base_url: self.base_url,
149 client: self.client,
150 supported_headers: self.supported_headers,
151 verify_headers: self.verify_headers,
152 settle_headers: self.settle_headers,
153 _phantom: std::marker::PhantomData,
154 }
155 }
156
157 pub fn with_verify_response_type<NewVRes>(
158 self,
159 ) -> RemoteFacilitatorClient<VReq, NewVRes, SReq, SRes>
160 where
161 NewVRes: IntoVerifyResponse + for<'de> Deserialize<'de>,
162 {
163 RemoteFacilitatorClient {
164 supported_headers: self.supported_headers,
165 base_url: self.base_url,
166 verify_headers: self.verify_headers,
167 settle_headers: self.settle_headers,
168 client: self.client,
169 _phantom: std::marker::PhantomData,
170 }
171 }
172
173 pub fn with_settle_request_type<NewSReq>(
174 self,
175 ) -> RemoteFacilitatorClient<VReq, VRes, NewSReq, SRes>
176 where
177 NewSReq: From<PaymentRequest> + Serialize,
178 {
179 RemoteFacilitatorClient {
180 supported_headers: self.supported_headers,
181 base_url: self.base_url,
182 verify_headers: self.verify_headers,
183 settle_headers: self.settle_headers,
184 client: self.client,
185 _phantom: std::marker::PhantomData,
186 }
187 }
188
189 pub fn with_settle_response_type<NewSRes>(
190 self,
191 ) -> RemoteFacilitatorClient<VReq, VRes, SReq, NewSRes>
192 where
193 NewSRes: IntoSettleResponse + for<'de> Deserialize<'de>,
194 {
195 RemoteFacilitatorClient {
196 supported_headers: self.supported_headers,
197 base_url: self.base_url,
198 verify_headers: self.verify_headers,
199 settle_headers: self.settle_headers,
200 client: self.client,
201 _phantom: std::marker::PhantomData,
202 }
203 }
204
205 pub fn header(mut self, key: &HeaderName, value: &HeaderValue) -> Self {
206 self.supported_headers.insert(key, value.to_owned());
207 self.verify_headers.insert(key, value.to_owned());
208 self.settle_headers.insert(key, value.to_owned());
209 self
210 }
211
212 pub fn supported_header(mut self, key: &HeaderName, value: &HeaderValue) -> Self {
213 self.supported_headers.insert(key, value.to_owned());
214 self
215 }
216
217 pub fn verify_header(mut self, key: &HeaderName, value: &HeaderValue) -> Self {
218 self.verify_headers.insert(key, value.to_owned());
219 self
220 }
221
222 pub fn settle_header(mut self, key: &HeaderName, value: &HeaderValue) -> Self {
223 self.settle_headers.insert(key, value.to_owned());
224 self
225 }
226}
227
228impl
229 RemoteFacilitatorClient<
230 DefaultPaymentRequest,
231 DefaultVerifyResponse,
232 DefaultPaymentRequest,
233 DefaultSettleResponse,
234 >
235{
236 pub fn from_url(base_url: Url) -> Self {
237 RemoteFacilitatorClient::new_from_url(base_url)
238 }
239}
240
241#[derive(Debug, thiserror::Error)]
242pub enum RemoteFacilitatorClientError {
243 #[error("URL parse error: {0}")]
244 UrlParseError(#[from] url::ParseError),
245 #[error("HTTP request error: {0}")]
246 HttpRequestError(#[from] reqwest::Error),
247 #[error("Serialization/Deserialization error: {0}")]
248 SerdeError(#[from] serde_json::Error),
249}
250
251impl<VReq, VRes, SReq, SRes> Facilitator for RemoteFacilitatorClient<VReq, VRes, SReq, SRes>
252where
253 VReq: From<PaymentRequest> + Serialize,
254 VRes: IntoVerifyResponse + for<'de> Deserialize<'de>,
255 SReq: From<PaymentRequest> + Serialize,
256 SRes: IntoSettleResponse + for<'de> Deserialize<'de>,
257{
258 type Error = RemoteFacilitatorClientError;
259
260 async fn supported(&self) -> Result<SupportedResponse, Self::Error> {
261 let supported = self
262 .client
263 .get(self.base_url.join("supported")?)
264 .headers(self.supported_headers.clone())
265 .send()
266 .await?
267 .json()
268 .await?;
269
270 Ok(supported)
271 }
272
273 async fn verify(&self, request: PaymentRequest) -> Result<VerifyResult, Self::Error> {
274 let result = self
275 .client
276 .post(self.base_url.join("verify")?)
277 .headers(self.verify_headers.clone())
278 .json(&VReq::from(request))
279 .send()
280 .await?
281 .json::<VRes>()
282 .await?;
283
284 Ok(result.into_verify_response())
285 }
286
287 async fn settle(&self, request: PaymentRequest) -> Result<SettleResult, Self::Error> {
288 let result = self
289 .client
290 .post(self.base_url.join("settle")?)
291 .headers(self.settle_headers.clone())
292 .json(&SReq::from(request))
293 .send()
294 .await?
295 .json::<SRes>()
296 .await?;
297
298 Ok(result.into_settle_response())
299 }
300}