chip_sdk/model/
purchase.rs1use super::client_details::ClientDetails;
2use super::enums::{Platform, ProductType, PurchaseStatus, RefundAvailability};
3use super::issuer_details::IssuerDetails;
4use super::payment_details::PaymentDetails;
5use super::purchase_details::PurchaseDetails;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
9pub struct Purchase {
10 #[serde(skip_serializing_if = "Option::is_none")]
11 pub id: Option<String>,
12 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
13 pub object_type: Option<String>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub created_on: Option<f64>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub updated_on: Option<f64>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub client: Option<ClientDetails>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub purchase: Option<PurchaseDetails>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub payment: Option<PaymentDetails>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub issuer_details: Option<IssuerDetails>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub transaction_data: Option<serde_json::Value>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub status: Option<PurchaseStatus>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub status_history: Option<Vec<serde_json::Value>>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub viewed_on: Option<f64>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub company_id: Option<String>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub is_test: Option<bool>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub user_id: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub brand_id: Option<String>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub billing_template_id: Option<String>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub client_id: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub send_receipt: Option<bool>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub is_recurring_token: Option<bool>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub recurring_token: Option<String>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub skip_capture: Option<bool>,
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub force_recurring: Option<bool>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub reference_generated: Option<String>,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub reference: Option<String>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub issued: Option<String>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub due: Option<f64>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub refund_availability: Option<RefundAvailability>,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub refundable_amount: Option<f64>,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub currency_conversion: Option<serde_json::Value>,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub payment_method_whitelist: Option<Vec<String>>,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub success_redirect: Option<String>,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub failure_redirect: Option<String>,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub cancel_redirect: Option<String>,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub success_callback: Option<String>,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub creator_agent: Option<String>,
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub platform: Option<Platform>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub product: Option<ProductType>,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub created_from_ip: Option<String>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub invoice_url: Option<String>,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub checkout_url: Option<String>,
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub direct_post_url: Option<String>,
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::model::Product;
100
101 #[test]
102 fn serialize_purchase_for_create() {
103 let p = Purchase {
104 brand_id: Some("test-brand-id".into()),
105 client: Some(ClientDetails {
106 email: "test@test.com".into(),
107 ..Default::default()
108 }),
109 purchase: Some(PurchaseDetails {
110 products: vec![Product {
111 name: "Test".into(),
112 price: 100.0,
113 ..Default::default()
114 }],
115 ..Default::default()
116 }),
117 success_redirect: Some("https://example.com/success".into()),
118 failure_redirect: Some("https://example.com/failure".into()),
119 ..Default::default()
120 };
121 let json = serde_json::to_value(&p).unwrap();
122 assert_eq!(json["brand_id"], "test-brand-id");
123 assert_eq!(json["client"]["email"], "test@test.com");
124 assert_eq!(json["purchase"]["products"][0]["name"], "Test");
125 assert!(json.get("id").is_none());
126 assert!(json.get("status").is_none());
127 }
128
129 #[test]
130 fn deserialize_purchase_response() {
131 let json = r#"{"id":"abc-123","type":"purchase","status":"created","brand_id":"brand-uuid","checkout_url":"https://gate.chip-in.asia/checkout/abc-123","is_test":true,"client":{"email":"test@test.com"},"purchase":{"products":[{"name":"Item","price":500}],"currency":"MYR"},"refund_availability":"all","platform":"api","created_on":1700000000}"#;
132 let p: Purchase = serde_json::from_str(json).unwrap();
133 assert_eq!(p.id.as_deref(), Some("abc-123"));
134 assert_eq!(p.object_type.as_deref(), Some("purchase"));
135 assert_eq!(p.status, Some(PurchaseStatus::Created));
136 assert_eq!(
137 p.checkout_url.as_deref(),
138 Some("https://gate.chip-in.asia/checkout/abc-123")
139 );
140 assert_eq!(p.refund_availability, Some(RefundAvailability::All));
141 assert_eq!(p.platform, Some(Platform::Api));
142 }
143
144 #[test]
145 fn purchase_roundtrip() {
146 let p = Purchase {
147 brand_id: Some("brand-1".into()),
148 status: Some(PurchaseStatus::Paid),
149 ..Default::default()
150 };
151 let json = serde_json::to_string(&p).unwrap();
152 let p2: Purchase = serde_json::from_str(&json).unwrap();
153 assert_eq!(p.brand_id, p2.brand_id);
154 assert_eq!(p.status, p2.status);
155 }
156}