From 68d31777014de07a768a103a3a15fc3a6ebbcf5d Mon Sep 17 00:00:00 2001 From: Yasir Shariff Date: Mon, 13 Nov 2023 07:04:42 +0300 Subject: [PATCH 1/4] feat/add-support-for-wasm32 --- Cargo.toml | 51 +++++++++++++++++++-------- src/client.rs | 61 +++++++++++++++++++++++---------- src/errors.rs | 2 +- src/services/express_request.rs | 5 +-- 4 files changed, 82 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9b7707636..322e7fba4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,29 +10,50 @@ readme = "./README.md" license = "MIT" [dependencies] -chrono = {version = "0.4", optional = true, default-features = false, features = ["clock", "serde"] } -openssl = {version = "0.10", optional = true} -reqwest = {version = "0.11", features = ["json"]} -secrecy = "0.8.0" -serde = {version="1.0", features= ["derive"]} +base64 = { version = "0.21", optional = true } +chrono = { version = "0.4", optional = true, default-features = false, features = [ + "clock", + "serde", +] } +rand = "0.8" +reqwest = { version = "0.11", features = ["json"] } +rsa = { version = "0.9", optional = true } +secrecy = "0.8" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_repr = "0.1" -thiserror = "1.0.37" -wiremock = "0.5" +thiserror = "1.0" +x509-parser = { version = "0.15", optional = true } +getrandom = { version = "0.2", optional = true, features = ["js"] } + + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.2", features = ["js"] } + [dev-dependencies] dotenv = "0.15" -tokio = {version = "1", features = ["rt", "rt-multi-thread", "macros"]} +tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } wiremock = "0.5" [features] -default = ["account_balance", "b2b", "b2c", "bill_manager", "c2b_register", "c2b_simulate", "express_request", "transaction_reversal", "transaction_status"] -account_balance = ["dep:openssl"] -b2b = ["dep:openssl"] -b2c = ["dep:openssl"] +default = [ + "account_balance", + "b2b", + "b2c", + "bill_manager", + "c2b_register", + "c2b_simulate", + "express_request", + "transaction_reversal", + "transaction_status", +] +account_balance = [] +b2b = ["dep:base64", "dep:x509-parser", "dep:rsa"] +b2c = ["dep:base64", "dep:x509-parser", "dep:rsa"] bill_manager = ["dep:chrono"] c2b_register = [] c2b_simulate = [] -express_request = ["dep:chrono"] -transaction_reversal = ["dep:openssl"] -transaction_status= ["dep:openssl"] +express_request = ["dep:chrono", "dep:base64"] +transaction_reversal = ["dep:base64", "dep:x509-parser", "dep:rsa"] +transaction_status = ["dep:base64", "dep:x509-parser", "dep:rsa"] diff --git a/src/client.rs b/src/client.rs index 3a9640e4b..d46367a22 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,4 +1,5 @@ use crate::environment::ApiEnvironment; + use crate::services::{ AccountBalanceBuilder, B2bBuilder, B2cBuilder, BulkInvoiceBuilder, C2bRegisterBuilder, C2bSimulateBuilder, CancelInvoiceBuilder, MpesaExpressRequestBuilder, OnboardBuilder, @@ -6,10 +7,12 @@ use crate::services::{ TransactionStatusBuilder, }; use crate::{ApiError, MpesaError}; -use openssl::base64; -use openssl::rsa::Padding; -use openssl::x509::X509; + +use base64::engine::general_purpose; +use base64::Engine as _; use reqwest::Client as HttpClient; + +use rsa::{BigUint, Pkcs1v15Encrypt}; use secrecy::{ExposeSecret, Secret}; use serde_json::Value; use std::cell::RefCell; @@ -50,8 +53,6 @@ impl<'mpesa, Env: ApiEnvironment> Mpesa { let http_client = HttpClient::builder() .connect_timeout(std::time::Duration::from_millis(10_000)) .user_agent(format!("mpesa-rust@{CARGO_PACKAGE_VERSION}")) - // TODO: Potentialy return a `Result` enum from Mpesa::new? - // Making assumption that creation of http client cannot fail .build() .expect("Error building http client"); Self { @@ -508,22 +509,44 @@ impl<'mpesa, Env: ApiEnvironment> Mpesa { /// /// # Errors /// Returns `EncryptionError` variant of `MpesaError` + pub(crate) fn gen_security_credentials(&self) -> MpesaResult { let pem = self.environment.get_certificate().as_bytes(); - let cert = X509::from_pem(pem)?; - // getting the public and rsa keys - let pub_key = cert.public_key()?; - let rsa_key = pub_key.rsa()?; - // configuring the buffer - let buf_len = pub_key.size(); - let mut buffer = vec![0; buf_len]; - - rsa_key.public_encrypt( - self.initiator_password().as_bytes(), - &mut buffer, - Padding::PKCS1, - )?; - Ok(base64::encode_block(&buffer)) + + let (_, cert) = x509_parser::pem::parse_x509_pem(pem) + .map_err(|e| MpesaError::EncryptionError(e.to_string()))?; + + let cert = cert + .parse_x509() + .map_err(|e| MpesaError::EncryptionError(e.to_string()))?; + + let key = cert + .public_key() + .parsed() + .map_err(|e| MpesaError::EncryptionError(e.to_string()))?; + + let rsa = match key { + x509_parser::public_key::PublicKey::RSA(rsa_key) => rsa_key, + _ => unreachable!("Invalid public key type"), + }; + + let value = rsa::RsaPublicKey::new( + BigUint::from_bytes_be(rsa.modulus), + BigUint::from_bytes_be(rsa.exponent), + ) + .map_err(|e| MpesaError::EncryptionError(e.to_string()))?; + + let value = value + .encrypt( + &mut rand::rngs::OsRng, + Pkcs1v15Encrypt, + self.initiator_password().as_bytes(), + ) + .map_err(|e| MpesaError::EncryptionError(e.to_string()))?; + + let value = general_purpose::STANDARD.encode(value); + + Ok(value) } } diff --git a/src/errors.rs b/src/errors.rs index 2adc20891..5c21c59b1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -41,7 +41,7 @@ pub enum MpesaError { #[error("An error has occured while retreiving an environmental variable")] EnvironmentalVariableError(#[from] VarError), #[error("An error has occurred while generating security credentials")] - EncryptionError(#[from] openssl::error::ErrorStack), + EncryptionError(String), #[error("{0}")] Message(&'static str), } diff --git a/src/services/express_request.rs b/src/services/express_request.rs index a08ce2d16..2d9d1ff86 100644 --- a/src/services/express_request.rs +++ b/src/services/express_request.rs @@ -2,8 +2,9 @@ use crate::client::{Mpesa, MpesaResult}; use crate::constants::CommandId; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; +use base64::engine::general_purpose; +use base64::Engine as _; use chrono::prelude::Local; -use openssl::base64; use serde::{Deserialize, Serialize}; /// Source: [test credentials](https://developer.safaricom.co.ke/test_credentials) @@ -100,7 +101,7 @@ impl<'mpesa, Env: ApiEnvironment> MpesaExpressRequestBuilder<'mpesa, Env> { /// Returns the encoded password and a timestamp string fn generate_password_and_timestamp(&self) -> (String, String) { let timestamp = Local::now().format("%Y%m%d%H%M%S").to_string(); - let encoded_password = base64::encode_block( + let encoded_password = general_purpose::STANDARD.encode( format!( "{}{}{}", self.business_short_code(), From daca906f7ea8057f16e18e4b39d3284aef03bf35 Mon Sep 17 00:00:00 2001 From: Yasir Shariff Date: Mon, 13 Nov 2023 07:04:54 +0300 Subject: [PATCH 2/4] feat/add-support-for-wasm32 --- Cargo.toml | 1 - src/client.rs | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 322e7fba4..f463b04ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ serde_json = "1.0" serde_repr = "0.1" thiserror = "1.0" x509-parser = { version = "0.15", optional = true } -getrandom = { version = "0.2", optional = true, features = ["js"] } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/src/client.rs b/src/client.rs index d46367a22..4ed3c6144 100644 --- a/src/client.rs +++ b/src/client.rs @@ -20,7 +20,6 @@ use std::cell::RefCell; /// Source: [test credentials](https://developer.safaricom.co.ke/test_credentials) const DEFAULT_INITIATOR_PASSWORD: &str = "Safcom496!"; /// Get current package version from metadata -const CARGO_PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION"); /// `Result` enum type alias pub type MpesaResult = Result; @@ -50,11 +49,16 @@ impl<'mpesa, Env: ApiEnvironment> Mpesa { /// # Panics /// This method can panic if a TLS backend cannot be initialized for the internal http_client pub fn new>(client_key: S, client_secret: S, environment: Env) -> Self { + #[cfg(target_arch = "wasm32")] + let http_client = HttpClient::new(); + + #[cfg(not(target_arch = "wasm32"))] let http_client = HttpClient::builder() .connect_timeout(std::time::Duration::from_millis(10_000)) - .user_agent(format!("mpesa-rust@{CARGO_PACKAGE_VERSION}")) + .user_agent(format!("mpesa-rust@{}", env!("CARGO_PKG_VERSION"))) .build() .expect("Error building http client"); + Self { client_key: client_key.into(), client_secret: Secret::new(client_secret.into()), From dda77ef7107903375775db730ad47586fbf60200 Mon Sep 17 00:00:00 2001 From: Yasir Shariff Date: Mon, 13 Nov 2023 07:18:13 +0300 Subject: [PATCH 3/4] chore: cleanup --- Cargo.toml | 56 +++++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f463b04ed..d6c26105b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,13 +9,38 @@ repository = "https://github.com/collinsmuriuki/mpesa-rust" readme = "./README.md" license = "MIT" +[features] +default = [ + "account_balance", + "b2b", + "b2c", + "bill_manager", + "c2b_register", + "c2b_simulate", + "express_request", + "transaction_reversal", + "transaction_status", +] +account_balance = [] +b2b = ["dep:base64", "dep:x509-parser", "dep:rsa", "dep:rand"] +b2c = ["dep:base64", "dep:x509-parser", "dep:rsa", "dep:rand"] +bill_manager = ["dep:chrono"] +c2b_register = [] +c2b_simulate = [] +express_request = ["dep:chrono", "dep:base64"] +transaction_reversal = ["dep:base64", "dep:x509-parser", "dep:rsa", "dep:rand"] +transaction_status = ["dep:base64", "dep:x509-parser", "dep:rsa", "dep:rand"] + + [dependencies] base64 = { version = "0.21", optional = true } chrono = { version = "0.4", optional = true, default-features = false, features = [ "clock", "serde", ] } -rand = "0.8" +rand = { version = "0.8", optional = true, default-features = false, features = [ + "std", +] } reqwest = { version = "0.11", features = ["json"] } rsa = { version = "0.9", optional = true } secrecy = "0.8" @@ -26,33 +51,12 @@ thiserror = "1.0" x509-parser = { version = "0.15", optional = true } -[target.'cfg(target_arch = "wasm32")'.dependencies] -getrandom = { version = "0.2", features = ["js"] } - - [dev-dependencies] dotenv = "0.15" tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } wiremock = "0.5" -[features] -default = [ - "account_balance", - "b2b", - "b2c", - "bill_manager", - "c2b_register", - "c2b_simulate", - "express_request", - "transaction_reversal", - "transaction_status", -] -account_balance = [] -b2b = ["dep:base64", "dep:x509-parser", "dep:rsa"] -b2c = ["dep:base64", "dep:x509-parser", "dep:rsa"] -bill_manager = ["dep:chrono"] -c2b_register = [] -c2b_simulate = [] -express_request = ["dep:chrono", "dep:base64"] -transaction_reversal = ["dep:base64", "dep:x509-parser", "dep:rsa"] -transaction_status = ["dep:base64", "dep:x509-parser", "dep:rsa"] + +# Support Wasm +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.2", features = ["js"] } From a443de22ff25d8f7bfc0fe49c12720f61b3c0875 Mon Sep 17 00:00:00 2001 From: Yasir Shariff Date: Mon, 13 Nov 2023 07:20:02 +0300 Subject: [PATCH 4/4] chore: fix imports order --- src/client.rs | 22 +++++++++---------- src/constants.rs | 3 ++- src/environment.rs | 4 +++- src/errors.rs | 4 +++- src/services/account_balance.rs | 3 ++- src/services/b2b.rs | 3 ++- src/services/b2c.rs | 3 ++- src/services/bill_manager/bulk_invoice.rs | 3 ++- src/services/bill_manager/cancel_invoice.rs | 3 ++- src/services/bill_manager/onboard.rs | 3 ++- src/services/bill_manager/onboard_modify.rs | 3 ++- src/services/bill_manager/reconciliation.rs | 5 +++-- src/services/bill_manager/single_invoice.rs | 5 +++-- src/services/c2b_register.rs | 3 ++- src/services/c2b_simulate.rs | 3 ++- src/services/express_request.rs | 9 ++++---- src/services/transaction_reversal.rs | 12 +++------- src/services/transaction_status.rs | 12 +++------- tests/mpesa-rust/account_balance_test.rs | 3 ++- tests/mpesa-rust/b2c_test.rs | 3 ++- .../bill_manager_test/bulk_invoice_test.rs | 3 ++- .../bill_manager_test/cancel_invoice_test.rs | 3 ++- .../bill_manager_test/onboard_modify_test.rs | 3 ++- .../bill_manager_test/onboard_test.rs | 3 ++- .../bill_manager_test/reconciliation_test.rs | 3 ++- .../bill_manager_test/single_invoice_test.rs | 3 ++- tests/mpesa-rust/c2b_register_test.rs | 3 ++- tests/mpesa-rust/c2b_simulate_test.rs | 3 ++- tests/mpesa-rust/stk_push_test.rs | 3 ++- tests/mpesa-rust/transaction_reversal_test.rs | 3 ++- tests/mpesa-rust/transaction_status_test.rs | 1 - 31 files changed, 77 insertions(+), 63 deletions(-) diff --git a/src/client.rs b/src/client.rs index 4ed3c6144..19212afd8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,5 +1,13 @@ -use crate::environment::ApiEnvironment; +use std::cell::RefCell; +use base64::engine::general_purpose; +use base64::Engine as _; +use reqwest::Client as HttpClient; +use rsa::{BigUint, Pkcs1v15Encrypt}; +use secrecy::{ExposeSecret, Secret}; +use serde_json::Value; + +use crate::environment::ApiEnvironment; use crate::services::{ AccountBalanceBuilder, B2bBuilder, B2cBuilder, BulkInvoiceBuilder, C2bRegisterBuilder, C2bSimulateBuilder, CancelInvoiceBuilder, MpesaExpressRequestBuilder, OnboardBuilder, @@ -8,15 +16,6 @@ use crate::services::{ }; use crate::{ApiError, MpesaError}; -use base64::engine::general_purpose; -use base64::Engine as _; -use reqwest::Client as HttpClient; - -use rsa::{BigUint, Pkcs1v15Encrypt}; -use secrecy::{ExposeSecret, Secret}; -use serde_json::Value; -use std::cell::RefCell; - /// Source: [test credentials](https://developer.safaricom.co.ke/test_credentials) const DEFAULT_INITIATOR_PASSWORD: &str = "Safcom496!"; /// Get current package version from metadata @@ -556,9 +555,8 @@ impl<'mpesa, Env: ApiEnvironment> Mpesa { #[cfg(test)] mod tests { - use crate::Sandbox; - use super::*; + use crate::Sandbox; #[test] fn test_setting_initator_password() { diff --git a/src/constants.rs b/src/constants.rs index 418ad5a52..0bdd9fa1b 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,7 +1,8 @@ +use std::fmt::{Display, Formatter, Result as FmtResult}; + use chrono::prelude::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::fmt::{Display, Formatter, Result as FmtResult}; /// Mpesa command ids #[derive(Debug, Serialize, Deserialize)] diff --git a/src/environment.rs b/src/environment.rs index eb6f1f579..0000cb959 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -10,8 +10,10 @@ //! and the `public key` an X509 certificate used for encrypting initiator passwords. You can read more about that from //! the Safaricom API [docs](https://developer.safaricom.co.ke/docs?javascript#security-credentials). +use std::convert::TryFrom; +use std::str::FromStr; + use crate::MpesaError; -use std::{convert::TryFrom, str::FromStr}; #[derive(Debug, Clone)] /// Enum to map to desired environment so as to access certificate diff --git a/src/errors.rs b/src/errors.rs index 5c21c59b1..dba407616 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,7 @@ +use std::env::VarError; +use std::fmt; + use serde::{Deserialize, Serialize}; -use std::{env::VarError, fmt}; /// Mpesa error stack #[derive(thiserror::Error, Debug)] diff --git a/src/services/account_balance.rs b/src/services/account_balance.rs index b3d7ba4d4..b59bf8b4f 100644 --- a/src/services/account_balance.rs +++ b/src/services/account_balance.rs @@ -1,8 +1,9 @@ +use serde::{Deserialize, Serialize}; + use crate::client::MpesaResult; use crate::constants::{CommandId, IdentifierTypes}; use crate::environment::ApiEnvironment; use crate::{Mpesa, MpesaError}; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize)] /// Account Balance payload diff --git a/src/services/b2b.rs b/src/services/b2b.rs index 08746109e..aaaab1341 100644 --- a/src/services/b2b.rs +++ b/src/services/b2b.rs @@ -1,8 +1,9 @@ +use serde::{Deserialize, Serialize}; + use crate::client::{Mpesa, MpesaResult}; use crate::constants::{CommandId, IdentifierTypes}; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize)] struct B2bPayload<'mpesa> { diff --git a/src/services/b2c.rs b/src/services/b2c.rs index 6ae46cfdc..6489ef329 100644 --- a/src/services/b2c.rs +++ b/src/services/b2c.rs @@ -1,7 +1,8 @@ +use serde::{Deserialize, Serialize}; + use crate::client::MpesaResult; use crate::environment::ApiEnvironment; use crate::{CommandId, Mpesa, MpesaError}; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize)] /// Payload to allow for b2c transactions: diff --git a/src/services/bill_manager/bulk_invoice.rs b/src/services/bill_manager/bulk_invoice.rs index 15618b9b1..6eeee012c 100644 --- a/src/services/bill_manager/bulk_invoice.rs +++ b/src/services/bill_manager/bulk_invoice.rs @@ -1,8 +1,9 @@ +use serde::Deserialize; + use crate::client::{Mpesa, MpesaResult}; use crate::constants::Invoice; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; -use serde::Deserialize; #[derive(Clone, Debug, Deserialize)] pub struct BulkInvoiceResponse { diff --git a/src/services/bill_manager/cancel_invoice.rs b/src/services/bill_manager/cancel_invoice.rs index aa159dd30..2b6c23641 100644 --- a/src/services/bill_manager/cancel_invoice.rs +++ b/src/services/bill_manager/cancel_invoice.rs @@ -1,7 +1,8 @@ +use serde::{Deserialize, Serialize}; + use crate::client::{Mpesa, MpesaResult}; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] diff --git a/src/services/bill_manager/onboard.rs b/src/services/bill_manager/onboard.rs index 9bb6f20dd..a6456c9d3 100644 --- a/src/services/bill_manager/onboard.rs +++ b/src/services/bill_manager/onboard.rs @@ -1,8 +1,9 @@ +use serde::{Deserialize, Serialize}; + use crate::client::{Mpesa, MpesaResult}; use crate::constants::SendRemindersTypes; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize)] /// Payload to opt you in as a biller to the bill manager features. diff --git a/src/services/bill_manager/onboard_modify.rs b/src/services/bill_manager/onboard_modify.rs index 36ff7e069..fecb33b03 100644 --- a/src/services/bill_manager/onboard_modify.rs +++ b/src/services/bill_manager/onboard_modify.rs @@ -1,8 +1,9 @@ +use serde::{Deserialize, Serialize}; + use crate::client::{Mpesa, MpesaResult}; use crate::constants::SendRemindersTypes; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize)] /// Payload to modify opt-in details to the bill manager api. diff --git a/src/services/bill_manager/reconciliation.rs b/src/services/bill_manager/reconciliation.rs index baf470b6f..59a7d6842 100644 --- a/src/services/bill_manager/reconciliation.rs +++ b/src/services/bill_manager/reconciliation.rs @@ -1,8 +1,9 @@ +use chrono::prelude::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + use crate::client::{Mpesa, MpesaResult}; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; -use chrono::prelude::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] diff --git a/src/services/bill_manager/single_invoice.rs b/src/services/bill_manager/single_invoice.rs index 5ce438bc0..a04c774d3 100644 --- a/src/services/bill_manager/single_invoice.rs +++ b/src/services/bill_manager/single_invoice.rs @@ -1,9 +1,10 @@ +use chrono::prelude::{DateTime, Utc}; +use serde::Deserialize; + use crate::client::{Mpesa, MpesaResult}; use crate::constants::{Invoice, InvoiceItem}; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; -use chrono::prelude::{DateTime, Utc}; -use serde::Deserialize; #[derive(Clone, Debug, Deserialize)] pub struct SingleInvoiceResponse { diff --git a/src/services/c2b_register.rs b/src/services/c2b_register.rs index fe214e7e6..3e4ad85a7 100644 --- a/src/services/c2b_register.rs +++ b/src/services/c2b_register.rs @@ -1,8 +1,9 @@ +use serde::{Deserialize, Serialize}; + use crate::client::{Mpesa, MpesaResult}; use crate::constants::ResponseType; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize)] /// Payload to register the 3rd party’s confirmation and validation URLs to M-Pesa diff --git a/src/services/c2b_simulate.rs b/src/services/c2b_simulate.rs index b9a41fdb6..157642ecc 100644 --- a/src/services/c2b_simulate.rs +++ b/src/services/c2b_simulate.rs @@ -1,8 +1,9 @@ +use serde::{Deserialize, Serialize}; + use crate::client::{Mpesa, MpesaResult}; use crate::constants::CommandId; use crate::environment::ApiEnvironment; use crate::errors::MpesaError; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize)] /// Payload to make payment requests from C2B. diff --git a/src/services/express_request.rs b/src/services/express_request.rs index 2d9d1ff86..f5421363d 100644 --- a/src/services/express_request.rs +++ b/src/services/express_request.rs @@ -1,12 +1,13 @@ -use crate::client::{Mpesa, MpesaResult}; -use crate::constants::CommandId; -use crate::environment::ApiEnvironment; -use crate::errors::MpesaError; use base64::engine::general_purpose; use base64::Engine as _; use chrono::prelude::Local; use serde::{Deserialize, Serialize}; +use crate::client::{Mpesa, MpesaResult}; +use crate::constants::CommandId; +use crate::environment::ApiEnvironment; +use crate::errors::MpesaError; + /// Source: [test credentials](https://developer.safaricom.co.ke/test_credentials) static DEFAULT_PASSKEY: &str = "bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919"; diff --git a/src/services/transaction_reversal.rs b/src/services/transaction_reversal.rs index 5aec81853..0080485ce 100644 --- a/src/services/transaction_reversal.rs +++ b/src/services/transaction_reversal.rs @@ -1,12 +1,6 @@ -use serde::Deserialize; -use serde::Serialize; - -use crate::ApiEnvironment; -use crate::CommandId; -use crate::IdentifierTypes; -use crate::Mpesa; -use crate::MpesaError; -use crate::MpesaResult; +use serde::{Deserialize, Serialize}; + +use crate::{ApiEnvironment, CommandId, IdentifierTypes, Mpesa, MpesaError, MpesaResult}; #[derive(Debug, Serialize)] pub struct TransactionReversalPayload<'mpesa> { diff --git a/src/services/transaction_status.rs b/src/services/transaction_status.rs index eac9f5552..5a7a50b99 100644 --- a/src/services/transaction_status.rs +++ b/src/services/transaction_status.rs @@ -1,12 +1,6 @@ -use serde::Deserialize; -use serde::Serialize; - -use crate::ApiEnvironment; -use crate::CommandId; -use crate::IdentifierTypes; -use crate::Mpesa; -use crate::MpesaError; -use crate::MpesaResult; +use serde::{Deserialize, Serialize}; + +use crate::{ApiEnvironment, CommandId, IdentifierTypes, Mpesa, MpesaError, MpesaResult}; #[derive(Debug, Serialize)] pub struct TransactionStatusPayload<'mpesa> { diff --git a/tests/mpesa-rust/account_balance_test.rs b/tests/mpesa-rust/account_balance_test.rs index 034311f09..ebbc65a9d 100644 --- a/tests/mpesa-rust/account_balance_test.rs +++ b/tests/mpesa-rust/account_balance_test.rs @@ -1,9 +1,10 @@ -use crate::get_mpesa_client; use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + #[tokio::test] async fn account_balance_success() { let (client, server) = get_mpesa_client!(); diff --git a/tests/mpesa-rust/b2c_test.rs b/tests/mpesa-rust/b2c_test.rs index 821a4a8da..39dec8ed4 100644 --- a/tests/mpesa-rust/b2c_test.rs +++ b/tests/mpesa-rust/b2c_test.rs @@ -1,9 +1,10 @@ -use crate::get_mpesa_client; use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + #[tokio::test] async fn b2c_success() { let (client, server) = get_mpesa_client!(); 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 591a373b0..6e05928e9 100644 --- a/tests/mpesa-rust/bill_manager_test/bulk_invoice_test.rs +++ b/tests/mpesa-rust/bill_manager_test/bulk_invoice_test.rs @@ -1,10 +1,11 @@ -use crate::get_mpesa_client; use chrono::prelude::Utc; use mpesa::{Invoice, InvoiceItem, MpesaError}; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + fn sample_response() -> ResponseTemplate { let sample_response = json!({ "rescode": "200", diff --git a/tests/mpesa-rust/bill_manager_test/cancel_invoice_test.rs b/tests/mpesa-rust/bill_manager_test/cancel_invoice_test.rs index 64d361055..201bbce3c 100644 --- a/tests/mpesa-rust/bill_manager_test/cancel_invoice_test.rs +++ b/tests/mpesa-rust/bill_manager_test/cancel_invoice_test.rs @@ -1,8 +1,9 @@ -use crate::get_mpesa_client; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + fn sample_response() -> ResponseTemplate { let sample_response = json!({ "rescode": "200", diff --git a/tests/mpesa-rust/bill_manager_test/onboard_modify_test.rs b/tests/mpesa-rust/bill_manager_test/onboard_modify_test.rs index dcf629dcc..3e0376940 100644 --- a/tests/mpesa-rust/bill_manager_test/onboard_modify_test.rs +++ b/tests/mpesa-rust/bill_manager_test/onboard_modify_test.rs @@ -1,8 +1,9 @@ -use crate::get_mpesa_client; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + fn sample_response() -> ResponseTemplate { let sample_response_body = json!({ "rescode": "200", diff --git a/tests/mpesa-rust/bill_manager_test/onboard_test.rs b/tests/mpesa-rust/bill_manager_test/onboard_test.rs index 2b2332468..b9a813112 100644 --- a/tests/mpesa-rust/bill_manager_test/onboard_test.rs +++ b/tests/mpesa-rust/bill_manager_test/onboard_test.rs @@ -1,9 +1,10 @@ -use crate::get_mpesa_client; use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + fn sample_response() -> ResponseTemplate { let sample_response_body = json!({ "app_key": "kfpB9X4o0H", diff --git a/tests/mpesa-rust/bill_manager_test/reconciliation_test.rs b/tests/mpesa-rust/bill_manager_test/reconciliation_test.rs index 02489be15..c555c8b1c 100644 --- a/tests/mpesa-rust/bill_manager_test/reconciliation_test.rs +++ b/tests/mpesa-rust/bill_manager_test/reconciliation_test.rs @@ -1,10 +1,11 @@ -use crate::get_mpesa_client; use chrono::prelude::Utc; use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + fn sample_response() -> ResponseTemplate { let sample_response = json!({ "rescode": "200", 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 a9622050b..b876de4f9 100644 --- a/tests/mpesa-rust/bill_manager_test/single_invoice_test.rs +++ b/tests/mpesa-rust/bill_manager_test/single_invoice_test.rs @@ -1,10 +1,11 @@ -use crate::get_mpesa_client; use chrono::prelude::Utc; use mpesa::{InvoiceItem, MpesaError}; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + fn sample_response() -> ResponseTemplate { let sample_response = json!({ "rescode": "200", diff --git a/tests/mpesa-rust/c2b_register_test.rs b/tests/mpesa-rust/c2b_register_test.rs index c50815455..18bcee648 100644 --- a/tests/mpesa-rust/c2b_register_test.rs +++ b/tests/mpesa-rust/c2b_register_test.rs @@ -1,9 +1,10 @@ -use crate::get_mpesa_client; use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + #[tokio::test] async fn c2b_register_success() { let (client, server) = get_mpesa_client!(); diff --git a/tests/mpesa-rust/c2b_simulate_test.rs b/tests/mpesa-rust/c2b_simulate_test.rs index 3a70e23e8..0ff085188 100644 --- a/tests/mpesa-rust/c2b_simulate_test.rs +++ b/tests/mpesa-rust/c2b_simulate_test.rs @@ -1,9 +1,10 @@ -use crate::get_mpesa_client; use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + #[tokio::test] async fn c2b_simulate_success() { let (client, server) = get_mpesa_client!(); diff --git a/tests/mpesa-rust/stk_push_test.rs b/tests/mpesa-rust/stk_push_test.rs index 19be8c512..dec5815da 100644 --- a/tests/mpesa-rust/stk_push_test.rs +++ b/tests/mpesa-rust/stk_push_test.rs @@ -1,9 +1,10 @@ -use crate::get_mpesa_client; use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + #[tokio::test] async fn stk_push_success_success() { let (client, server) = get_mpesa_client!(); diff --git a/tests/mpesa-rust/transaction_reversal_test.rs b/tests/mpesa-rust/transaction_reversal_test.rs index a5b39ad9a..017ac4583 100644 --- a/tests/mpesa-rust/transaction_reversal_test.rs +++ b/tests/mpesa-rust/transaction_reversal_test.rs @@ -1,9 +1,10 @@ -use crate::get_mpesa_client; use mpesa::MpesaError; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate}; +use crate::get_mpesa_client; + #[tokio::test] async fn transaction_reversal_success() { let (client, server) = get_mpesa_client!(); diff --git a/tests/mpesa-rust/transaction_status_test.rs b/tests/mpesa-rust/transaction_status_test.rs index b0011c1a2..46f0aa008 100644 --- a/tests/mpesa-rust/transaction_status_test.rs +++ b/tests/mpesa-rust/transaction_status_test.rs @@ -1,5 +1,4 @@ use mpesa::MpesaError; - use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, ResponseTemplate};