Skip to main content

bpi_rs/wallet/
params.rs

1use serde_json::{Value, json};
2
3use crate::{BpiError, BpiResult};
4
5/// Parameters for `/paywallet/wallet/getUserWallet`.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct WalletInfoParams {
8    platform_type: u32,
9    timestamp_ms: i64,
10    trace_id: i64,
11    version: String,
12}
13
14impl WalletInfoParams {
15    /// Creates wallet-info parameters using the current UTC timestamp in milliseconds.
16    pub fn new() -> Self {
17        Self::at_timestamp(chrono::Utc::now().timestamp_millis())
18    }
19
20    /// Creates wallet-info parameters with a fixed timestamp for deterministic callers/tests.
21    pub fn at_timestamp(timestamp_ms: i64) -> Self {
22        Self {
23            platform_type: 3,
24            timestamp_ms,
25            trace_id: timestamp_ms,
26            version: "1.0".to_string(),
27        }
28    }
29
30    pub fn with_platform_type(mut self, platform_type: u32) -> BpiResult<Self> {
31        if platform_type == 0 {
32            return Err(BpiError::invalid_parameter(
33                "platformType",
34                "platform type must be non-zero",
35            ));
36        }
37
38        self.platform_type = platform_type;
39        Ok(self)
40    }
41
42    pub fn with_trace_id(mut self, trace_id: i64) -> BpiResult<Self> {
43        if trace_id <= 0 {
44            return Err(BpiError::invalid_parameter(
45                "traceId",
46                "trace id must be positive",
47            ));
48        }
49
50        self.trace_id = trace_id;
51        Ok(self)
52    }
53
54    pub fn with_version(mut self, version: impl Into<String>) -> BpiResult<Self> {
55        let version = version.into();
56        let version = version.trim();
57        if version.is_empty() {
58            return Err(BpiError::invalid_parameter(
59                "version",
60                "version cannot be blank",
61            ));
62        }
63
64        self.version = version.to_string();
65        Ok(self)
66    }
67
68    pub(crate) fn body(&self, csrf: &str) -> Value {
69        json!({
70            "csrf": csrf,
71            "platformType": self.platform_type,
72            "timestamp": self.timestamp_ms,
73            "traceId": self.trace_id,
74            "version": self.version,
75        })
76    }
77}
78
79impl Default for WalletInfoParams {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn wallet_info_params_serializes_default_body() {
91        let params = WalletInfoParams::at_timestamp(1_700_000_000_000);
92
93        assert_eq!(
94            params.body("csrf-token"),
95            json!({
96                "csrf": "csrf-token",
97                "platformType": 3,
98                "timestamp": 1_700_000_000_000_i64,
99                "traceId": 1_700_000_000_000_i64,
100                "version": "1.0",
101            })
102        );
103    }
104
105    #[test]
106    fn wallet_info_params_serializes_custom_values() -> BpiResult<()> {
107        let params = WalletInfoParams::at_timestamp(1_700_000_000_000)
108            .with_platform_type(4)?
109            .with_trace_id(1_700_000_000_001)?
110            .with_version("2.0")?;
111
112        assert_eq!(
113            params.body("csrf-token"),
114            json!({
115                "csrf": "csrf-token",
116                "platformType": 4,
117                "timestamp": 1_700_000_000_000_i64,
118                "traceId": 1_700_000_000_001_i64,
119                "version": "2.0",
120            })
121        );
122        Ok(())
123    }
124
125    #[test]
126    fn wallet_info_params_rejects_zero_platform_type() {
127        let err = WalletInfoParams::at_timestamp(1)
128            .with_platform_type(0)
129            .unwrap_err();
130
131        assert!(matches!(
132            err,
133            BpiError::InvalidParameter {
134                field: "platformType",
135                ..
136            }
137        ));
138    }
139
140    #[test]
141    fn wallet_info_params_rejects_blank_version() {
142        let err = WalletInfoParams::at_timestamp(1)
143            .with_version("  ")
144            .unwrap_err();
145
146        assert!(matches!(
147            err,
148            BpiError::InvalidParameter {
149                field: "version",
150                ..
151            }
152        ));
153    }
154
155    #[test]
156    fn wallet_info_params_trims_version() -> BpiResult<()> {
157        let params = WalletInfoParams::at_timestamp(1).with_version(" 2.0 ")?;
158
159        assert_eq!(params.body("csrf-token")["version"], "2.0");
160        Ok(())
161    }
162}