1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct AddressBeneficiary {
11 pub currency: String,
13 pub address: String,
15 #[serde(default)]
17 pub user_id: Option<u64>,
18 pub agreed: bool,
20 pub personal: bool,
22 pub unhosted: bool,
24 #[serde(default)]
26 pub beneficiary_vasp_name: Option<String>,
27 #[serde(default)]
29 pub beneficiary_vasp_did: Option<String>,
30 #[serde(default)]
32 pub beneficiary_vasp_website: Option<String>,
33 #[serde(default)]
35 pub beneficiary_first_name: Option<String>,
36 #[serde(default)]
38 pub beneficiary_last_name: Option<String>,
39 #[serde(default)]
41 pub beneficiary_company_name: Option<String>,
42 #[serde(default)]
44 pub beneficiary_address: Option<String>,
45 #[serde(default)]
47 pub tag: Option<String>,
48 #[serde(default, alias = "created")]
50 pub creation_timestamp: Option<u64>,
51 #[serde(default, alias = "updated")]
53 pub update_timestamp: Option<u64>,
54}
55
56#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
61pub struct SaveAddressBeneficiaryRequest {
62 pub currency: String,
64 pub address: String,
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub tag: Option<String>,
69 pub agreed: bool,
71 pub personal: bool,
73 pub unhosted: bool,
75 pub beneficiary_vasp_name: String,
77 pub beneficiary_vasp_did: String,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub beneficiary_vasp_website: Option<String>,
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub beneficiary_first_name: Option<String>,
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub beneficiary_last_name: Option<String>,
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub beneficiary_company_name: Option<String>,
91 pub beneficiary_address: String,
93}
94
95#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
97pub struct ListAddressBeneficiariesRequest {
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub currency: Option<String>,
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub address: Option<String>,
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub tag: Option<String>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub created_before: Option<u64>,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub created_after: Option<u64>,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub updated_before: Option<u64>,
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub updated_after: Option<u64>,
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub personal: Option<bool>,
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub unhosted: Option<bool>,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub beneficiary_vasp_name: Option<String>,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub beneficiary_vasp_did: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub beneficiary_vasp_website: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub limit: Option<u32>,
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub continuation: Option<String>,
140}
141
142#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
144pub struct ListAddressBeneficiariesResponse {
145 pub data: Vec<AddressBeneficiary>,
147 #[serde(default)]
149 pub count: Option<u64>,
150 #[serde(default)]
152 pub continuation: Option<String>,
153}
154
155#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
159pub struct DepositId {
160 pub currency: String,
162 pub user_id: u64,
164 pub address: String,
166 pub tx_hash: String,
168}
169
170#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
174pub struct Originator {
175 pub is_personal: bool,
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub company_name: Option<String>,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub first_name: Option<String>,
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub last_name: Option<String>,
186 pub address: String,
188}
189
190#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub struct ClearanceDepositResult {
195 pub currency: String,
197 #[serde(default)]
199 pub user_id: Option<u64>,
200 pub address: String,
202 pub amount: f64,
204 pub state: String,
206 #[serde(default)]
208 pub transaction_id: Option<String>,
209 #[serde(default)]
211 pub source_address: Option<String>,
212 #[serde(default)]
214 pub received_timestamp: Option<u64>,
215 #[serde(default)]
217 pub updated_timestamp: Option<u64>,
218 #[serde(default)]
220 pub note: Option<String>,
221 #[serde(default)]
224 pub clearance_state: Option<String>,
225 #[serde(default)]
227 pub refund_transaction_id: Option<String>,
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn test_address_beneficiary_deserialize() {
236 let json = r#"{
237 "currency": "BTC",
238 "address": "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf0uyj",
239 "user_id": 1016,
240 "agreed": true,
241 "personal": false,
242 "unhosted": false,
243 "beneficiary_vasp_name": "Money's Gone",
244 "beneficiary_vasp_did": "did:example:123456789abcdefghi",
245 "beneficiary_vasp_website": "https://example.com",
246 "beneficiary_first_name": "John",
247 "beneficiary_last_name": "Doe",
248 "beneficiary_company_name": "Example Corp",
249 "beneficiary_address": "NL, Amsterdam, Street, 1",
250 "created": 1536569522277,
251 "updated": 1536569522277
252 }"#;
253
254 let beneficiary: AddressBeneficiary = serde_json::from_str(json).unwrap();
255 assert_eq!(beneficiary.currency, "BTC");
256 assert_eq!(
257 beneficiary.address,
258 "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf0uyj"
259 );
260 assert!(beneficiary.agreed);
261 assert!(!beneficiary.personal);
262 assert!(!beneficiary.unhosted);
263 assert_eq!(
264 beneficiary.beneficiary_vasp_name,
265 Some("Money's Gone".to_string())
266 );
267 assert_eq!(beneficiary.beneficiary_first_name, Some("John".to_string()));
268 assert_eq!(beneficiary.creation_timestamp, Some(1536569522277));
269 }
270
271 #[test]
272 fn test_save_request_serialize() {
273 let request = SaveAddressBeneficiaryRequest {
274 currency: "BTC".to_string(),
275 address: "bc1qtest".to_string(),
276 tag: None,
277 agreed: true,
278 personal: false,
279 unhosted: false,
280 beneficiary_vasp_name: "Test VASP".to_string(),
281 beneficiary_vasp_did: "did:test:123".to_string(),
282 beneficiary_vasp_website: Some("https://test.com".to_string()),
283 beneficiary_first_name: Some("John".to_string()),
284 beneficiary_last_name: Some("Doe".to_string()),
285 beneficiary_company_name: None,
286 beneficiary_address: "Test Address".to_string(),
287 };
288
289 let json = serde_json::to_string(&request).unwrap();
290 assert!(json.contains("\"currency\":\"BTC\""));
291 assert!(json.contains("\"agreed\":true"));
292 assert!(!json.contains("\"tag\"")); assert!(!json.contains("beneficiary_company_name")); }
295
296 #[test]
297 fn test_list_response_deserialize() {
298 let json = r#"{
299 "data": [
300 {
301 "currency": "BTC",
302 "address": "bc1qtest",
303 "agreed": true,
304 "personal": false,
305 "unhosted": false
306 }
307 ],
308 "continuation": "xY7T6cutS3t2B9YtaDkE6TS379oKnkzTvmEDUnEUP2Msa9xKWNNaT",
309 "count": 1
310 }"#;
311
312 let response: ListAddressBeneficiariesResponse = serde_json::from_str(json).unwrap();
313 assert_eq!(response.data.len(), 1);
314 assert_eq!(response.count, Some(1));
315 assert!(response.continuation.is_some());
316 }
317
318 #[test]
319 fn test_clearance_deposit_result_deserialize() {
320 let json = r#"{
321 "currency": "BTC",
322 "user_id": 123,
323 "address": "2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBz",
324 "amount": 0.4,
325 "state": "completed",
326 "transaction_id": "230669110fdaf0a0dbcdc079b6b8b43d5af29cc73683835b9bc6b3406c065fda",
327 "source_address": "A3BqqD5GRJ8wHy1PYyCXTe9ke5226Fha123",
328 "received_timestamp": 1550574558607,
329 "updated_timestamp": 1550574558807,
330 "note": "Note",
331 "clearance_state": "in_progress"
332 }"#;
333
334 let result: ClearanceDepositResult = serde_json::from_str(json).unwrap();
335 assert_eq!(result.currency, "BTC");
336 assert_eq!(result.user_id, Some(123));
337 let amount_diff = (result.amount - 0.4).abs();
338 assert!(amount_diff < f64::EPSILON);
339 assert_eq!(result.state, "completed");
340 assert_eq!(result.clearance_state, Some("in_progress".to_string()));
341 }
342
343 #[test]
344 fn test_deposit_id_serialize() {
345 let deposit_id = DepositId {
346 currency: "BTC".to_string(),
347 user_id: 123,
348 address: "2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBz".to_string(),
349 tx_hash: "230669110fdaf0a0dbcdc079b6b8b43d5af29cc73683835b9bc6b3406c065fda".to_string(),
350 };
351
352 let json = serde_json::to_string(&deposit_id).unwrap();
353 assert!(json.contains("\"currency\":\"BTC\""));
354 assert!(json.contains("\"user_id\":123"));
355 assert!(json.contains("\"tx_hash\":"));
356 }
357
358 #[test]
359 fn test_originator_serialize() {
360 let originator = Originator {
361 is_personal: false,
362 company_name: Some("Company Name".to_string()),
363 first_name: Some("First".to_string()),
364 last_name: Some("Last".to_string()),
365 address: "NL, Amsterdam, Street, 1".to_string(),
366 };
367
368 let json = serde_json::to_string(&originator).unwrap();
369 assert!(json.contains("\"is_personal\":false"));
370 assert!(json.contains("\"company_name\":\"Company Name\""));
371 assert!(json.contains("\"address\":\"NL, Amsterdam, Street, 1\""));
372 }
373
374 #[test]
375 fn test_list_request_default() {
376 let request = ListAddressBeneficiariesRequest::default();
377 assert!(request.currency.is_none());
378 assert!(request.limit.is_none());
379 assert!(request.continuation.is_none());
380 }
381}