1use crate::models::cards::CardType;
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
10#[serde(rename_all = "lowercase")]
11pub enum TransactionType {
12 Spend,
13 Collateral,
14 Payment,
15 Fee,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
20#[serde(rename_all = "lowercase")]
21pub enum SpendTransactionStatus {
22 Pending,
23 Reversed,
24 Declined,
25 Completed,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
30#[serde(rename_all = "lowercase")]
31pub enum PaymentTransactionStatus {
32 Pending,
33 Completed,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct SpendTransaction {
39 pub amount: i64,
40 pub currency: String,
41 #[serde(rename = "localAmount", skip_serializing_if = "Option::is_none")]
42 pub local_amount: Option<i64>,
43 #[serde(rename = "localCurrency", skip_serializing_if = "Option::is_none")]
44 pub local_currency: Option<String>,
45 #[serde(rename = "authorizedAmount", skip_serializing_if = "Option::is_none")]
46 pub authorized_amount: Option<i64>,
47 #[serde(
48 rename = "authorizationMethod",
49 skip_serializing_if = "Option::is_none"
50 )]
51 pub authorization_method: Option<String>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub memo: Option<String>,
54 pub receipt: bool,
55 #[serde(rename = "merchantName")]
56 pub merchant_name: String,
57 #[serde(rename = "merchantCategory")]
58 pub merchant_category: String,
59 #[serde(rename = "merchantCategoryCode")]
60 pub merchant_category_code: String,
61 #[serde(rename = "merchantId", skip_serializing_if = "Option::is_none")]
62 pub merchant_id: Option<String>,
63 #[serde(
64 rename = "enrichedMerchantIcon",
65 skip_serializing_if = "Option::is_none"
66 )]
67 pub enriched_merchant_icon: Option<String>,
68 #[serde(
69 rename = "enrichedMerchantName",
70 skip_serializing_if = "Option::is_none"
71 )]
72 pub enriched_merchant_name: Option<String>,
73 #[serde(
74 rename = "enrichedMerchantCategory",
75 skip_serializing_if = "Option::is_none"
76 )]
77 pub enriched_merchant_category: Option<String>,
78 #[serde(rename = "cardId")]
79 pub card_id: Uuid,
80 #[serde(rename = "cardType")]
81 pub card_type: CardType,
82 #[serde(rename = "companyId", skip_serializing_if = "Option::is_none")]
83 pub company_id: Option<Uuid>,
84 #[serde(rename = "userId")]
85 pub user_id: Uuid,
86 #[serde(rename = "userFirstName")]
87 pub user_first_name: String,
88 #[serde(rename = "userLastName", skip_serializing_if = "Option::is_none")]
89 pub user_last_name: Option<String>,
90 #[serde(rename = "userEmail")]
91 pub user_email: String,
92 pub status: SpendTransactionStatus,
93 #[serde(rename = "declinedReason", skip_serializing_if = "Option::is_none")]
94 pub declined_reason: Option<String>,
95 #[serde(rename = "authorizedAt")]
96 pub authorized_at: String,
97 #[serde(rename = "postedAt", skip_serializing_if = "Option::is_none")]
98 pub posted_at: Option<String>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct CollateralTransaction {
104 pub amount: f64,
105 pub currency: String,
106 #[serde(rename = "chainId")]
107 pub chain_id: i64,
108 #[serde(rename = "walletAddress")]
109 pub wallet_address: String,
110 #[serde(rename = "transactionHash")]
111 pub transaction_hash: String,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub memo: Option<String>,
114 #[serde(rename = "companyId", skip_serializing_if = "Option::is_none")]
115 pub company_id: Option<Uuid>,
116 #[serde(rename = "userId", skip_serializing_if = "Option::is_none")]
117 pub user_id: Option<Uuid>,
118 #[serde(rename = "postedAt", skip_serializing_if = "Option::is_none")]
119 pub posted_at: Option<DateTime<Utc>>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct PaymentTransaction {
125 pub amount: i64,
126 pub currency: String,
127 pub status: PaymentTransactionStatus,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub memo: Option<String>,
130 #[serde(rename = "chainId", skip_serializing_if = "Option::is_none")]
131 pub chain_id: Option<i64>,
132 #[serde(rename = "walletAddress", skip_serializing_if = "Option::is_none")]
133 pub wallet_address: Option<String>,
134 #[serde(rename = "transactionHash", skip_serializing_if = "Option::is_none")]
135 pub transaction_hash: Option<String>,
136 #[serde(rename = "companyId", skip_serializing_if = "Option::is_none")]
137 pub company_id: Option<Uuid>,
138 #[serde(rename = "userId", skip_serializing_if = "Option::is_none")]
139 pub user_id: Option<Uuid>,
140 #[serde(rename = "postedAt", skip_serializing_if = "Option::is_none")]
141 pub posted_at: Option<String>,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct FeeTransaction {
147 pub amount: i64,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub description: Option<String>,
150 #[serde(rename = "companyId", skip_serializing_if = "Option::is_none")]
151 pub company_id: Option<Uuid>,
152 #[serde(rename = "userId", skip_serializing_if = "Option::is_none")]
153 pub user_id: Option<Uuid>,
154 #[serde(rename = "postedAt", skip_serializing_if = "Option::is_none")]
155 pub posted_at: Option<DateTime<Utc>>,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160#[serde(tag = "type")]
161#[allow(clippy::large_enum_variant)]
162pub enum Transaction {
163 #[serde(rename = "spend")]
164 Spend {
165 id: Uuid,
166 #[serde(flatten)]
167 spend: SpendTransaction,
168 },
169 #[serde(rename = "collateral")]
170 Collateral {
171 id: Uuid,
172 #[serde(flatten)]
173 collateral: CollateralTransaction,
174 },
175 #[serde(rename = "payment")]
176 Payment {
177 id: Uuid,
178 #[serde(flatten)]
179 payment: PaymentTransaction,
180 },
181 #[serde(rename = "fee")]
182 Fee {
183 id: Uuid,
184 #[serde(flatten)]
185 fee: FeeTransaction,
186 },
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct ListTransactionsParams {
192 #[serde(rename = "companyId", skip_serializing_if = "Option::is_none")]
193 pub company_id: Option<Uuid>,
194 #[serde(rename = "userId", skip_serializing_if = "Option::is_none")]
195 pub user_id: Option<Uuid>,
196 #[serde(rename = "cardId", skip_serializing_if = "Option::is_none")]
197 pub card_id: Option<Uuid>,
198 #[serde(
199 rename = "type",
200 skip_serializing_if = "Option::is_none",
201 serialize_with = "serialize_transaction_types"
202 )]
203 pub transaction_type: Option<Vec<TransactionType>>,
204 #[serde(rename = "transactionHash", skip_serializing_if = "Option::is_none")]
205 pub transaction_hash: Option<String>,
206 #[serde(rename = "authorizedBefore", skip_serializing_if = "Option::is_none")]
207 pub authorized_before: Option<DateTime<Utc>>,
208 #[serde(rename = "authorizedAfter", skip_serializing_if = "Option::is_none")]
209 pub authorized_after: Option<DateTime<Utc>>,
210 #[serde(rename = "postedBefore", skip_serializing_if = "Option::is_none")]
211 pub posted_before: Option<DateTime<Utc>>,
212 #[serde(rename = "postedAfter", skip_serializing_if = "Option::is_none")]
213 pub posted_after: Option<DateTime<Utc>>,
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub cursor: Option<String>,
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub limit: Option<u32>,
218}
219
220fn serialize_transaction_types<S>(
221 types: &Option<Vec<TransactionType>>,
222 serializer: S,
223) -> Result<S::Ok, S::Error>
224where
225 S: serde::Serializer,
226{
227 use serde::ser::SerializeSeq;
228
229 match types {
230 Some(ref vec) => {
231 let mut seq = serializer.serialize_seq(Some(vec.len()))?;
232 for item in vec {
233 let s = match item {
234 TransactionType::Spend => "spend",
235 TransactionType::Collateral => "collateral",
236 TransactionType::Payment => "payment",
237 TransactionType::Fee => "fee",
238 };
239 seq.serialize_element(s)?;
240 }
241 seq.end()
242 }
243 None => serializer.serialize_none(),
244 }
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct UpdateTransactionRequest {
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub memo: Option<String>,
252}
253
254#[derive(Debug, Clone)]
256pub struct UploadReceiptRequest {
257 pub receipt: Vec<u8>,
258 pub file_name: String,
259}
260
261pub type ListTransactionsResponse = Vec<Transaction>;