1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9use super::contact::ContactAddress;
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub struct PaymentCurrencyAmount {
18 pub currency: String,
20 pub value: f64,
22}
23
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
28pub struct PaymentItem {
29 pub label: String,
31 pub amount: PaymentCurrencyAmount,
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub pending: Option<bool>,
36 #[serde(default = "default_refund_period")]
38 pub refund_period: i32,
39}
40
41fn default_refund_period() -> i32 {
42 30
43}
44
45#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49pub struct PaymentShippingOption {
50 pub id: String,
52 pub label: String,
54 pub amount: PaymentCurrencyAmount,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub selected: Option<bool>,
59}
60
61#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
65pub struct PaymentOptions {
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub request_payer_name: Option<bool>,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub request_payer_email: Option<bool>,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub request_payer_phone: Option<bool>,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub request_shipping: Option<bool>,
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub shipping_type: Option<String>,
77}
78
79impl Default for PaymentOptions {
80 fn default() -> Self {
81 Self {
82 request_payer_name: Some(false),
83 request_payer_email: Some(false),
84 request_payer_phone: Some(false),
85 request_shipping: Some(true),
86 shipping_type: None,
87 }
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
95pub struct PaymentMethodData {
96 pub supported_methods: String,
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub data: Option<HashMap<String, serde_json::Value>>,
101}
102
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
107pub struct PaymentDetailsModifier {
108 pub supported_methods: String,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub total: Option<PaymentItem>,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub additional_display_items: Option<Vec<PaymentItem>>,
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub data: Option<HashMap<String, serde_json::Value>>,
119}
120
121#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub struct PaymentDetailsInit {
126 pub id: String,
128 pub display_items: Vec<PaymentItem>,
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub shipping_options: Option<Vec<PaymentShippingOption>>,
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub modifiers: Option<Vec<PaymentDetailsModifier>>,
136 pub total: PaymentItem,
138}
139
140#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
144pub struct PaymentRequest {
145 pub method_data: Vec<PaymentMethodData>,
147 pub details: PaymentDetailsInit,
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub options: Option<PaymentOptions>,
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub shipping_address: Option<ContactAddress>,
155}
156
157#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
161pub struct PaymentResponse {
162 pub request_id: String,
164 pub method_name: String,
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub details: Option<HashMap<String, serde_json::Value>>,
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub shipping_address: Option<ContactAddress>,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub shipping_option: Option<PaymentShippingOption>,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub payer_name: Option<String>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub payer_email: Option<String>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub payer_phone: Option<String>,
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn payment_currency_amount_roundtrip() {
187 let amt = PaymentCurrencyAmount {
188 currency: "USD".into(),
189 value: 120.0,
190 };
191 let json = serde_json::to_string(&amt).unwrap();
192 let back: PaymentCurrencyAmount = serde_json::from_str(&json).unwrap();
193 assert_eq!(amt, back);
194 }
195
196 #[test]
197 fn payment_item_default_refund_period() {
198 let json = r#"{"label":"Shoes","amount":{"currency":"USD","value":99.99}}"#;
199 let item: PaymentItem = serde_json::from_str(json).unwrap();
200 assert_eq!(item.refund_period, 30);
201 }
202
203 #[test]
204 fn payment_request_full_roundtrip() {
205 let req = PaymentRequest {
206 method_data: vec![PaymentMethodData {
207 supported_methods: "CARD".into(),
208 data: Some(HashMap::from([(
209 "payment_processor_url".into(),
210 serde_json::Value::String("http://example.com/pay".into()),
211 )])),
212 }],
213 details: PaymentDetailsInit {
214 id: "order_123".into(),
215 display_items: vec![PaymentItem {
216 label: "Cool Shoes".into(),
217 amount: PaymentCurrencyAmount {
218 currency: "USD".into(),
219 value: 120.0,
220 },
221 pending: None,
222 refund_period: 30,
223 }],
224 shipping_options: None,
225 modifiers: None,
226 total: PaymentItem {
227 label: "Total".into(),
228 amount: PaymentCurrencyAmount {
229 currency: "USD".into(),
230 value: 120.0,
231 },
232 pending: None,
233 refund_period: 30,
234 },
235 },
236 options: Some(PaymentOptions {
237 request_payer_name: Some(false),
238 request_payer_email: Some(false),
239 request_payer_phone: Some(false),
240 request_shipping: Some(true),
241 shipping_type: None,
242 }),
243 shipping_address: None,
244 };
245 let json = serde_json::to_string(&req).unwrap();
246 let back: PaymentRequest = serde_json::from_str(&json).unwrap();
247 assert_eq!(req, back);
248 }
249
250 #[test]
251 fn payment_response_minimal() {
252 let resp = PaymentResponse {
253 request_id: "order_123".into(),
254 method_name: "CARD".into(),
255 details: None,
256 shipping_address: None,
257 shipping_option: None,
258 payer_name: None,
259 payer_email: None,
260 payer_phone: None,
261 };
262 let json = serde_json::to_string(&resp).unwrap();
263 let back: PaymentResponse = serde_json::from_str(&json).unwrap();
264 assert_eq!(resp, back);
265 }
266
267 #[test]
268 fn payment_method_data_single_string() {
269 let json = r#"{"supported_methods":"CARD"}"#;
271 let pmd: PaymentMethodData = serde_json::from_str(json).unwrap();
272 assert_eq!(pmd.supported_methods, "CARD");
273 }
274}