paystack/models/
customer_models.rs

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