use std::time;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::ResponseError;
#[derive(Debug, Deserialize, Serialize)]
pub struct PaymentBuilder {
amount: f64,
email: String,
key: String,
channel: Option<Channel>,
currency: Option<Currency>,
label: Option<String>,
metadata: Option<String>,
reference: Option<String>,
}
impl PaymentBuilder {
pub fn init_payment(email: String, amount: f64, key: String) -> Self {
Self {
amount,
email,
key,
channel: None,
currency: None,
label: None,
metadata: None,
reference: None,
}
}
pub fn build(self) -> InitialisePay {
InitialisePay(self)
}
pub fn amount(&self) -> f64 {
self.amount
}
pub fn channel(&mut self, channel: Channel) {
match channel {
Channel::Card => self.channel = Some(Channel::Card),
Channel::Bank => self.channel = Some(Channel::Bank),
Channel::USSD => self.channel = Some(Channel::USSD),
Channel::QR => self.channel = Some(Channel::QR),
Channel::MobileMoney(info) => self.channel = Some(Channel::MobileMoney(info)),
Channel::BankTransfer => self.channel = Some(Channel::BankTransfer),
}
}
pub fn currency(&mut self, currency: Currency) {
self.currency = Some(currency)
}
pub fn metadata(&mut self, metadata: String) {
self.metadata = Some(metadata)
}
pub fn label(&mut self, label: String) {
self.label = Some(label)
}
pub fn reference(&mut self, reference: String) {
self.reference = Some(reference)
}
pub fn add_fallback(&self, _f: fn() -> ()) {
todo!()
}
fn json_builder(&self) -> serde_json::Value {
let mut json = serde_json::to_value(self).unwrap();
if let Value::Object(ref mut map) = json {
let keys_to_remove: Vec<String> = map
.iter()
.filter(|&(_, v)| v.is_null())
.map(|(k, _)| k.clone())
.collect();
for k in keys_to_remove {
map.remove(&k);
}
}
json
}
}
pub struct InitialisePay(PaymentBuilder);
impl InitialisePay {
pub async fn send(&self) -> Result<(), ResponseError> {
let timeout = time::Duration::from_millis(10000);
let http_client = Client::builder().timeout(timeout).build().unwrap();
let data = self.0.json_builder();
http_client
.post("https://api.paystack.co/transaction/initialize")
.header("Authorization", format!("Bearer {}", self.0.key))
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.header("Cache-Control", "no-cache")
.json(&data)
.send()
.await
.map_err(|e| ResponseError::PayStackError(e.to_string()))
.unwrap();
Ok(())
}
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Currency {
NGN,
USD,
GHS,
ZAR,
KES,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Channel {
Card,
Bank,
USSD,
QR,
MobileMoney(MobileMoneyInfo),
BankTransfer,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MobileMoneyInfo {
phone: u32,
provider: String,
}
mod test {
use serde_json::Value;
use super::{Channel, PaymentBuilder};
fn init_Builder() -> PaymentBuilder {
let mut builder = PaymentBuilder::init_payment(
"test@example.com".to_string(),
100.0,
"secret_key".to_string(),
);
builder.channel(Channel::Card);
builder.label("label".to_string());
builder.reference("reference".to_string());
builder
}
#[test]
fn json_response() {
let builder = init_Builder();
let json_builder = builder.json_builder();
let data = r#"{
"amount":100.0,
"channel":"Card",
"email":"test@example.com",
"key":"secret_key",
"label":"label",
"reference":"reference"
}"#;
let json: Value = serde_json::from_str(data).unwrap();
assert_eq!(json, json_builder)
}
}