use serde::{Deserialize, Serialize};
use crate::environment::ApiEnvironment;
use crate::{CommandId, Mpesa, MpesaError, MpesaResult};
#[derive(Debug, Serialize)]
struct B2cPayload<'mpesa> {
#[serde(rename(serialize = "InitiatorName"))]
initiator_name: &'mpesa str,
#[serde(rename(serialize = "SecurityCredential"))]
security_credential: &'mpesa str,
#[serde(rename(serialize = "CommandID"))]
command_id: CommandId,
#[serde(rename(serialize = "Amount"))]
amount: f64,
#[serde(rename(serialize = "PartyA"))]
party_a: &'mpesa str,
#[serde(rename(serialize = "PartyB"))]
party_b: &'mpesa str,
#[serde(rename(serialize = "Remarks"))]
remarks: &'mpesa str,
#[serde(rename(serialize = "QueueTimeOutURL"))]
queue_time_out_url: &'mpesa str,
#[serde(rename(serialize = "ResultURL"))]
result_url: &'mpesa str,
#[serde(rename(serialize = "Occasion"))]
occasion: &'mpesa str,
}
#[derive(Debug, Deserialize, Clone)]
pub struct B2cResponse {
#[serde(rename(deserialize = "ConversationID"))]
pub conversation_id: String,
#[serde(rename(deserialize = "OriginatorConversationID"))]
pub originator_conversation_id: String,
#[serde(rename(deserialize = "ResponseCode"))]
pub response_code: String,
#[serde(rename(deserialize = "ResponseDescription"))]
pub response_description: String,
}
#[derive(Debug)]
pub struct B2cBuilder<'mpesa, Env: ApiEnvironment> {
initiator_name: &'mpesa str,
client: &'mpesa Mpesa<Env>,
command_id: Option<CommandId>,
amount: Option<f64>,
party_a: Option<&'mpesa str>,
party_b: Option<&'mpesa str>,
remarks: Option<&'mpesa str>,
queue_timeout_url: Option<&'mpesa str>,
result_url: Option<&'mpesa str>,
occasion: Option<&'mpesa str>,
}
impl<'mpesa, Env: ApiEnvironment> B2cBuilder<'mpesa, Env> {
pub fn new(client: &'mpesa Mpesa<Env>, initiator_name: &'mpesa str) -> B2cBuilder<'mpesa, Env> {
B2cBuilder {
client,
initiator_name,
amount: None,
party_a: None,
party_b: None,
remarks: None,
queue_timeout_url: None,
result_url: None,
occasion: None,
command_id: None,
}
}
pub fn command_id(mut self, command_id: CommandId) -> B2cBuilder<'mpesa, Env> {
self.command_id = Some(command_id);
self
}
pub fn party_a(mut self, party_a: &'mpesa str) -> B2cBuilder<'mpesa, Env> {
self.party_a = Some(party_a);
self
}
pub fn party_b(mut self, party_b: &'mpesa str) -> B2cBuilder<'mpesa, Env> {
self.party_b = Some(party_b);
self
}
#[deprecated]
pub fn parties(
mut self,
party_a: &'mpesa str,
party_b: &'mpesa str,
) -> B2cBuilder<'mpesa, Env> {
self.party_a = Some(party_a);
self.party_b = Some(party_b);
self
}
pub fn remarks(mut self, remarks: &'mpesa str) -> B2cBuilder<'mpesa, Env> {
self.remarks = Some(remarks);
self
}
pub fn occasion(mut self, occasion: &'mpesa str) -> B2cBuilder<'mpesa, Env> {
self.occasion = Some(occasion);
self
}
pub fn amount<Number: Into<f64>>(mut self, amount: Number) -> B2cBuilder<'mpesa, Env> {
self.amount = Some(amount.into());
self
}
pub fn timeout_url(mut self, timeout_url: &'mpesa str) -> B2cBuilder<'mpesa, Env> {
self.queue_timeout_url = Some(timeout_url);
self
}
pub fn result_url(mut self, result_url: &'mpesa str) -> B2cBuilder<'mpesa, Env> {
self.result_url = Some(result_url);
self
}
#[deprecated]
pub fn urls(
mut self,
timeout_url: &'mpesa str,
result_url: &'mpesa str,
) -> B2cBuilder<'mpesa, Env> {
self.queue_timeout_url = Some(timeout_url);
self.result_url = Some(result_url);
self
}
pub async fn send(self) -> MpesaResult<B2cResponse> {
let url = format!(
"{}/mpesa/b2c/v1/paymentrequest",
self.client.environment.base_url()
);
let credentials = self.client.gen_security_credentials()?;
let payload = B2cPayload {
initiator_name: self.initiator_name,
security_credential: &credentials,
command_id: self.command_id.unwrap_or(CommandId::BusinessPayment),
amount: self
.amount
.ok_or(MpesaError::Message("amount is required"))?,
party_a: self
.party_a
.ok_or(MpesaError::Message("party_a is required"))?,
party_b: self
.party_b
.ok_or(MpesaError::Message("party_b is required"))?,
remarks: self.remarks.unwrap_or_else(|| stringify!(None)),
queue_time_out_url: self
.queue_timeout_url
.ok_or(MpesaError::Message("queue_timeout_url is required"))?,
result_url: self
.result_url
.ok_or(MpesaError::Message("result_url is required"))?,
occasion: self.occasion.unwrap_or_else(|| stringify!(None)),
};
let response = self
.client
.http_client
.post(&url)
.bearer_auth(self.client.auth().await?)
.json(&payload)
.send()
.await?;
if response.status().is_success() {
let value = response.json::<_>().await?;
return Ok(value);
}
let value = response.json().await?;
Err(MpesaError::B2cError(value))
}
}