extern crate reqwest;
extern crate serde_json;
use crate::{
Charge, Currency, ExportTransactionResponse, PartialDebitTransaction, PaystackResult,
RequestNotSuccessful, Status, Transaction, TransactionResponse, TransactionStatus,
TransactionStatusList, TransactionTimeline, TransactionTotalsResponse,
};
static BASE_URL: &str = "https://api.paystack.co";
#[derive(Clone, Debug)]
pub struct PaystackClient {
client: reqwest::Client,
api_key: String,
}
impl PaystackClient {
pub fn new(key: impl Into<String>) -> Self {
Self {
client: reqwest::Client::new(),
api_key: key.into(),
}
}
pub async fn initialize_transaction(
&self,
transaction_body: Transaction,
) -> PaystackResult<TransactionResponse> {
let url = format!("{}/transaction/initialize", BASE_URL);
let response = self
.client
.post(url)
.bearer_auth(&self.api_key)
.header("Content-Type", "application/json")
.json(&transaction_body)
.send()
.await?;
if response.error_for_status_ref().is_err() {
return Err(
RequestNotSuccessful::new(response.status(), response.text().await?).into(),
);
}
let content = response.json::<TransactionResponse>().await?;
Ok(content)
}
pub async fn verify_transaction(&self, reference: String) -> PaystackResult<TransactionStatus> {
let url = format!("{}/transaction/verify/{}", BASE_URL, reference);
let response = self
.client
.get(url)
.bearer_auth(&self.api_key)
.header("Content-Type", "application/json")
.send()
.await?;
if response.error_for_status_ref().is_err() {
return Err(
RequestNotSuccessful::new(response.status(), response.text().await?).into(),
);
}
let content = response.json::<TransactionStatus>().await?;
Ok(content)
}
pub async fn list_transactions(
&self,
number_of_transactions: Option<u32>,
status: Option<Status>,
) -> PaystackResult<TransactionStatusList> {
let url = format!("{}/transaction", BASE_URL);
let query = vec![
("perPage", number_of_transactions.unwrap_or(10).to_string()),
("status", status.unwrap_or(Status::Abandoned).to_string()),
];
let response = self
.client
.get(url)
.query(&query)
.bearer_auth(&self.api_key)
.header("Content-Type", "application.json")
.send()
.await?;
if response.error_for_status_ref().is_err() {
return Err(
RequestNotSuccessful::new(response.status(), response.text().await?).into(),
);
}
let contents = response.json::<TransactionStatusList>().await?;
Ok(contents)
}
pub async fn fetch_transactions(
&self,
transaction_id: u32,
) -> PaystackResult<TransactionStatus> {
let url = format!("{}/transaction/{}", BASE_URL, transaction_id);
let response = self
.client
.get(url)
.bearer_auth(&self.api_key)
.header("Content-Type", "application/json")
.send()
.await?;
if response.error_for_status_ref().is_err() {
return Err(
RequestNotSuccessful::new(response.status(), response.text().await?).into(),
);
}
let content = response.json::<TransactionStatus>().await?;
Ok(content)
}
pub async fn charge_authorization(&self, charge: Charge) -> PaystackResult<TransactionStatus> {
let url = format!("{}/transaction/charge_authorization", BASE_URL);
let response = self
.client
.post(url)
.bearer_auth(&self.api_key)
.header("Content-Type", "application/json")
.json(&charge)
.send()
.await?;
if response.error_for_status_ref().is_err() {
return Err(
RequestNotSuccessful::new(response.status(), response.text().await?).into(),
);
}
let content = response.json::<TransactionStatus>().await?;
Ok(content)
}
pub async fn view_transaction_timeline(
&self,
id: Option<u32>,
reference: Option<String>,
) -> PaystackResult<TransactionTimeline> {
let url: PaystackResult<String> = match (id, reference) {
(Some(id), None) => Ok(format!("{}/transaction/timeline/{}", BASE_URL, id)),
(None, Some(reference)) => {
Ok(format!("{}/transaction/timeline/{}", BASE_URL, &reference))
}
_ => {
return Err(crate::PaystackError::Transaction(
"Transaction Id or Reference is need to view transaction timeline".to_string(),
))
}
};
let url = url.unwrap(); let response = self
.client
.get(url)
.bearer_auth(&self.api_key)
.header("Content-Type", "application/json")
.send()
.await?;
if response.error_for_status_ref().is_err() {
return Err(
RequestNotSuccessful::new(response.status(), response.text().await?).into(),
);
}
let content = response.json::<TransactionTimeline>().await?;
Ok(content)
}
pub async fn total_transactions(&self) -> PaystackResult<TransactionTotalsResponse> {
let url = format!("{}/transaction/totals", BASE_URL);
let response = self
.client
.get(url)
.bearer_auth(&self.api_key)
.header("Content-Type", "application/json")
.send()
.await?;
if response.error_for_status_ref().is_err() {
return Err(
RequestNotSuccessful::new(response.status(), response.text().await?).into(),
);
};
let content = response.json::<TransactionTotalsResponse>().await?;
Ok(content)
}
pub async fn export_transaction(
&self,
status: Option<Status>,
currency: Option<Currency>,
settled: Option<bool>,
) -> PaystackResult<ExportTransactionResponse> {
let url = format!("{}/transaction/export", BASE_URL);
let settled = match settled {
Some(settled) => settled.to_string(),
None => String::from(""),
};
let query = vec![
("status", status.unwrap_or(Status::Success).to_string()),
("currency", currency.unwrap_or(Currency::EMPTY).to_string()),
("settled", settled),
];
let response = self
.client
.get(url)
.query(&query)
.bearer_auth(&self.api_key)
.header("Content-Type", "application/json")
.send()
.await?;
if response.error_for_status_ref().is_err() {
return Err(
RequestNotSuccessful::new(response.status(), response.text().await?).into(),
);
};
let content = response.json::<ExportTransactionResponse>().await?;
Ok(content)
}
pub async fn partial_debit(
&self,
transaction_body: PartialDebitTransaction,
) -> PaystackResult<TransactionStatus> {
let url = format!("{}/transaction/partial_debit", BASE_URL);
let response = self
.client
.post(url)
.bearer_auth(&self.api_key)
.header("Content-Type", "application/json")
.json(&transaction_body)
.send()
.await?;
if response.error_for_status_ref().is_err() {
return Err(
RequestNotSuccessful::new(response.status(), response.text().await?).into(),
);
}
let content = response.json::<TransactionStatus>().await?;
Ok(content)
}
}