use hyper::http::{HeaderValue, Uri};
use payjp_client_core::{RequestStrategy, SharedConfigBuilder};
use base64::{engine::general_purpose, Engine as _};
use crate::hyper::client::Client;
use crate::PayjpError;
static DEFAULT_USER_AGENT: &str = concat!("Payjp/v1 RustBindings/", env!("CARGO_PKG_VERSION"));
const DEFAULT_API_BASE: &str = "https://api.pay.jp/";
#[derive(Clone, Debug)]
pub struct ClientBuilder {
inner: SharedConfigBuilder,
}
impl ClientBuilder {
pub fn new(secret: impl Into<String>) -> Self {
Self { inner: SharedConfigBuilder::new(secret) }
}
pub fn request_strategy(mut self, strategy: RequestStrategy) -> Self {
self.inner = self.inner.request_strategy(strategy);
self
}
pub fn url(mut self, url: impl Into<String>) -> Self {
self.inner = self.inner.url(url);
self
}
pub fn app_info(
mut self,
name: impl Into<String>,
version: Option<String>,
url: Option<String>,
) -> Self {
self.inner = self.inner.app_info(name, version, url);
self
}
fn try_into_config(self) -> Result<ClientConfig, PayjpError> {
let api_base = if let Some(url) = self.inner.api_base {
Uri::try_from(url).map_err(|err| {
PayjpError::ConfigError(format!("user-provided url is invalid: {err}"))
})?
} else {
Uri::from_static(DEFAULT_API_BASE)
};
let user_agent_header = if let Some(app_info_str) = self.inner.app_info_str {
HeaderValue::try_from(format!("{DEFAULT_USER_AGENT} {app_info_str}"))
.map_err(|_| cons_header_err("app_info"))?
} else {
HeaderValue::from_static(DEFAULT_USER_AGENT)
};
let encoded = general_purpose::STANDARD.encode(format!("{}:", self.inner.secret));
let mut secret = HeaderValue::try_from(format!("Basic {}", encoded))
.map_err(|_| cons_header_err("secret"))?;
secret.set_sensitive(true);
Ok(ClientConfig {
user_agent: user_agent_header,
request_strategy: self.inner.request_strategy.unwrap_or(RequestStrategy::Once),
secret,
api_base,
})
}
pub fn build(self) -> Result<Client, PayjpError> {
Ok(Client::from_config(self.try_into_config()?))
}
#[cfg(feature = "blocking")]
pub fn build_sync(self) -> Result<crate::hyper::blocking::Client, PayjpError> {
Ok(crate::hyper::blocking::Client::from_async(self.build()?))
}
}
fn cons_header_err(config_name: &'static str) -> PayjpError {
PayjpError::ClientError(format!("`{config_name}` can only include visible ASCII characters"))
}
#[derive(Clone, Debug)]
pub struct ClientConfig {
pub user_agent: HeaderValue,
pub request_strategy: RequestStrategy,
pub secret: HeaderValue,
pub api_base: Uri,
}