1use crate::BilibiliRequest;
2use crate::BpiError;
3use crate::BpiResult;
4use crate::electric::ElectricClient;
5use serde::{Deserialize, Serialize};
6
7const BCOIN_QUICK_PAY_ENDPOINT: &str =
8 "https://api.bilibili.com/x/ugcpay/web/v2/trade/elec/pay/quick";
9
10#[derive(Debug, Serialize, Clone, Deserialize)]
11pub struct BcoinQuickPayData {
12 pub mid: i64,
14 pub up_mid: i64,
16 pub order_no: String,
18 pub bp_num: String,
20 pub exp: u32,
22 pub status: i32,
27 pub msg: String,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct BcoinQuickPayParams {
34 bp_num: i32,
35 is_bp_remains_prior: bool,
36 up_mid: i64,
37 otype: String,
38 oid: i64,
39}
40
41impl BcoinQuickPayParams {
42 pub fn new(
43 bp_num: i32,
44 is_bp_remains_prior: bool,
45 up_mid: i64,
46 otype: impl Into<String>,
47 oid: i64,
48 ) -> BpiResult<Self> {
49 if !(2..=9999).contains(&bp_num) {
50 return Err(BpiError::invalid_parameter(
51 "bp_num",
52 "value must be between 2 and 9999",
53 ));
54 }
55 if up_mid <= 0 {
56 return Err(BpiError::invalid_parameter("up_mid", "id must be positive"));
57 }
58 if oid <= 0 {
59 return Err(BpiError::invalid_parameter("oid", "id must be positive"));
60 }
61
62 let otype = otype.into();
63 if !matches!(otype.as_str(), "up" | "archive") {
64 return Err(BpiError::invalid_parameter(
65 "otype",
66 "value must be 'up' or 'archive'",
67 ));
68 }
69
70 Ok(Self {
71 bp_num,
72 is_bp_remains_prior,
73 up_mid,
74 otype,
75 oid,
76 })
77 }
78
79 fn form_pairs(&self, csrf: &str) -> Vec<(&'static str, String)> {
80 vec![
81 ("bp_num", self.bp_num.to_string()),
82 ("is_bp_remains_prior", self.is_bp_remains_prior.to_string()),
83 ("up_mid", self.up_mid.to_string()),
84 ("otype", self.otype.clone()),
85 ("oid", self.oid.to_string()),
86 ("csrf", csrf.to_string()),
87 ]
88 }
89}
90
91impl<'a> ElectricClient<'a> {
92 pub async fn bcoin_quick_pay(
94 &self,
95 params: BcoinQuickPayParams,
96 ) -> BpiResult<BcoinQuickPayData> {
97 let csrf = self.client.csrf()?;
98
99 self.client
100 .post(BCOIN_QUICK_PAY_ENDPOINT)
101 .form(¶ms.form_pairs(&csrf))
102 .send_bpi_payload("electric.bcoin.quick_pay")
103 .await
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn bcoin_quick_pay_params_rejects_invalid_charge_amount() {
113 let err = BcoinQuickPayParams::new(1, true, 42, "up", 42).unwrap_err();
114
115 assert!(matches!(
116 err,
117 BpiError::InvalidParameter {
118 field: "bp_num",
119 ..
120 }
121 ));
122 }
123}