1use crate::client::ConcurClient;
6use crate::error::Result;
7use reqwest::Method;
8use serde::{Deserialize, Serialize};
9
10pub struct CardsApi<'a> {
12 client: &'a ConcurClient,
13}
14
15impl<'a> CardsApi<'a> {
16 pub fn new(client: &'a ConcurClient) -> Self {
17 Self { client }
18 }
19
20 pub async fn create_accounts_bulk(
22 &self,
23 company_id: &str,
24 request: &CreateAccountsRequest,
25 ) -> Result<BulkResponse> {
26 let req = self
27 .client
28 .request(
29 Method::POST,
30 &format!("/cards/v4/companies/{}/accounts/bulk", company_id),
31 )
32 .await?;
33 let req = ConcurClient::with_json(req, request)?;
34 self.client.execute(req).await
35 }
36
37 pub async fn create_transactions_bulk(
39 &self,
40 company_id: &str,
41 request: &CreateTransactionsRequest,
42 ) -> Result<BulkResponse> {
43 let req = self
44 .client
45 .request(
46 Method::POST,
47 &format!("/cards/v4/companies/{}/transactions/bulk", company_id),
48 )
49 .await?;
50 let req = ConcurClient::with_json(req, request)?;
51 self.client.execute(req).await
52 }
53
54 pub async fn get_bulk_request(
56 &self,
57 company_id: &str,
58 request_id: &str,
59 ) -> Result<BulkRequestStatus> {
60 self.client
61 .request(
62 Method::GET,
63 &format!(
64 "/cards/v4/companies/{}/bulkrequests/{}",
65 company_id, request_id
66 ),
67 )
68 .await?
69 .send()
70 .await?
71 .json()
72 .await
73 .map_err(Into::into)
74 }
75
76 pub async fn get_user_transactions(
78 &self,
79 user_id: &str,
80 query: &TransactionQuery,
81 ) -> Result<TransactionListResponse> {
82 let mut path = format!("/cards/v4/users/{}/transactions", user_id);
83 let mut params: Vec<(String, String)> = Vec::new();
84 if let Some(v) = &query.status {
85 params.push(("status".to_string(), v.clone()));
86 }
87 if let Some(v) = &query.transaction_date_from {
88 params.push(("transactionDateFrom".to_string(), v.clone()));
89 }
90 if let Some(v) = &query.transaction_date_to {
91 params.push(("transactionDateTo".to_string(), v.clone()));
92 }
93 if let Some(v) = query.page_size {
94 params.push(("pageSize".to_string(), v.to_string()));
95 }
96 if let Some(v) = &query.page_token {
97 params.push(("pageToken".to_string(), v.clone()));
98 }
99 if let Some(v) = &query.sort {
100 params.push(("sort".to_string(), v.clone()));
101 }
102 if let Some(v) = &query.order {
103 params.push(("order".to_string(), v.clone()));
104 }
105 if let Some(v) = &query.include_addendum_for {
106 params.push(("includeAddendumFor".to_string(), v.clone()));
107 }
108 if let Some(v) = &query.include_addendum_details_for {
109 params.push(("includeAddendumDetailsFor".to_string(), v.clone()));
110 }
111 if !params.is_empty() {
112 path.push('?');
113 path.push_str(
114 ¶ms
115 .iter()
116 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
117 .collect::<Vec<_>>()
118 .join("&"),
119 );
120 }
121 self.client
122 .request(Method::GET, &path)
123 .await?
124 .send()
125 .await?
126 .json()
127 .await
128 .map_err(Into::into)
129 }
130}
131
132#[derive(Debug, Clone, Default)]
133pub struct TransactionQuery {
134 pub status: Option<String>,
135 pub transaction_date_from: Option<String>,
136 pub transaction_date_to: Option<String>,
137 pub page_size: Option<u32>,
138 pub page_token: Option<String>,
139 pub sort: Option<String>,
140 pub order: Option<String>,
141 pub include_addendum_for: Option<String>,
142 pub include_addendum_details_for: Option<String>,
143}
144
145#[derive(Debug, Clone, Default, Serialize, Deserialize)]
146pub struct CreateAccountsRequest {
147 pub accounts: Vec<CardAccount>,
148}
149
150#[derive(Debug, Clone, Default, Serialize, Deserialize)]
151pub struct CardAccount {
152 #[serde(rename = "accountType")]
153 pub account_type: String,
154 #[serde(rename = "billedCurrency")]
155 pub billed_currency: String,
156 #[serde(rename = "cardBrand")]
157 pub card_brand: String,
158 #[serde(rename = "cardProductDescription")]
159 pub card_product_description: Option<String>,
160 #[serde(rename = "cardProductType")]
161 pub card_product_type: String,
162 pub cardholder: Cardholder,
163 #[serde(rename = "externalId")]
164 pub external_id: String,
165 #[serde(rename = "lastSegment")]
166 pub last_segment: Option<String>,
167 #[serde(rename = "liabilityType")]
168 pub liability_type: String,
169 #[serde(rename = "nameOnCard")]
170 pub name_on_card: String,
171 pub provider: CardProvider,
172 pub status: String,
173 #[serde(rename = "billingAccount", skip_serializing_if = "Option::is_none")]
174 pub billing_account: Option<serde_json::Value>,
175}
176
177#[derive(Debug, Clone, Default, Serialize, Deserialize)]
178pub struct Cardholder {
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub user_id: Option<String>,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub employee_id: Option<String>,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub email: Option<String>,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub first_name: Option<String>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub last_name: Option<String>,
189}
190
191#[derive(Debug, Clone, Default, Serialize, Deserialize)]
192pub struct CardProvider {
193 #[serde(rename = "countryCode")]
194 pub country_code: String,
195}
196
197#[derive(Debug, Clone, Default, Serialize, Deserialize)]
198pub struct CreateTransactionsRequest {
199 pub transactions: Vec<CardTransaction>,
200}
201
202#[derive(Debug, Clone, Default, Serialize, Deserialize)]
203pub struct CardTransaction {
204 #[serde(rename = "externalId")]
205 pub external_id: String,
206 #[serde(rename = "transactionDate")]
207 pub transaction_date: String,
208 #[serde(rename = "transactionDateTime")]
209 pub transaction_date_time: Option<String>,
210 #[serde(rename = "postedDate")]
211 pub posted_date: String,
212 #[serde(rename = "transactionAmount")]
213 pub transaction_amount: Money,
214 #[serde(rename = "postedAmount")]
215 pub posted_amount: Money,
216 #[serde(rename = "billedAmount")]
217 pub billed_amount: Option<Money>,
218 #[serde(rename = "referenceNumber")]
219 pub reference_number: Option<String>,
220 #[serde(
221 rename = "authorizationExternalId",
222 skip_serializing_if = "Option::is_none"
223 )]
224 pub authorization_external_id: Option<String>,
225 #[serde(rename = "type")]
226 pub type_: String,
227 pub description: Option<String>,
228 pub account: TransactionAccountRef,
229 pub merchant: Option<serde_json::Value>,
230 pub lodging: Option<serde_json::Value>,
231}
232
233#[derive(Debug, Clone, Default, Serialize, Deserialize)]
234pub struct TransactionAccountRef {
235 #[serde(rename = "externalId")]
236 pub external_id: String,
237}
238
239#[derive(Debug, Clone, Default, Serialize, Deserialize)]
240pub struct Money {
241 #[serde(rename = "currencyCode")]
242 pub currency_code: String,
243 pub value: f64,
244}
245
246#[derive(Debug, Clone, Deserialize)]
247pub struct BulkResponse {
248 #[serde(rename = "requestId")]
249 pub request_id: String,
250 pub links: Vec<serde_json::Value>,
251}
252
253#[derive(Debug, Clone, Deserialize)]
254pub struct BulkRequestStatus {
255 #[serde(rename = "requestId")]
256 pub request_id: String,
257 pub status: String,
258 #[serde(rename = "processedCount")]
259 pub processed_count: Option<i32>,
260 #[serde(rename = "totalCount")]
261 pub total_count: Option<i32>,
262 pub errors: Option<Vec<serde_json::Value>>,
263}
264
265#[derive(Debug, Clone, Deserialize)]
266pub struct TransactionListResponse {
267 pub transactions: Vec<serde_json::Value>,
268 pub paging: Option<serde_json::Value>,
269}