1use crate::{
2 error::{Error, Result},
3 models::{
4 request::{self, CancelQR, GetAccessToken, RefundPayment},
5 response::{self, AuthToken},
6 AccessToken, ClientId, ClientSecret, PaymentId, QRId,
7 },
8};
9
10#[derive(Debug)]
11pub struct Client {
12 http_client: reqwest::Client,
13 api_base_url: String,
14}
15
16impl Client {
17 pub fn new(api_base_url: String) -> Self {
18 return Self {
19 http_client: reqwest::Client::new(),
20 api_base_url,
21 };
22 }
23
24 pub async fn get_access_token(
26 &self,
27 id: &ClientId,
28 secret: &ClientSecret,
29 ) -> Result<AuthToken> {
30 let body = GetAccessToken {
31 client_id: id,
32 client_secret: secret,
33 };
34
35 println!("ID: {}", body.client_id.as_str());
36 println!("Secret: {}", body.client_secret.as_str());
37
38 let input = SendRequestInput {
39 method: reqwest::Method::POST,
40 url: "/v2/auth/token",
41 token: None,
42 body: Some(body),
43 };
44 return self.send_request(input).await;
45 }
46
47 pub async fn create_qr<'a, 'b>(
48 &'a self,
49 payload: &request::CreateQR<'b>,
50 token: &'a AccessToken,
51 ) -> Result<response::CreateQRResponse> {
52 let input = SendRequestInput {
53 method: reqwest::Method::POST,
54 url: "/v2/mia/qr",
55 token: Some(token),
56 body: Some(payload),
57 };
58 return self.send_request(input).await;
59 }
60
61 pub async fn get_qr(
62 &self,
63 qr_id: &QRId,
64 token: &AccessToken,
65 ) -> Result<response::GetQRDetails> {
66 let url = format!("/v2/mia/qr/{}", qr_id.as_str());
67 let input: SendRequestInput<QRId> = SendRequestInput {
68 method: reqwest::Method::GET,
69 url: url.as_str(),
70 token: Some(token),
71 body: None,
72 };
73
74 return self.send_request(input).await;
75 }
76
77 pub async fn cancel_qr(
78 &self,
79 qr_id: &QRId,
80 payload: &CancelQR,
81 token: &AccessToken,
82 ) -> Result<response::CancelQR> {
83 let url = format!("/v2/mia/qr/{qr_id}/cancel");
84 let input = SendRequestInput {
85 method: reqwest::Method::POST,
86 url: url.as_str(),
87 token: Some(token),
88 body: Some(payload),
89 };
90
91 return self.send_request(input).await;
92 }
93
94 pub async fn get_payment(
95 &self,
96 id: &PaymentId,
97 token: &AccessToken,
98 ) -> Result<response::PaymentDetails> {
99 let url = format!("/v2/mia/payments/{id}");
100 let input: SendRequestInput<()> = SendRequestInput {
101 method: reqwest::Method::GET,
102 url: url.as_str(),
103 token: Some(token),
104 body: None,
105 };
106
107 return self.send_request(input).await;
108 }
109
110 pub async fn refund_payment(
111 &self,
112 id: &PaymentId,
113 payload: &RefundPayment,
114 token: &AccessToken,
115 ) -> Result<response::RefundPayment> {
116 let url = format!("/v2/mia/payments/{id}/refund");
117
118 let input = SendRequestInput {
119 method: reqwest::Method::POST,
120 url: &url,
121 token: Some(token),
122 body: Some(payload),
123 };
124
125 return self.send_request(input).await;
126 }
127
128 async fn send_request<'a, B, R>(&self, input: SendRequestInput<'a, B>) -> Result<R>
129 where
130 B: serde::Serialize,
131 R: serde::de::DeserializeOwned + core::fmt::Debug,
132 {
133 use reqwest::header::{self, HeaderMap, HeaderValue};
134
135 let mut headers: HeaderMap<HeaderValue> = HeaderMap::new();
136 headers.insert(
137 header::ACCEPT,
138 HeaderValue::from_str("application/json").unwrap(),
139 );
140
141 if input.method != reqwest::Method::GET {
142 headers.insert(
143 header::CONTENT_TYPE,
144 HeaderValue::from_str("application/json").unwrap(),
145 );
146 }
147
148 if let Some(token) = input.token {
149 let value = format!("Bearer {}", token.as_str());
150 headers.insert(
151 header::AUTHORIZATION,
152 HeaderValue::from_str(&value).unwrap(),
153 );
154 }
155
156 let url = format!("{}{}", &self.api_base_url, input.url);
157 let mut req = self.http_client.request(input.method, url).headers(headers);
158
159 if let Some(ref body) = input.body {
160 req = req.json(body);
161 }
162
163 let res = req.send().await.map_err(|err| {
164 let source = report(&err);
165 eprintln!("{source}");
166 return Error::Http(format!("error sending request: {err}"));
167 })?;
168
169 if res.status() == 401 {
170 return Err(Error::Unauthorized);
171 }
172
173 let res: response::ApiResponse<R> = res
174 .json()
175 .await
176 .map_err(|err| Error::Json(format!("error parsing response: {err}")))?;
177
178 if res.result.is_some() {
179 return Ok(res.result.unwrap());
180 }
181
182 return Err(Error::Api(res.errors.unwrap()));
183 }
184}
185
186struct SendRequestInput<'a, B: serde::Serialize> {
187 method: reqwest::Method,
188 url: &'a str,
189 token: Option<&'a AccessToken>,
190 body: Option<B>,
191}
192
193fn report(mut err: &dyn std::error::Error) -> String {
194 use std::fmt::Write;
195
196 let mut s = format!("{}", err);
197 while let Some(src) = err.source() {
198 let _ = write!(s, "\n\nCaused by: {}", src);
199 err = src;
200 }
201 s
202}