From bb2acae40b42899bf741b2a10af2da073338a6b5 Mon Sep 17 00:00:00 2001 From: Yasir Shariff Date: Mon, 8 Jul 2024 16:28:07 +0300 Subject: [PATCH] feat(bill-manager): Update cached dependency and refactor client code * Update cached dependency to version 0.52 * Refactor client code to conditionally include openssl-related imports based on feature flags * Move Invoice and InvoiceItem structs to bill_manager module --- Cargo.toml | 2 +- src/client.rs | 56 +++++++++++++++++-- src/constants.rs | 41 -------------- src/errors.rs | 7 +++ src/lib.rs | 3 +- src/services/bill_manager/bulk_invoice.rs | 2 +- src/services/bill_manager/mod.rs | 44 +++++++++++++++ src/services/bill_manager/single_invoice.rs | 2 +- src/services/mod.rs | 16 +++++- .../bill_manager_test/bulk_invoice_test.rs | 3 +- .../bill_manager_test/single_invoice_test.rs | 3 +- 11 files changed, 125 insertions(+), 54 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8a8be47ed..5e2e6179b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ transaction_status = ["dep:openssl"] [dependencies] -cached = { version = "0.49", features = ["wasm", "async", "proc_macro"] } +cached = { version = "0.52", features = ["async", "proc_macro"] } chrono = { version = "0.4", optional = true, default-features = false, features = [ "clock", "serde", diff --git a/src/client.rs b/src/client.rs index 5b804b635..cda9e49ad 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,8 +2,29 @@ use std::cell::RefCell; use std::time::Duration; use cached::Cached; +#[cfg(any( + feature = "account_balance", + feature = "b2b", + feature = "b2c", + feature = "transaction_reversal", + feature = "transaction_status" +))] use openssl::base64; +#[cfg(any( + feature = "account_balance", + feature = "b2b", + feature = "b2c", + feature = "transaction_reversal", + feature = "transaction_status" +))] use openssl::rsa::Padding; +#[cfg(any( + feature = "account_balance", + feature = "b2b", + feature = "b2c", + feature = "transaction_reversal", + feature = "transaction_status" +))] use openssl::x509::X509; use reqwest::Client as HttpClient; use secrecy::{ExposeSecret, Secret}; @@ -12,13 +33,31 @@ use serde::Serialize; use crate::auth::AUTH; use crate::environment::ApiEnvironment; +#[cfg(feature = "account_balance")] +use crate::services::AccountBalanceBuilder; +#[cfg(feature = "b2b")] +use crate::services::B2bBuilder; +#[cfg(feature = "b2c")] +use crate::services::B2cBuilder; +#[cfg(feature = "c2b_register")] +use crate::services::C2bRegisterBuilder; +#[cfg(feature = "c2b_simulate")] +use crate::services::C2bSimulateBuilder; +#[cfg(feature = "transaction_status")] +use crate::services::TransactionStatusBuilder; +#[cfg(feature = "bill_manager")] use crate::services::{ - AccountBalanceBuilder, B2bBuilder, B2cBuilder, BulkInvoiceBuilder, C2bRegisterBuilder, - C2bSimulateBuilder, CancelInvoiceBuilder, DynamicQR, DynamicQRBuilder, MpesaExpress, - MpesaExpressBuilder, MpesaExpressQuery, MpesaExpressQueryBuilder, OnboardBuilder, - OnboardModifyBuilder, ReconciliationBuilder, SingleInvoiceBuilder, TransactionReversal, - TransactionReversalBuilder, TransactionStatusBuilder, + BulkInvoiceBuilder, CancelInvoiceBuilder, OnboardBuilder, OnboardModifyBuilder, + ReconciliationBuilder, SingleInvoiceBuilder, }; +#[cfg(feature = "dynamic_qr")] +use crate::services::{DynamicQR, DynamicQRBuilder}; +#[cfg(feature = "express")] +use crate::services::{ + MpesaExpress, MpesaExpressBuilder, MpesaExpressQuery, MpesaExpressQueryBuilder, +}; +#[cfg(feature = "transaction_reversal")] +use crate::services::{TransactionReversal, TransactionReversalBuilder}; use crate::{auth, MpesaError, MpesaResult, ResponseError}; /// Source: [test credentials](https://developer.safaricom.co.ke/test_credentials) @@ -273,6 +312,13 @@ impl Mpesa { /// /// # Errors /// Returns `EncryptionError` variant of `MpesaError` + #[cfg(any( + feature = "account_balance", + feature = "b2b", + feature = "b2c", + feature = "transaction_reversal", + feature = "transaction_status" + ))] pub(crate) fn gen_security_credentials(&self) -> MpesaResult { let pem = self.certificate.as_bytes(); let cert = X509::from_pem(pem)?; diff --git a/src/constants.rs b/src/constants.rs index cf99c0865..b8048f9e5 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,6 +1,5 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; -use chrono::prelude::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -105,46 +104,6 @@ impl Display for SendRemindersTypes { } } -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Invoice<'i> { - pub amount: f64, - pub account_reference: &'i str, - pub billed_full_name: &'i str, - pub billed_period: &'i str, - pub billed_phone_number: &'i str, - pub due_date: DateTime, - pub external_reference: &'i str, - #[serde(skip_serializing_if = "Option::is_none")] - pub invoice_items: Option>>, - pub invoice_name: &'i str, -} - -impl<'i> Display for Invoice<'i> { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - write!( - f, - "amount: {}, account_reference: {}, due_date: {}, invoice_name: {}", - self.amount, - self.account_reference, - self.due_date.format("%Y-%m-%d"), - self.invoice_name, - ) - } -} - -#[derive(Debug, Serialize)] -pub struct InvoiceItem<'i> { - pub amount: f64, - pub item_name: &'i str, -} - -impl<'i> Display for InvoiceItem<'i> { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - write!(f, "amount: {}, item_name: {}", self.amount, self.item_name) - } -} - #[derive(Debug, Clone, Copy, Serialize)] pub enum TransactionType { /// Send Money(Mobile number). diff --git a/src/errors.rs b/src/errors.rs index 546ade537..6fe3f19a5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -15,6 +15,13 @@ pub enum MpesaError { ParseError(#[from] serde_json::Error), #[error("An error has occurred while retrieving an environmental variable")] EnvironmentalVariableError(#[from] VarError), + #[cfg(any( + feature = "account_balance", + feature = "b2b", + feature = "b2c", + feature = "transaction_reversal", + feature = "transaction_status" + ))] #[error("An error has occurred while generating security credentials")] EncryptionError(#[from] openssl::error::ErrorStack), #[error("{0}")] diff --git a/src/lib.rs b/src/lib.rs index f764ec1f9..95621ddeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,7 @@ pub mod validator; pub use client::Mpesa; pub use constants::{ - CommandId, IdentifierTypes, Invoice, InvoiceItem, ResponseType, SendRemindersTypes, - TransactionType, + CommandId, IdentifierTypes, ResponseType, SendRemindersTypes, TransactionType, }; pub use environment::ApiEnvironment; pub use environment::Environment::{self, Production, Sandbox}; diff --git a/src/services/bill_manager/bulk_invoice.rs b/src/services/bill_manager/bulk_invoice.rs index f2dc051e3..b3655e666 100644 --- a/src/services/bill_manager/bulk_invoice.rs +++ b/src/services/bill_manager/bulk_invoice.rs @@ -2,8 +2,8 @@ use serde::Deserialize; +use super::Invoice; use crate::client::Mpesa; -use crate::constants::Invoice; use crate::errors::{MpesaError, MpesaResult}; const BILL_MANAGER_BULK_INVOICE_API_URL: &str = "v1/billmanager-invoice/bulk-invoicing"; diff --git a/src/services/bill_manager/mod.rs b/src/services/bill_manager/mod.rs index 6394e53eb..e8b2d679b 100644 --- a/src/services/bill_manager/mod.rs +++ b/src/services/bill_manager/mod.rs @@ -5,9 +5,53 @@ mod onboard_modify; mod reconciliation; mod single_invoice; +use std::fmt::{Display, Formatter, Result as FmtResult}; + pub use bulk_invoice::{BulkInvoiceBuilder, BulkInvoiceResponse}; pub use cancel_invoice::{CancelInvoiceBuilder, CancelInvoiceResponse}; +use chrono::{DateTime, Utc}; pub use onboard::{OnboardBuilder, OnboardResponse}; pub use onboard_modify::{OnboardModifyBuilder, OnboardModifyResponse}; pub use reconciliation::{ReconciliationBuilder, ReconciliationResponse}; +use serde::Serialize; pub use single_invoice::{SingleInvoiceBuilder, SingleInvoiceResponse}; + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Invoice<'i> { + pub amount: f64, + pub account_reference: &'i str, + pub billed_full_name: &'i str, + pub billed_period: &'i str, + pub billed_phone_number: &'i str, + pub due_date: DateTime, + pub external_reference: &'i str, + #[serde(skip_serializing_if = "Option::is_none")] + pub invoice_items: Option>>, + pub invoice_name: &'i str, +} + +impl<'i> Display for Invoice<'i> { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + write!( + f, + "amount: {}, account_reference: {}, due_date: {}, invoice_name: {}", + self.amount, + self.account_reference, + self.due_date.format("%Y-%m-%d"), + self.invoice_name, + ) + } +} + +#[derive(Debug, Serialize)] +pub struct InvoiceItem<'i> { + pub amount: f64, + pub item_name: &'i str, +} + +impl<'i> Display for InvoiceItem<'i> { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + write!(f, "amount: {}, item_name: {}", self.amount, self.item_name) + } +} diff --git a/src/services/bill_manager/single_invoice.rs b/src/services/bill_manager/single_invoice.rs index 2451c846d..77d7f35c1 100644 --- a/src/services/bill_manager/single_invoice.rs +++ b/src/services/bill_manager/single_invoice.rs @@ -3,8 +3,8 @@ use chrono::prelude::{DateTime, Utc}; use serde::Deserialize; +use super::{Invoice, InvoiceItem}; use crate::client::Mpesa; -use crate::constants::{Invoice, InvoiceItem}; use crate::errors::{MpesaError, MpesaResult}; const BILL_MANAGER_SINGLE_INVOICE_API_URL: &str = "v1/billmanager-invoice/single-invoicing"; diff --git a/src/services/mod.rs b/src/services/mod.rs index cb7d579eb..141ac79be 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -17,15 +17,27 @@ //! 9. [Transaction Status](https://developer.safaricom.co.ke/APIs/TransactionStatus) //! 10. [Dynamic QR](https://developer.safaricom.co.ke/APIs/DynamicQRCode) +//! 11. [Express](https://developer.safaricom.co.ke/APIs/ExpressCheckout) + +#[cfg(feature = "account_balance")] mod account_balance; +#[cfg(feature = "b2b")] mod b2b; +#[cfg(feature = "b2c")] mod b2c; +#[cfg(feature = "bill_manager")] mod bill_manager; +#[cfg(feature = "c2b_register")] mod c2b_register; +#[cfg(feature = "c2b_simulate")] mod c2b_simulate; +#[cfg(feature = "dynamic_qr")] mod dynamic_qr; +#[cfg(feature = "express")] mod express; +#[cfg(feature = "transaction_reversal")] mod transaction_reversal; +#[cfg(feature = "transaction_status")] mod transaction_status; #[cfg(feature = "account_balance")] @@ -53,4 +65,6 @@ pub use transaction_reversal::{ TransactionReversalResponse, }; #[cfg(feature = "transaction_status")] -pub use transaction_status::{TransactionStatusBuilder, TransactionStatusResponse}; +pub use transaction_status::{ + TransactionStatusBuilder, TransactionStatusPayload, TransactionStatusResponse, +}; diff --git a/tests/mpesa-rust/bill_manager_test/bulk_invoice_test.rs b/tests/mpesa-rust/bill_manager_test/bulk_invoice_test.rs index 6e05928e9..a0c324a42 100644 --- a/tests/mpesa-rust/bill_manager_test/bulk_invoice_test.rs +++ b/tests/mpesa-rust/bill_manager_test/bulk_invoice_test.rs @@ -1,5 +1,6 @@ use chrono::prelude::Utc; -use mpesa::{Invoice, InvoiceItem, MpesaError}; +use mpesa::services::{Invoice, InvoiceItem}; +use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; diff --git a/tests/mpesa-rust/bill_manager_test/single_invoice_test.rs b/tests/mpesa-rust/bill_manager_test/single_invoice_test.rs index b876de4f9..6e8fd686a 100644 --- a/tests/mpesa-rust/bill_manager_test/single_invoice_test.rs +++ b/tests/mpesa-rust/bill_manager_test/single_invoice_test.rs @@ -1,5 +1,6 @@ use chrono::prelude::Utc; -use mpesa::{InvoiceItem, MpesaError}; +use mpesa::services::InvoiceItem; +use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate};