Skip to main content

bpi_rs/wallet/
client.rs

1use crate::wallet::{UserWallet, WalletInfoParams};
2use crate::{BilibiliRequest, BpiClient, BpiResult};
3
4const INFO_ENDPOINT: &str = "https://pay.bilibili.com/paywallet/wallet/getUserWallet";
5
6/// Wallet API client.
7#[derive(Clone, Copy)]
8pub struct WalletClient<'a> {
9    pub(crate) client: &'a BpiClient,
10}
11
12impl<'a> WalletClient<'a> {
13    pub(crate) fn new(client: &'a BpiClient) -> Self {
14        Self { client }
15    }
16
17    #[cfg(test)]
18    pub(crate) fn info_endpoint(&self) -> &'static str {
19        INFO_ENDPOINT
20    }
21
22    /// Gets authenticated wallet information.
23    pub async fn info(&self, params: WalletInfoParams) -> BpiResult<UserWallet> {
24        let csrf = self.client.csrf()?;
25        let body = params.body(&csrf);
26
27        self.client
28            .post(INFO_ENDPOINT)
29            .json(&body)
30            .send_bpi_payload("wallet.info")
31            .await
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use std::future::Future;
38
39    use crate::probe::contract::HttpMethod;
40    use crate::probe::endpoint_contract::EndpointContract;
41    use crate::wallet::{UserWallet, WalletInfoParams};
42    use crate::{BpiClient, BpiResult};
43
44    fn assert_info_future<F>(_future: F)
45    where
46        F: Future<Output = BpiResult<UserWallet>>,
47    {
48    }
49
50    fn contract() -> BpiResult<EndpointContract> {
51        EndpointContract::from_slice(include_bytes!(
52            "../../tests/contracts/wallet/read/info/contract.json"
53        ))
54    }
55
56    #[test]
57    fn wallet_client_exposes_promoted_endpoint_url() -> BpiResult<()> {
58        let client = BpiClient::new()?;
59        let wallet = client.wallet();
60
61        assert_eq!(
62            wallet.info_endpoint(),
63            "https://pay.bilibili.com/paywallet/wallet/getUserWallet"
64        );
65        Ok(())
66    }
67
68    #[test]
69    fn wallet_info_returns_payload_future() -> BpiResult<()> {
70        let client = BpiClient::new()?;
71        let wallet = client.wallet();
72
73        assert_info_future(wallet.info(WalletInfoParams::at_timestamp(1_700_000_000_000)));
74        Ok(())
75    }
76
77    #[test]
78    fn wallet_contract_matches_module_client_endpoint() -> BpiResult<()> {
79        let client = BpiClient::new()?;
80        let wallet = client.wallet();
81        let contract = contract()?;
82
83        assert_eq!(contract.name, "wallet.info");
84        assert_eq!(contract.request.method, HttpMethod::Post);
85        assert_eq!(contract.request.url.as_str(), wallet.info_endpoint());
86        assert!(contract.request.query.is_empty());
87        assert_eq!(contract.cases.len(), 2);
88
89        let body =
90            contract.request.body.as_ref().ok_or_else(|| {
91                crate::BpiError::unsupported_response("missing wallet contract body")
92            })?;
93        assert_eq!(body["csrf"], "${csrf}");
94        assert_eq!(body["platformType"], 3);
95        assert_eq!(body["timestamp"], 1_700_000_000_000_i64);
96        assert_eq!(body["traceId"], 1_700_000_000_000_i64);
97        assert_eq!(body["version"], "1.0");
98        Ok(())
99    }
100}