use crate::{
error::{Error, Result},
models::{
request::{self, CancelQR, GetAccessToken, RefundPayment},
response::{self, AuthToken},
AccessToken, ClientId, ClientSecret, PaymentId, QRId,
},
};
#[derive(Debug)]
pub struct Client {
http_client: reqwest::Client,
api_base_url: String,
}
impl Client {
pub fn new(api_base_url: String) -> Self {
return Self {
http_client: reqwest::Client::new(),
api_base_url,
};
}
pub async fn get_access_token(
&self,
id: &ClientId,
secret: &ClientSecret,
) -> Result<AuthToken> {
let body = GetAccessToken {
client_id: id,
client_secret: secret,
};
println!("ID: {}", body.client_id.as_str());
println!("Secret: {}", body.client_secret.as_str());
let input = SendRequestInput {
method: reqwest::Method::POST,
url: "/v2/auth/token",
token: None,
body: Some(body),
};
return self.send_request(input).await;
}
pub async fn create_qr<'a, 'b>(
&'a self,
payload: &request::CreateQR<'b>,
token: &'a AccessToken,
) -> Result<response::CreateQRResponse> {
let input = SendRequestInput {
method: reqwest::Method::POST,
url: "/v2/mia/qr",
token: Some(token),
body: Some(payload),
};
return self.send_request(input).await;
}
pub async fn get_qr(
&self,
qr_id: &QRId,
token: &AccessToken,
) -> Result<response::GetQRDetails> {
let url = format!("/v2/mia/qr/{}", qr_id.as_str());
let input: SendRequestInput<QRId> = SendRequestInput {
method: reqwest::Method::GET,
url: url.as_str(),
token: Some(token),
body: None,
};
return self.send_request(input).await;
}
pub async fn cancel_qr(
&self,
qr_id: &QRId,
payload: &CancelQR,
token: &AccessToken,
) -> Result<response::CancelQR> {
let url = format!("/v2/mia/qr/{qr_id}/cancel");
let input = SendRequestInput {
method: reqwest::Method::POST,
url: url.as_str(),
token: Some(token),
body: Some(payload),
};
return self.send_request(input).await;
}
pub async fn get_payment(
&self,
id: &PaymentId,
token: &AccessToken,
) -> Result<response::PaymentDetails> {
let url = format!("/v2/mia/payments/{id}");
let input: SendRequestInput<()> = SendRequestInput {
method: reqwest::Method::GET,
url: url.as_str(),
token: Some(token),
body: None,
};
return self.send_request(input).await;
}
pub async fn refund_payment(
&self,
id: &PaymentId,
payload: &RefundPayment,
token: &AccessToken,
) -> Result<response::RefundPayment> {
let url = format!("/v2/mia/payments/{id}/refund");
let input = SendRequestInput {
method: reqwest::Method::POST,
url: &url,
token: Some(token),
body: Some(payload),
};
return self.send_request(input).await;
}
async fn send_request<'a, B, R>(&self, input: SendRequestInput<'a, B>) -> Result<R>
where
B: serde::Serialize,
R: serde::de::DeserializeOwned + core::fmt::Debug,
{
use reqwest::header::{self, HeaderMap, HeaderValue};
let mut headers: HeaderMap<HeaderValue> = HeaderMap::new();
headers.insert(
header::ACCEPT,
HeaderValue::from_str("application/json").unwrap(),
);
if input.method != reqwest::Method::GET {
headers.insert(
header::CONTENT_TYPE,
HeaderValue::from_str("application/json").unwrap(),
);
}
if let Some(token) = input.token {
let value = format!("Bearer {}", token.as_str());
headers.insert(
header::AUTHORIZATION,
HeaderValue::from_str(&value).unwrap(),
);
}
let url = format!("{}{}", &self.api_base_url, input.url);
let mut req = self.http_client.request(input.method, url).headers(headers);
if let Some(ref body) = input.body {
req = req.json(body);
}
let res = req
.send()
.await
.map_err(|err| Error::Http(format!("error sending request: {err}")))?;
if res.status() == 401 {
return Err(Error::Unauthorized);
}
let res: response::ApiResponse<R> = res
.json()
.await
.map_err(|err| Error::Json(format!("error parsing response: {err}")))?;
if res.result.is_some() {
return Ok(res.result.unwrap());
}
return Err(Error::Api(res.errors.unwrap()));
}
}
struct SendRequestInput<'a, B: serde::Serialize> {
method: reqwest::Method,
url: &'a str,
token: Option<&'a AccessToken>,
body: Option<B>,
}