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
173
174
175
//! Subaccounts
//! ===========
//! The Subaccounts API allows you create and manage subaccounts on your integration.
//! Subaccounts can be used to split payment between two accounts (your main account and a sub account).

use derive_builder::Builder;
use reqwest::StatusCode;
use serde::Serialize;

use crate::{
    get_request, post_request, put_request, CreateSubaccountResponse, Error,
    FetchSubaccountResponse, ListSubaccountsResponse, PaystackResult,
};

/// This struct is used to create the body for creating a subaccount on your integration.
#[derive(Serialize, Debug, Builder)]
pub struct CreateSubaccountBody {
    /// Name of business for subaccount
    business_name: String,
    /// Bank Code for the bank.
    /// You can get the list of Bank Codes by calling the List Banks endpoint.
    settlement_bank: String,
    /// Bank Account Number
    account_number: String,
    /// The default percentage charged when receiving on behalf of this subaccount
    percentage_charge: f32,
    /// A description for this subaccount
    description: String,
    /// A contact email for the subaccount
    #[builder(default = "None")]
    primary_contact_email: Option<String>,
    /// A name for the contact person for this subaccount
    #[builder(default = "None")]
    primary_contact_name: Option<String>,
    /// A phone number to call for this subaccount
    #[builder(default = "None")]
    primary_contact_phone: Option<String>,
    /// Stringified JSON object.
    /// Add a custom_fields attribute which has an array of objects if you would like the fields to be
    /// added to your transaction when displayed on the dashboard.
    /// Sample: {"custom_fields":[{"display_name":"Cart ID","variable_name": "cart_id","value": "8393"}]}
    #[builder(default = "None")]
    metadata: Option<String>,
}

/// A struct to hold all functions in the subaccount API route
#[derive(Debug, Clone)]
pub struct Subaccount {
    /// Paystack API key
    api_key: String,
}

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

impl Subaccount {
    /// Constructor for the subaccount object
    pub fn new(key: String) -> Self {
        Subaccount { api_key: key }
    }

    /// Create a subaccount on your integration
    ///
    /// Takes in the following parameters
    ///     - body: subaccount to create.
    pub async fn create_subaccount(
        &self,
        body: CreateSubaccountBody,
    ) -> PaystackResult<CreateSubaccountResponse> {
        let url = format!("{}/subaccount", BASE_URL);

        match post_request(&self.api_key, &url, body).await {
            Ok(response) => match response.status() {
                StatusCode::CREATED => match response.json::<CreateSubaccountResponse>().await {
                    Ok(content) => Ok(content),
                    Err(err) => Err(Error::Subaccount(err.to_string())),
                },
                _ => Err(Error::RequestNotSuccessful(
                    response.status().to_string(),
                    response.text().await?,
                )),
            },
            Err(err) => Err(Error::FailedRequest(err.to_string())),
        }
    }

    /// List subaccounts available on your integration
    ///
    /// Take in the following parameters
    ///     - perPage: Specify how many records you want to retrieve per page. If not specify we use a default value of 50.
    ///     - page: Specify exactly what page you want to retrieve. If not specify we use a default value of 1.
    ///     - from: A timestamp from which to start listing subaccounts e.g. `2016-09-24T00:00:05.000Z`, `2016-09-21`
    ///     - to: A timestamp at which to stop listing subaccounts e.g. `2016-09-24T00:00:05.000Z`, `2016-09-21`
    pub async fn list_subaccounts(
        &self,
        per_page: Option<u32>,
        page: Option<u32>,
        from: Option<String>,
        to: Option<String>,
    ) -> PaystackResult<ListSubaccountsResponse> {
        let url = format!("{}/subaccount", BASE_URL);

        let query = vec![
            ("perPage", per_page.unwrap_or(50).to_string()),
            ("page", page.unwrap_or(1).to_string()),
            ("from", from.unwrap_or_default()),
            ("to", to.unwrap_or_default()),
        ];

        match get_request(&self.api_key, &url, Some(query)).await {
            Ok(response) => match response.status() {
                StatusCode::OK => match response.json::<ListSubaccountsResponse>().await {
                    Ok(content) => Ok(content),
                    Err(err) => Err(Error::Subaccount(err.to_string())),
                },
                _ => Err(Error::RequestNotSuccessful(
                    response.status().to_string(),
                    response.text().await?,
                )),
            },
            Err(err) => Err(Error::FailedRequest(err.to_string())),
        }
    }

    /// Get details of a subaccount on your integration.
    ///
    /// Takes the following parameters:
    ///     - id_or_code: The subaccount `ID` or `code` you want to fetch
    pub async fn fetch_subaccount(
        &self,
        id_or_code: String,
    ) -> PaystackResult<FetchSubaccountResponse> {
        let url = format!("{}/subaccount/{}", BASE_URL, id_or_code);

        match get_request(&self.api_key, &url, None).await {
            Ok(response) => match response.status() {
                StatusCode::OK => match response.json::<FetchSubaccountResponse>().await {
                    Ok(content) => Ok(content),
                    Err(err) => Err(Error::Subaccount(err.to_string())),
                },
                _ => Err(Error::RequestNotSuccessful(
                    response.status().to_string(),
                    response.text().await?,
                )),
            },
            Err(err) => Err(Error::FailedRequest(err.to_string())),
        }
    }

    /// Update a subaccount details on your integration.
    ///
    /// Takes the following parameters:
    ///     - id_or_code: Subaccount's ID or code.
    ///     - body: Subaccount modification payload
    pub async fn update_subaccount(
        &self,
        id_or_code: String,
        body: CreateSubaccountBody,
    ) -> PaystackResult<CreateSubaccountResponse> {
        let url = format!("{}/subaccount/{}", BASE_URL, id_or_code);

        match put_request(&self.api_key, &url, body).await {
            Ok(response) => match response.status() {
                StatusCode::OK => match response.json::<CreateSubaccountResponse>().await {
                    Ok(content) => Ok(content),
                    Err(err) => Err(Error::Subaccount(err.to_string())),
                },
                _ => Err(Error::RequestNotSuccessful(
                    response.status().to_string(),
                    response.text().await?,
                )),
            },
            Err(err) => Err(Error::FailedRequest(err.to_string())),
        }
    }
}