use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::time::Duration;
mod error;
pub use error::Error;
mod meta;
pub use meta::Meta;
mod create_order;
mod currency;
pub use currency::Currency;
mod payment_method;
pub use payment_method::PaymentMethod;
mod order;
pub use order::Order;
pub mod prelude {
pub use super::{Currency, Gateway, Order, PaymentMethod};
}
const BASE_URL: &str = "https://api.v1.checkout.bambora.com";
pub struct Gateway {
client: reqwest::Client,
_access_token: String,
_merchant_number: String,
_secret_token: String,
}
impl Gateway {
pub async fn new(
access_token: String,
merchant_number: String,
secret_token: String,
timeout: Option<Duration>,
) -> Result<Gateway, Error> {
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
"Content-Type",
reqwest::header::HeaderValue::from_static("application/json"),
);
headers.insert(
"Accept",
reqwest::header::HeaderValue::from_static("application/json"),
);
let auth_header_value = format!(
"Basic {}",
base64::encode_config(
format!("{}@{}:{}", access_token, merchant_number, secret_token),
base64::URL_SAFE_NO_PAD
)
);
let auth_header = match reqwest::header::HeaderValue::from_str(&auth_header_value) {
Ok(header) => header,
Err(err) => {
return Err(Error::Unspecified(format!(
"could not create auth header ({})",
err.to_string()
)))
}
};
headers.insert("Authorization", auth_header);
let timeout = match timeout {
Some(t) => t,
None => Duration::new(60, 0),
};
let client = match reqwest::ClientBuilder::new()
.default_headers(headers)
.https_only(true)
.timeout(timeout)
.build()
{
Ok(r) => r,
Err(err) => {
return Err(Error::Unspecified(format!(
"could not create reqwest client ({})",
err.to_string()
)))
}
};
let c = Gateway {
client,
_access_token: access_token,
_merchant_number: merchant_number,
_secret_token: secret_token,
};
Ok(c)
}
async fn send<'a, T: DeserializeOwned>(
&self,
url: &str,
body: impl Serialize,
) -> Result<T, Error> {
let res = match self.client.post(url).json(&body).send().await {
Ok(r) => r,
Err(err) => {
return Err(Error::NetworkError(format!(
"could not send request ({})",
err.to_string()
)))
}
};
let status = res.status();
let text = res
.text()
.await
.unwrap_or_else(|_| String::from("Could not retrieve body text."));
if status.as_u16() < 200 || status.as_u16() >= 300 {
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
struct ApiError {
pub meta: Option<Meta>,
}
let api_error: ApiError =
serde_json::from_str(&text).unwrap_or_else(|_| ApiError { meta: None });
let meta = match api_error.meta {
Some(m) => m,
None => {
return Err(Error::ApiError(
status.as_u16().to_string(),
"unknown".to_string(),
None,
None,
text,
))
}
};
let action = match meta.action {
Some(m) => m,
None => {
return Err(Error::ApiError(
status.as_u16().to_string(),
"unknown".to_string(),
None,
None,
text,
))
}
};
let code = match action.code {
Some(m) => m,
None => {
return Err(Error::ApiError(
status.as_u16().to_string(),
"unknown".to_string(),
None,
None,
text,
))
}
};
let source = match action.source {
Some(m) => m,
None => {
return Err(Error::ApiError(
status.as_u16().to_string(),
"unknown".to_string(),
None,
None,
text,
))
}
};
let (enduser_message, merchant_message) = match meta.message {
Some(m) => (m.enduser, m.merchant),
None => (None, None),
};
return Err(Error::ApiError(
code,
source,
enduser_message,
merchant_message,
text,
));
}
let body: T = match serde_json::from_str(&text) {
Ok(r) => r,
Err(err) => {
return Err(Error::SerializationError(format!(
"could not deserialize response ({}): {}",
err, text
)))
}
};
Ok(body)
}
async fn get<'a, T: DeserializeOwned>(&self, url: &str) -> Result<T, Error> {
let res = match self.client.get(url).send().await {
Ok(r) => r,
Err(err) => {
return Err(Error::NetworkError(format!(
"could not send request ({})",
err
)))
}
};
let status = res.status();
let text = res
.text()
.await
.unwrap_or_else(|_| String::from("Could not retrieve body text."));
if status.as_u16() < 200 || status.as_u16() >= 300 {
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
struct ApiError {
pub meta: Option<Meta>,
}
let api_error: ApiError =
serde_json::from_str(&text).unwrap_or_else(|_| ApiError { meta: None });
let meta = match api_error.meta {
Some(m) => m,
None => {
return Err(Error::ApiError(
status.as_u16().to_string(),
"unknown".to_string(),
None,
None,
text,
))
}
};
let action = match meta.action {
Some(m) => m,
None => {
return Err(Error::ApiError(
status.as_u16().to_string(),
"unknown".to_string(),
None,
None,
text,
))
}
};
let code = match action.code {
Some(m) => m,
None => {
return Err(Error::ApiError(
status.as_u16().to_string(),
"unknown".to_string(),
None,
None,
text,
))
}
};
let source = match action.source {
Some(m) => m,
None => {
return Err(Error::ApiError(
status.as_u16().to_string(),
"unknown".to_string(),
None,
None,
text,
))
}
};
let (enduser_message, merchant_message) = match meta.message {
Some(m) => (m.enduser, m.merchant),
None => (None, None),
};
return Err(Error::ApiError(
code,
source,
enduser_message,
merchant_message,
text,
));
}
let body: T = match serde_json::from_str(&text) {
Ok(r) => r,
Err(err) => {
return Err(Error::SerializationError(format!(
"could not deserialize response ({}): {}",
err, text
)))
}
};
Ok(body)
}
}