flutterwave_v3/
v3_client.rs

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
use crate::fwcall::{Payload, ToFwCall};
use async_symm_crypto::AsyncEncryption;
use flutterwave_models::{
    charge::{ach::AchReq, bank_transfer::BankTransferReq, direct_charge::CardChargeReq},
    encrypted_payload::EncryptedPayload,
    errors::FWaveError,
    transactions::transaction_verify::{VerifyTransByIdReq, VerifyTransByTxRefReq},
    validate_charge::ValidateChargeReq,
    virtual_acct_number::{
        get_bulk_virtual_acct_details::BulkVirtualAcctDetailsReq, VirtualAcctBulkCreationReq,
        VirtualAcctCreationReq,
    },
};
use reqwest::{
    header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE},
    Url,
};
use std::str::FromStr;

macro_rules! generate_client_method {
    ($t:ty, $func_name:ident) => {
        #[allow(unused)]
        async fn $func_name(&self, req: $t) -> Result<<$t as ToFwCall>::ApiResponse, FWaveError> {
            self.make_v3_request(req).await
        }
    };
}

static BASE_URL: &str = "https://api.flutterwave.com/";

pub struct FWV3Client<'a> {
    #[allow(unused)]
    enc_key: &'a str,
    #[allow(unused)]
    public: &'a str,
    #[allow(unused)]
    secret: &'a str,
    client: reqwest::Client,
    crypt: AsyncEncryption<'a>,
}

impl<'a> FWV3Client<'a> {
    pub fn new(secret_key: &'a str, public_key: &'a str, encryption_key: &'a str) -> Self {
        let mut default_headers = HeaderMap::new();
        default_headers.append(CONTENT_TYPE, HeaderValue::from_static("application/json"));
        default_headers.append(ACCEPT, HeaderValue::from_static("application/json"));

        let client = reqwest::ClientBuilder::new()
            .https_only(true)
            .default_headers(default_headers)
            .build()
            .unwrap();

        Self {
            secret: secret_key,
            public: public_key,
            enc_key: encryption_key,
            client,
            crypt: AsyncEncryption::new(
                openssl::symm::Cipher::des_ede3_ecb(),
                secret_key.as_bytes(),
                None,
            ),
        }
    }

    async fn make_v3_request<'b, C: ToFwCall<'b> + 'b>(
        &'b self,
        call: C,
    ) -> Result<C::ApiResponse, FWaveError> {
        let call = call.get_call();

        let mut request = self
            .client
            .request(
                call.method.clone(),
                Url::from_str(BASE_URL)
                    .unwrap()
                    .join(call.path.as_ref())
                    .unwrap(),
            )
            .bearer_auth(self.secret)
            .header(CONTENT_TYPE, "application/json")
            .header(ACCEPT, "application/json");

        if [reqwest::Method::PUT, reqwest::Method::POST].contains(&call.method) {
            match &call.payload {
                Some(Payload::Plain(pload)) => {
                    request = request.json(&pload);
                }
                Some(Payload::ToEncrypt(pload)) => {
                    let to_enc_bytes = serde_json::to_string(pload)?.as_bytes().to_vec();
                    let encrypted_bytes = self.crypt.encrypt(&to_enc_bytes).await.unwrap();
                    request = request.json(&EncryptedPayload::new(openssl::base64::encode_block(
                        &encrypted_bytes,
                    )));
                }
                None => {}
            }
        }

        Ok(request.send().await?.json::<C::ApiResponse>().await?)
    }

    generate_client_method!(VirtualAcctCreationReq, create_virtual_acct_no);
    generate_client_method!(VirtualAcctBulkCreationReq, create_bulk_virtual_acct_no);
    generate_client_method!(CardChargeReq, initiate_card_charge);
    generate_client_method!(BankTransferReq, initiate_bank_transfer);
    generate_client_method!(VerifyTransByIdReq, verify_trans_by_id);
    generate_client_method!(VerifyTransByTxRefReq, verify_trans_by_txref);
    generate_client_method!(ValidateChargeReq, validate_charge);
    generate_client_method!(BulkVirtualAcctDetailsReq, get_bulk_virtual_acct_details);
    generate_client_method!(AchReq, initiate_ach_payment);
}

#[tokio::test]
async fn test_creat_acct() {
    let client = FWV3Client::new(
        "FLWSECK-266126f0ebb8c833c2852fdc5a21eec5-194272cd837vt-X",
        "FLWPUBK-03d00084cd0fd4f65a56017d2423b9fc-X",
        "266126f0ebb836dbcff05110",
    );

    let response = client
        .create_virtual_acct_no(VirtualAcctCreationReq {
            amount: 0.into(),
            bvn: "".into(),
            email: "test@email.com".into(),
            is_permanent: None,
            narration: None,
            tx_ref: None,
        })
        .await;

    match response {
        Ok(res) => {
            println!("{:?}", res);
        }
        Err(err) => {
            println!("{}", err);
        }
    }
}