1use serde::{de::DeserializeOwned, Serialize};
2
3use crate::{
4 accountbalance::AccountBalance, b2bexpress::B2bExpress, b2ctopup::B2bTopup, b2c::B2C, bbuygoods::Bbuygoods, bill_reconciliation::Reconciliation, billmanager::BillOnboarding, billupdate::BillUpdate, cancelinvoice::CancelInvoice, config::{Config, MpesaConfig}, error::{map_deserialization_error, ApiError, MpesaError}, expressquery::ExpressQuery, qr::Qr, ratiba::Ratiba, reversal::Reversal, singleinvoice::SingleInvoice, stkpush::STKPush, tax::Tax, transactionstatus::TransactionStatus
5};
6
7#[derive(Debug, Clone)]
8pub struct Client<C: Config> {
9 http_client: reqwest::Client,
10 config: C
11}
12
13impl Client<MpesaConfig> {
14 pub fn new() -> Self {
16 Self {
17 http_client: reqwest::Client::new(),
18 config: MpesaConfig::default()
19 }
20 }
21}
22
23impl<C: Config> Client<C> {
24 pub fn with_config(config: C) -> Self {
26 Self {
27 http_client: reqwest::Client::new(),
28 config,
29 }
30 }
31
32 pub fn with_http_client(mut self, http_client: reqwest::Client) -> Self {
36 self.http_client = http_client;
37 self
38 }
39
40
41 pub fn accountbalance(&self) -> AccountBalance<C> {
43 AccountBalance::new(self)
44 }
45
46 pub fn b2c(&self) -> B2C<C> {
47 B2C::new(self)
48 }
49
50 pub fn b2bexpress(&self) -> B2bExpress<C> {
51 B2bExpress::new(self)
52 }
53
54 pub fn b2btopup(&self) -> B2bTopup<C> {
55 B2bTopup::new(self)
56 }
57
58 pub fn bbuygoods(&self) -> Bbuygoods<C> {
59 Bbuygoods::new(self)
60 }
61
62 pub fn billreconciliation(&self) -> Reconciliation<C> {
63 Reconciliation::new(self)
64 }
65
66 pub fn billupdate(&self) -> BillUpdate<C> {
67 BillUpdate::new(self)
68 }
69
70 pub fn billmanager(&self) -> BillOnboarding<C> {
71 BillOnboarding::new(self)
72 }
73
74 pub fn bpaybill(&self) -> Bbuygoods<C> {
75 Bbuygoods::new(self)
76 }
77
78 pub fn cancelinvoice(&self) -> CancelInvoice<C> {
79 CancelInvoice::new(self)
80 }
81
82 pub fn expressquery(&self) -> ExpressQuery<C> {
83 ExpressQuery::new(self)
84 }
85
86 pub fn qr(&self) -> Qr<C> {
87 Qr::new(self)
88 }
89
90 pub fn ratiba(&self) -> Ratiba<C> {
91 Ratiba::new(self)
92 }
93
94 pub fn reversal(&self) -> Reversal<C> {
95 Reversal::new(self)
96 }
97
98 pub fn singleinvoice(&self) -> SingleInvoice<C> {
99 SingleInvoice::new(self)
100 }
101
102 pub fn stkpush(&self) -> STKPush<C> {
103 STKPush::new(self)
104 }
105
106 pub fn taxremit(&self) -> Tax<C> {
107 Tax::new(self)
108 }
109
110 pub fn transactionstatus(&self) -> TransactionStatus<C> {
111 TransactionStatus::new(self)
112 }
113
114 pub(crate) async fn post<I, O>(&self, path: &str, request: I) -> Result<O, MpesaError>
116 where
117 I: Serialize + std::fmt::Debug,
118 O: DeserializeOwned,
119 {
120 let request = self
121 .http_client
122 .post(self.config.url(path))
123 .headers(self.config.headers())
124 .json(&request)
125 .build()?;
126 self.execute(request).await
127 }
128
129 async fn execute<O>(&self, request: reqwest::Request) -> Result<O, MpesaError>
131 where
132 O: DeserializeOwned,
133 {
134 let client = self.http_client.clone();
135
136 let response = client
137 .execute(request.try_into().unwrap())
138 .await
139 .map_err(MpesaError::Reqwest)?;
140
141 let status = response.status();
142
143 let bytes = response
144 .bytes()
145 .await
146 .map_err(MpesaError::Reqwest)?;
147
148 if !status.is_success() {
149 let wrapped_error: ApiError = serde_json::from_slice(bytes.as_ref())
150 .map_err(|e| map_deserialization_error(e, bytes.as_ref()))?;
151
152 return Err(MpesaError::ApiError(wrapped_error));
153 }
154
155 let response: O = serde_json::from_slice(bytes.as_ref())
156 .map_err(|e| map_deserialization_error(e, bytes.as_ref()))?;
157
158 Ok(response)
159 }
160
161}