1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use crate::client::Mpesa;
use crate::constants::TransactionType;
use crate::environment::ApiEnvironment;
use crate::errors::{MpesaError, MpesaResult};
const DYNAMIC_QR_URL: &str = "/mpesa/qrcode/v1/generate";
#[derive(Debug, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct DynamicQRRequest<'mpesa> {
/// Name of the Company/M-Pesa Merchant Name
pub merchant_name: &'mpesa str,
/// Transaction Reference Number
pub ref_no: &'mpesa str,
/// The total amount of the transaction
pub amount: f64,
#[serde(rename = "TrxCode")]
/// Transaction Type
///
/// This can be a `TransactionType` or a `&str`
/// The `&str` must be one of the following:
/// - `BG` for Buy Goods
/// - `PB` for Pay Bill
/// - `WA` Withdraw Cash
/// - `SM` Send Money (Mobile Number)
/// - `SB` Sent to Business. Business number CPI in MSISDN format.
pub transaction_type: TransactionType,
///Credit Party Identifier.
///
/// Can be a Mobile Number, Business Number, Agent
/// Till, Paybill or Business number, or Merchant Buy Goods.
#[serde(rename = "CPI")]
pub credit_party_identifier: &'mpesa str,
/// Size of the QR code image in pixels.
///
/// QR code image will always be a square image.
pub size: &'mpesa str,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DynamicQRResponse {
#[serde(rename(deserialize = "QRCode"))]
pub qr_code: String,
pub response_code: String,
pub response_description: String,
}
/// Dynamic QR builder struct
#[derive(Builder, Debug, Clone)]
#[builder(build_fn(error = "MpesaError"))]
pub struct DynamicQR<'mpesa, Env: ApiEnvironment> {
#[builder(pattern = "immutable")]
client: &'mpesa Mpesa<Env>,
/// Name of the Company/M-Pesa Merchant Name
#[builder(setter(into))]
merchant_name: &'mpesa str,
/// Transaction Reference Number
#[builder(setter(into))]
amount: f64,
/// The total amount of the transaction
ref_no: &'mpesa str,
/// Transaction Type
///
/// This can be a `TransactionType` or a `&str`
/// The `&str` must be one of the following:
/// - `BG` for Buy Goods
/// - `PB` for Pay Bill
/// - `WA` Withdraw Cash
/// - `SM` Send Money (Mobile Number)
/// - `SB` Sent to Business. Business number CPI in MSISDN format.
#[builder(try_setter, setter(into))]
transaction_type: TransactionType,
/// Credit Party Identifier.
/// Can be a Mobile Number, Business Number, Agent
/// Till, Paybill or Business number, or Merchant Buy Goods.
#[builder(setter(into))]
credit_party_identifier: &'mpesa str,
/// Size of the QR code image in pixels.
///
/// QR code image will always be a square image.
#[builder(setter(into))]
size: &'mpesa str,
}
impl<'mpesa, Env: ApiEnvironment> From<DynamicQR<'mpesa, Env>> for DynamicQRRequest<'mpesa> {
fn from(express: DynamicQR<'mpesa, Env>) -> DynamicQRRequest<'mpesa> {
DynamicQRRequest {
merchant_name: express.merchant_name,
ref_no: express.ref_no,
amount: express.amount,
transaction_type: express.transaction_type,
credit_party_identifier: express.credit_party_identifier,
size: express.size,
}
}
}
impl<'mpesa, Env: ApiEnvironment> DynamicQR<'mpesa, Env> {
pub(crate) fn builder(client: &'mpesa Mpesa<Env>) -> DynamicQRBuilder<'mpesa, Env> {
DynamicQRBuilder::default().client(client)
}
/// # Build Dynamic QR
///
/// Returns a `DynamicQR` which can be used to send a request
pub fn from_request(
client: &'mpesa Mpesa<Env>,
request: DynamicQRRequest<'mpesa>,
) -> DynamicQR<'mpesa, Env> {
DynamicQR {
client,
merchant_name: request.merchant_name,
ref_no: request.ref_no,
amount: request.amount,
transaction_type: request.transaction_type,
credit_party_identifier: request.credit_party_identifier,
size: request.size,
}
}
/// # Generate a Dynamic QR
///
/// This enables Safaricom M-PESA customers who
/// have My Safaricom App or M-PESA app, to scan a QR (Quick Response)
/// code, to capture till number and amount then authorize to pay for goods
/// and services at select LIPA NA M-PESA (LNM) merchant outlets.
///
/// # Response
/// A successful request returns a `DynamicQRResponse` type
/// which contains the QR code
///
/// # Errors
/// Returns a `MpesaError` on failure
pub async fn send(self) -> MpesaResult<DynamicQRResponse> {
let url = format!("{}{}", self.client.environment.base_url(), DYNAMIC_QR_URL);
let response = self
.client
.http_client
.post(&url)
.bearer_auth(self.client.auth().await?)
.json::<DynamicQRRequest>(&self.into())
.send()
.await?;
if response.status().is_success() {
let value = response.json::<_>().await?;
return Ok(value);
}
let value = response.json().await?;
Err(MpesaError::MpesaDynamicQrError(value))
}
}