#![allow(
clippy::type_complexity,
clippy::large_enum_variant,
clippy::result_large_err,
clippy::if_same_then_else
)]
use reqwest::Client;
use url::Url;
pub mod models;
pub use models::*;
pub mod checkouts;
pub mod customers;
pub mod members;
pub mod memberships;
pub mod merchant;
pub mod payouts;
pub mod readers;
pub mod receipts;
pub mod roles;
pub mod transactions;
pub use transactions::TransactionHistoryQuery;
#[derive(Debug)]
pub enum Error {
Http(reqwest::Error),
Json(serde_json::Error),
Url(url::ParseError),
ApiError { status: u16, body: ApiErrorBody },
}
#[derive(Debug, serde::Deserialize)]
pub struct ApiErrorBody {
#[serde(rename = "type")]
pub error_type: Option<String>,
pub title: Option<String>,
pub status: Option<u16>,
pub detail: Option<String>,
pub error_code: Option<String>,
pub message: Option<String>,
pub param: Option<String>,
#[serde(flatten)]
pub additional_fields: std::collections::HashMap<String, serde_json::Value>,
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
Error::Http(err)
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::Json(err)
}
}
impl From<url::ParseError> for Error {
fn from(err: url::ParseError) -> Self {
Error::Url(err)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Http(e) => write!(f, "HTTP error: {}", e),
Error::Json(e) => write!(f, "JSON error: {}", e),
Error::Url(e) => write!(f, "URL error: {}", e),
Error::ApiError { status, body } => {
let status_str = status.to_string();
let message = body
.detail
.as_ref()
.or(body.message.as_ref())
.or(body.title.as_ref())
.unwrap_or(&status_str);
write!(f, "API error {}: {}", status, message)
}
}
}
}
impl std::error::Error for Error {}
pub type Result<T> = std::result::Result<T, Error>;
pub struct SumUpClient {
pub(crate) http_client: Client,
pub(crate) api_key: String,
pub(crate) base_url: Url,
}
impl SumUpClient {
pub fn new(api_key: String, use_sandbox: bool) -> Result<Self> {
let base_url_str = if use_sandbox {
"https://api.sumup.com" } else {
"https://api.sumup.com"
};
Ok(Self {
http_client: Client::new(),
api_key,
base_url: Url::parse(base_url_str)?,
})
}
pub fn with_custom_url(api_key: String, base_url: String) -> Result<Self> {
Ok(Self {
http_client: Client::new(),
api_key,
base_url: Url::parse(&base_url)?,
})
}
pub(crate) fn build_url(&self, path: &str) -> Result<Url> {
Ok(self.base_url.join(path)?)
}
pub(crate) async fn handle_error<T>(&self, response: reqwest::Response) -> Result<T> {
let status = response.status().as_u16();
let response_text = response.text().await.unwrap_or_default();
let body = match serde_json::from_str::<ApiErrorBody>(&response_text) {
Ok(parsed_body) => parsed_body,
Err(_) => {
ApiErrorBody {
error_type: None,
title: None,
status: Some(status),
detail: Some(response_text),
error_code: None,
message: None,
param: None,
additional_fields: std::collections::HashMap::new(),
}
}
};
Err(Error::ApiError { status, body })
}
pub fn api_key(&self) -> &str {
&self.api_key
}
pub fn base_url(&self) -> &Url {
&self.base_url
}
}