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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! Client
//! ===========
//! The file for the Paystack API client and it's associated functions

extern crate reqwest;
extern crate serde_json;

use crate::{
    Charge, PaystackResult, RequestNotSuccessful, Transaction, TransactionResponse,
    TransactionStatus, TransactionStatusList,
};

static BASE_URL: &str = "https://api.paystack.co";

/// This is the struct that allows you to authenticate to the PayStack API.
/// It contains the API key which allows you to interact with the API.
#[derive(Clone, Debug)]
pub struct PaystackClient {
    client: reqwest::Client,
    api_key: String,
}

impl PaystackClient {
    /// This method creates a new PayStack client with the specified API key.
    ///
    /// It takes the following parameters:
    ///     - key: Paystack API key.
    pub fn new(key: impl Into<String>) -> Self {
        Self {
            client: reqwest::Client::new(),
            api_key: key.into(),
        }
    }

    /// This method initalizes a new transaction using the Paystack API.
    ///
    /// It takes a Transaction type as its parameter
    pub async fn initialize_transaction(
        &self,
        transaction_body: Transaction,
    ) -> PaystackResult<TransactionResponse> {
        let url = format!("{}/transaction/initialize", BASE_URL);

        let response = self
            .client
            .post(url)
            .bearer_auth(&self.api_key)
            .header("Content-Type", "application/json")
            .json(&transaction_body)
            .send()
            .await?;

        if response.error_for_status_ref().is_err() {
            return Err(
                RequestNotSuccessful::new(response.status(), response.text().await?).into(),
            );
        }

        let content = response.json::<TransactionResponse>().await?;

        Ok(content)
    }

    /// This method confirms the status of a transaction.
    ///
    /// It takes the following parameters:
    ///     - reference: The transaction reference used to intiate the transaction
    pub async fn verify_transaction(&self, reference: String) -> PaystackResult<TransactionStatus> {
        let url = format!("{}/transaction/verify/{}", BASE_URL, reference);

        let response = self
            .client
            .get(url)
            .bearer_auth(&self.api_key)
            .header("Content-Type", "application/json")
            .send()
            .await?;

        if response.error_for_status_ref().is_err() {
            return Err(
                RequestNotSuccessful::new(response.status(), response.text().await?).into(),
            );
        }

        let content = response.json::<TransactionStatus>().await?;
        Ok(content)
    }

    /// This method returns a Vec of transactions carried out on your integrations.
    ///
    /// The method takes an Optional parameter for the number of transactions to return.
    ///
    /// If None is passed as the parameter, the last 10 transactions are returned
    pub async fn list_transactions(
        &self,
        number_of_transactions: Option<u32>,
    ) -> PaystackResult<TransactionStatusList> {
        let url = format!("{}/transaction", BASE_URL);
        let query = vec![("perPage", number_of_transactions.unwrap_or(10))];

        let response = self
            .client
            .get(url)
            .query(&query)
            .bearer_auth(&self.api_key)
            .header("Content-Type", "application.json")
            .send()
            .await?;

        if response.error_for_status_ref().is_err() {
            return Err(
                RequestNotSuccessful::new(response.status(), response.text().await?).into(),
            );
        }

        let contents = response.json::<TransactionStatusList>().await?;
        Ok(contents)
    }

    /// Get details of a transaction carried out on your integration
    ///
    /// This methods take the Id of the desired transaction as a parameter
    pub async fn fetch_transactions(
        &self,
        transaction_id: u32,
    ) -> PaystackResult<TransactionStatus> {
        let url = format!("{}/transaction/{}", BASE_URL, transaction_id);

        let response = self
            .client
            .get(url)
            .bearer_auth(&self.api_key)
            .header("Content-Type", "application/json")
            .send()
            .await?;

        if response.error_for_status_ref().is_err() {
            return Err(
                RequestNotSuccessful::new(response.status(), response.text().await?).into(),
            );
        }

        let content = response.json::<TransactionStatus>().await?;

        Ok(content)
    }

    /// All authorizations marked as reusable can be charged with this endpoint whenever you need to receive payments
    ///
    /// This function takes a Charge Struct as parameter
    pub async fn charge_authorization(&self, charge: Charge) -> PaystackResult<TransactionStatus> {
        let url = format!("{}/transaction/charge_authorization", BASE_URL);

        let response = self
            .client
            .post(url)
            .bearer_auth(&self.api_key)
            .header("Content-Type", "application/json")
            .json(&charge)
            .send()
            .await?;

        if response.error_for_status_ref().is_err() {
            return Err(
                RequestNotSuccessful::new(response.status(), response.text().await?).into(),
            );
        }
        let content = response.json::<TransactionStatus>().await?;

        Ok(content)
    }
}