pix_brcode_parser/
types.rs1#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq)]
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9pub struct BRCode {
10 pub payload_format_indicator: String,
12
13 pub point_of_initiation_method: Option<String>,
15
16 pub merchant_account_info: MerchantAccountInfo,
18
19 pub merchant_category_code: String,
21
22 pub transaction_currency: String,
24
25 pub transaction_amount: Option<String>,
27
28 pub country_code: String,
30
31 pub merchant_name: String,
33
34 pub merchant_city: String,
36
37 pub additional_data: Option<AdditionalData>,
39
40 pub crc16: String,
42}
43
44#[derive(Debug, Clone, PartialEq)]
46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47pub struct MerchantAccountInfo {
48 pub gui: String,
50
51 pub pix_key: String,
53
54 pub description: Option<String>,
56
57 pub url: Option<String>,
59}
60
61#[derive(Debug, Clone, PartialEq)]
63#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64pub struct AdditionalData {
65 pub bill_number: Option<String>,
67
68 pub mobile_number: Option<String>,
70
71 pub store_label: Option<String>,
73
74 pub loyalty_number: Option<String>,
76
77 pub reference_label: Option<String>,
79
80 pub customer_label: Option<String>,
82
83 pub terminal_label: Option<String>,
85
86 pub purpose_of_transaction: Option<String>,
88
89 pub additional_consumer_data_request: Option<String>,
91}
92
93#[derive(Debug, Clone, PartialEq)]
95#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
96pub enum PixKeyType {
97 Uuid,
99 Email,
101 Phone,
103 Cpf,
105 Cnpj,
107 Random,
109}
110
111#[derive(Debug, Clone, PartialEq)]
113pub struct EmvField {
114 pub tag: String,
116 pub length: usize,
118 pub value: String,
120}
121
122impl BRCode {
123 pub fn is_static(&self) -> bool {
125 self.point_of_initiation_method.as_deref() == Some("11")
126 }
127
128 pub fn is_dynamic(&self) -> bool {
130 self.point_of_initiation_method.as_deref() == Some("12")
131 }
132
133 pub fn pix_key_type(&self) -> PixKeyType {
135 classify_pix_key(&self.merchant_account_info.pix_key)
136 }
137
138 pub fn has_amount(&self) -> bool {
140 self.transaction_amount.is_some()
141 }
142}
143
144pub fn classify_pix_key(key: &str) -> PixKeyType {
146 if key.len() == 36 && key.chars().nth(8) == Some('-') && key.chars().nth(13) == Some('-') {
148 return PixKeyType::Uuid;
149 }
150
151 if key.contains('@') && key.contains('.') {
153 return PixKeyType::Email;
154 }
155
156 if key.starts_with("+55") && key.len() >= 13 {
158 return PixKeyType::Phone;
159 }
160
161 if key.len() == 11 && key.chars().all(|c| c.is_ascii_digit()) {
163 return PixKeyType::Cpf;
164 }
165
166 if key.len() == 14 && key.chars().all(|c| c.is_ascii_digit()) {
168 return PixKeyType::Cnpj;
169 }
170
171 PixKeyType::Random
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn test_pix_key_classification() {
181 assert_eq!(classify_pix_key("123e4567-e89b-12d3-a456-426614174000"), PixKeyType::Uuid);
182 assert_eq!(classify_pix_key("user@example.com"), PixKeyType::Email);
183 assert_eq!(classify_pix_key("+5511999999999"), PixKeyType::Phone);
184 assert_eq!(classify_pix_key("12345678901"), PixKeyType::Cpf);
185 assert_eq!(classify_pix_key("12345678000195"), PixKeyType::Cnpj);
186 assert_eq!(classify_pix_key("randomkey123"), PixKeyType::Random);
187 }
188}