1use async_symm_crypto::AsyncEncryption;
2use flutterwave_models::{
3 api_responses::ResponseType,
4 common::payload::Payload,fwcall::ToFwCall,
5 charge::{ach::AchReq, bank_transfer::BankTransferReq, direct_charge::CardChargeReq},
6 encrypted_payload::EncryptedPayload,
7 errors::FWaveError,
8 payment_plans::{CancelPlanReq, CreatePlanReq, GetPlanReq, GetPlansReq, UpdatePlanReq},
9 preauthorization::{
10 capture_preauth_charge::CapturePreAuthChargeReq,
11 refund_preauth_charge::RefundPreAuthChargeReq, void_preauth_charge::VoidPreAuthChargeReq,
12 },
13 transactions::{
14 fetch_refunded_trans::{FetchMultiRefundedTransReq, FetchRefundedTransReq},
15 get_transactions::GetTransactionsReq,
16 query_trans_fees::QueryTransFeesReq,
17 refund_trans::RefundTransactionReq,
18 resend_failed_webhook::ResendFailedWebhookReq,
19 transaction_verify::{VerifyTransByIdReq, VerifyTransByTxRefReq},
20 view_trans_timeline::ViewTransTimelineReq,
21 },
22 validate_charge::ValidateChargeReq,
23 virtual_acct_number::{
24 create_virtual_account::{VirtualAcctBulkCreationReq, VirtualAcctCreationReq},
25 get_bulk_virtual_acct_details::BulkVirtualAcctDetailsReq,
26 },
27};
28use reqwest::{
29 header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE},
30 Url,
31};
32use std::str::FromStr;
33
34macro_rules! generate_client_methods {
35 (
36 $(
37 ($t:ty, $func_name:ident)
38 )+
39 ) => {
40 $(
41 #[allow(unused)]
42 pub async fn $func_name(
43 &self,
44 req: $t,
45 ) -> Result<ResponseType<<$t as ToFwCall>::ApiResponse>, FWaveError> {
46 self.make_v3_request(req).await
47 }
48 )+
49 };
50}
51
52static BASE_URL: &str = "https://api.flutterwave.com/";
53
54pub struct FWV3Client<'a> {
55 #[allow(unused)]
56 enc_key: &'a str,
57 #[allow(unused)]
58 public: &'a str,
59 #[allow(unused)]
60 secret: &'a str,
61 client: reqwest::Client,
62 crypt: AsyncEncryption<'a>,
63}
64
65impl<'a> FWV3Client<'a> {
66 pub fn new(secret_key: &'a str, public_key: &'a str, encryption_key: &'a str) -> Self {
67 let mut default_headers = HeaderMap::new();
68 default_headers.append(CONTENT_TYPE, HeaderValue::from_static("application/json"));
69 default_headers.append(ACCEPT, HeaderValue::from_static("application/json"));
70
71 let client = reqwest::ClientBuilder::new()
72 .https_only(true)
73 .default_headers(default_headers)
74 .build()
75 .unwrap();
76
77 Self {
78 secret: secret_key,
79 public: public_key,
80 enc_key: encryption_key,
81 client,
82 crypt: AsyncEncryption::new(
83 openssl::symm::Cipher::des_ede3_ecb(),
84 secret_key.as_bytes(),
85 None,
86 ),
87 }
88 }
89
90 async fn make_v3_request<'b, C: ToFwCall<'b> + 'b>(
91 &'b self,
92 call: C,
93 ) -> Result<ResponseType<C::ApiResponse>, FWaveError> {
94 let call = call.get_call();
95
96 let mut request = self
97 .client
98 .request(
99 call.method.clone(),
100 Url::from_str(BASE_URL)
101 .unwrap()
102 .join(call.path.as_ref())
103 .unwrap(),
104 )
105 .bearer_auth(self.secret)
106 .header(CONTENT_TYPE, "application/json")
107 .header(ACCEPT, "application/json");
108
109 if [reqwest::Method::PUT, reqwest::Method::POST].contains(&call.method) {
110 match &call.payload {
111 Some(Payload::Plain(pload)) => {
112 request = request.json(&pload);
113 }
114 Some(Payload::ToEncrypt(pload)) => {
115 let to_enc_bytes = serde_json::to_string(pload)?.as_bytes().to_vec();
116 let encrypted_bytes = self.crypt.encrypt(&to_enc_bytes).await.unwrap();
117 request = request.json(&EncryptedPayload::new(openssl::base64::encode_block(
118 &encrypted_bytes,
119 )));
120 }
121 None => {}
122 }
123 }
124
125 let response = request.send().await?;
126 let status = response.status();
127 Ok(response.json::<ResponseType<C::ApiResponse>>().await?.replace_stat_code(status))
128 }
129
130 generate_client_methods!(
131 (CardChargeReq, initiate_card_charge)
133 (BankTransferReq, initiate_bank_transfer)
134 (AchReq, initiate_ach_payment)
135
136 (VerifyTransByIdReq, verify_trans_by_id)
138 (VerifyTransByTxRefReq, verify_trans_by_txref)
139
140 (ValidateChargeReq, validate_charge)
142
143 (CapturePreAuthChargeReq, capture_preauth_charge)
145 (VoidPreAuthChargeReq, void_preauth_charge)
146 (RefundPreAuthChargeReq, refund_preauth_charge)
147
148 (BulkVirtualAcctDetailsReq, get_bulk_virtual_acct_details)
150 (VirtualAcctCreationReq, create_virtual_acct_no)
151 (VirtualAcctBulkCreationReq, create_bulk_virtual_acct_no)
152
153 (FetchRefundedTransReq, fetch_refunded_transactions)
155 (FetchMultiRefundedTransReq, fetch_multi_refunded_transactions)
156 (GetTransactionsReq, get_transaction)
157 (QueryTransFeesReq, query_transaction_fees)
158 (RefundTransactionReq, refund_transaction)
159 (ResendFailedWebhookReq, resend_failed_webhook)
160 (ViewTransTimelineReq, view_trans_imeline)
161
162 (CreatePlanReq, create_payment_plan)
164 (GetPlanReq, get_payment_plan)
165 (GetPlansReq, get_payment_plans)
166 (CancelPlanReq, cancel_payment_plan)
167 (UpdatePlanReq, update_payment_plan)
168 );
169
170}