paystack/models/
customer.rs

1use std::fmt;
2
3use derive_builder::Builder;
4use serde::{Deserialize, Serialize};
5
6use super::{Authorization, Subscription, TransactionStatusData};
7
8/// This struct represents the Paystack customer data
9#[derive(Debug, Deserialize, Serialize, Clone, Default)]
10pub struct CustomerResponseData {
11    pub id: u64,
12    pub integration: Option<u64>,
13    pub domain: Option<String>,
14    pub identified: Option<bool>,
15    pub first_name: Option<String>,
16    pub last_name: Option<String>,
17    pub email: String,
18    pub customer_code: String,
19    pub phone: Option<String>,
20    pub risk_action: Option<RiskAction>,
21    pub international_format_phone: Option<String>,
22    pub identification: Option<String>,
23    pub transactions: Option<Vec<TransactionStatusData>>,
24    pub subscriptions: Option<Vec<Subscription>>,
25    pub authorizations: Option<Vec<Authorization>>,
26    #[serde(rename = "createdAt")]
27    pub created_at: Option<String>,
28    #[serde(rename = "updatedAt")]
29    pub updated_at: Option<String>,
30    pub total_transactions: Option<u16>,
31    pub total_transaction_value: Option<Vec<String>>,
32    pub dedicated_account: Option<String>,
33}
34
35/// This struct constains the data for creating a customer in your integration
36#[derive(Debug, Clone, Serialize, Default, Deserialize, Builder)]
37pub struct CreateCustomerRequest {
38    /// Customer's email address
39    pub email: String,
40    /// Customer's first name
41    #[builder(setter(strip_option), default)]
42    pub first_name: Option<String>,
43    /// Customer's last name
44    #[builder(setter(strip_option), default)]
45    pub last_name: Option<String>,
46    /// Customer's phone number
47    #[builder(setter(strip_option), default)]
48    pub phone: Option<String>,
49}
50
51/// This struct constains the data for updating a customer in your integration
52#[derive(Debug, Clone, Serialize, Default, Deserialize, Builder)]
53pub struct UpdateCustomerRequest {
54    /// Customer's first name
55    #[builder(setter(strip_option), default)]
56    pub first_name: Option<String>,
57    /// Customer's last name
58    #[builder(setter(strip_option), default)]
59    pub last_name: Option<String>,
60    /// Customer's phone number
61    #[builder(setter(strip_option), default)]
62    pub phone: Option<String>,
63}
64
65#[derive(Debug, Clone, Serialize, Default, Deserialize, Builder)]
66pub struct ValidateCustomerRequest {
67    /// Customer's first name
68    pub first_name: String,
69    /// Customer's last name
70    pub last_name: String,
71    /// Customer's middle name
72    #[builder(setter(strip_option), default)]
73    pub middle_name: Option<String>,
74    /// Predefined types of identification. Only `bank_code` is supported at the moment
75    #[serde(rename = "type")]
76    pub identification_type: IdentificationType,
77    /// Customer's identification number
78    #[builder(setter(strip_option), default)]
79    pub value: Option<String>,
80    /// 2 letter country code of identification issuer
81    pub country: String,
82    /// Customer's Bank Verification Number
83    pub bvn: String,
84    /// Customer bank code
85    pub bank_code: String,
86    /// Customer's bank account number. (required if `identification_type` is `bank_account`.
87    #[builder(setter(strip_option), default)]
88    pub account_number: Option<String>,
89}
90
91/// Represents the different predefined types of identification.
92///
93/// Only `bank_account`is supported at the moment.
94#[derive(Debug, Serialize, Default, Deserialize, Clone, PartialEq)]
95#[serde(rename_all = "lowercase")]
96pub enum IdentificationType {
97    #[default]
98    #[serde(rename = "bank_account")]
99    BankAccount,
100}
101
102impl fmt::Display for IdentificationType {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        let identification_type = match self {
105            IdentificationType::BankAccount => "bank_account",
106        };
107        write!(f, "{}", identification_type)
108    }
109}
110
111#[derive(Debug, Serialize, Default, Deserialize, Clone, PartialEq)]
112#[serde(rename_all = "lowercase")]
113pub enum RiskAction {
114    #[default]
115    Default,
116    Allow,
117    Deny,
118}
119
120impl fmt::Display for RiskAction {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        let risk_action = match self {
123            RiskAction::Allow => "allow",
124            RiskAction::Default => "default",
125            RiskAction::Deny => "deny",
126        };
127        write!(f, "{}", risk_action)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn can_build_customer() {
137        let customer = CreateCustomerRequestBuilder::default()
138            .email("customer@example.com".to_string())
139            .first_name("Zero".to_string())
140            .last_name("Sum".to_string())
141            .phone("+2348123456789".to_string())
142            .build()
143            .expect("unable to build customer request");
144
145        assert_eq!(customer.first_name, Some("Zero".to_string()));
146        assert_eq!(customer.last_name, Some("Sum".to_string()));
147    }
148
149    #[test]
150    fn build_customer_with_invalid_data_fails() {
151        let first_name = "Zero".to_string();
152        let last_name = "Sum".to_string();
153        let phone = "+2348123456789".to_string();
154
155        let body = CreateCustomerRequestBuilder::default()
156            .first_name(first_name)
157            .last_name(last_name)
158            .phone(phone)
159            .build();
160
161        assert!(body.is_err());
162    }
163
164    #[test]
165    fn can_use_identification_type() {
166        let identification = IdentificationType::BankAccount;
167
168        assert_eq!(identification.to_string(), "bank_account".to_string());
169    }
170}