br_pay/
lib.rs

1use chrono::DateTime;
2use crate::wechat::Wechat;
3use json::{object, JsonValue};
4use crate::alipay::AliPay;
5
6pub mod alipay;
7pub mod wechat;
8
9#[derive(Clone)]
10pub enum Pay {
11    Wechat(Wechat),
12    Alipay(AliPay),
13    None
14}
15
16impl Pay {
17    pub fn new(data: JsonValue) -> Self {
18        match data["mode"].as_str().unwrap_or("") {
19            "wechat" => Pay::Wechat(Wechat {
20                appid: data["appid"].to_string(),
21                mchid: data["mchid"].to_string(),
22                serial_no: data["serial_no"].to_string(),
23                app_private: data["app_private"].to_string(),
24                secret: data["secret"].to_string(),
25                apikey: data["apikey"].to_string(),
26            }),
27            "alipay" => Pay::Alipay(AliPay {
28                appid: data["appid"].to_string(),
29                app_private: data["app_private"].to_string(),
30                app_auth_token: data["app_auth_token"].as_str().unwrap_or("").to_string(),
31            }),
32            _=>Pay::None
33        }
34    }
35}
36impl PayMode for Pay {
37    fn login(&mut self, code: &str) -> Result<JsonValue, String> {
38        match self {
39            Self::Wechat(e) => e.login(code),
40            Self::Alipay(e) => e.login(code),
41            Pay::None => Err("No login data".to_owned())
42        }
43    }
44
45    /// jsapi 支付
46    fn jsapi(
47        &mut self,
48        sub_mchid: &str,
49        out_trade_no: &str,
50        description: &str,
51        total_fee: f64,
52        notify_url: &str,
53        sp_openid: &str,
54    ) -> Result<JsonValue, String> {
55        match self {
56            Self::Wechat(e) => e.jsapi(
57                sub_mchid,
58                out_trade_no,
59                description,
60                total_fee,
61                notify_url,
62                sp_openid,
63            ),
64            Self::Alipay(e) => e.jsapi(
65                sub_mchid,
66                out_trade_no,
67                description,
68                total_fee,
69                notify_url,
70                sp_openid,
71            ),
72            Pay::None => Err("No login data".to_owned())
73        }
74    }
75
76    fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
77        match self {
78            Self::Wechat(e) => e.pay_query(
79                out_trade_no,
80                sub_mchid,
81            ),
82            Self::Alipay(e) => e.pay_query(
83                out_trade_no,
84                sub_mchid,
85            ),
86            Pay::None => Err("No login data".to_owned())
87
88        }
89    }
90
91    fn refund(&mut self, sub_mchid: &str, out_trade_no: &str, transaction_id: &str, out_refund_no: &str, amount: f64, total: f64, currency: &str) -> Result<JsonValue, String> {
92        match self {
93            Self::Wechat(e) => e.refund(
94                sub_mchid,
95                out_trade_no,
96                transaction_id,
97                out_refund_no,
98                amount,
99                total,
100                currency,
101            ),
102            Self::Alipay(e) => e.refund(
103                sub_mchid,
104                out_trade_no,
105                transaction_id,
106                out_refund_no,
107                amount,
108                total,
109                currency,
110            ),
111            Pay::None => Err("No login data".to_owned())
112
113        }
114    }
115
116    fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
117        match self {
118            Self::Wechat(e) => e.pay_notify(
119                nonce,
120                ciphertext,
121                associated_data,
122            ),
123            Self::Alipay(e) => e.pay_notify(
124                nonce,
125                ciphertext,
126                associated_data,
127            ),
128            Pay::None => Err("No login data".to_owned())
129
130        }
131    }
132
133    fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
134        match self {
135            Self::Wechat(e) => e.refund_notify(
136                nonce,
137                ciphertext,
138                associated_data,
139            ),
140            Self::Alipay(e) => e.refund_notify(
141                nonce,
142                ciphertext,
143                associated_data,
144            ),
145            Pay::None => Err("No login data".to_owned())
146
147        }
148    }
149
150    fn refund_query(&mut self, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
151        match self {
152            Self::Wechat(e) => e.refund_query(
153                out_refund_no,
154                sub_mchid,
155            ),
156            Self::Alipay(e) => e.refund_query(
157                out_refund_no,
158                sub_mchid,
159            ),
160            Pay::None => Err("No login data".to_owned())
161        }
162    }
163
164    fn auth(&mut self, code: &str) -> Result<JsonValue, String> {
165        match self {
166            Self::Wechat(e) => e.auth(
167                code
168            ),
169            Self::Alipay(e) => e.auth(
170                code
171            ),
172            Pay::None => Err("No login data".to_owned())
173        }
174    }
175
176    fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
177        match self {
178            Self::Wechat(e) => e.close(
179                out_trade_no,sub_mchid
180            ),
181            Self::Alipay(e) => e.close(
182                out_trade_no,sub_mchid
183            ),
184            Pay::None => Err("No login data".to_owned())
185        }
186    }
187}
188
189pub trait PayMode {
190    fn login(&mut self, code: &str) -> Result<JsonValue, String>;
191    fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
192    /// JSAPI 支付
193    /// * out_trade_no 商户订单号
194    /// * sub_mchid 子商户号
195    /// * description 商品名称
196    /// * total_fee 支付金额
197    /// * notify_url 通知地址
198    /// * sp_openid 支付客户ID
199    fn jsapi(&mut self, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, notify_url: &str, sp_openid: &str) -> Result<JsonValue, String>;
200    /// 关闭订单
201    fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
202    /// 订单查询
203    /// * out_trade_no 商户订单号
204    /// * sub_mchid 子商户号
205    fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
206    /// 支付成功通知
207    fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
208
209    #[allow(clippy::too_many_arguments)]
210    /// 订单退款
211    fn refund(&mut self, sub_mchid: &str, out_trade_no: &str, transaction_id: &str, out_refund_no: &str, amount: f64, total: f64, currency: &str) -> Result<JsonValue, String>;
212    /// 退款通知
213    fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
214    /// 退款查询
215    fn refund_query(&mut self, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
216}
217
218/// 支付回调数据
219#[derive(Debug)]
220pub struct PayNotify {
221    /// 支付方式
222    trade_type: TradeType,
223    /// 商户订单号
224    out_trade_no: String,
225    /// 服务商商户号
226    sp_mchid: String,
227    /// 子商户号
228    sub_mchid: String,
229    /// 应用APPID
230    sp_appid: String,
231    /// 微信内部订单号
232    transaction_id: String,
233    /// 成功时间
234    success_time: i64,
235    /// 服务商用户ID
236    sp_openid: String,
237    /// 子商户用户ID
238    sub_openid: String,
239    /// 支付金额
240    total: usize,
241    /// 实际支付金额
242    payer_total: usize,
243    /// 币种
244    currency: String,
245    /// 实际支付币种
246    payer_currency: String,
247    /// 状态
248    trade_state: TradeState,
249}
250impl PayNotify {
251    pub fn json(&self) -> JsonValue {
252        object! {
253            "trade_type" => self.trade_type.to_string(),
254            "out_trade_no" => self.out_trade_no.to_string(),
255            "sp_mchid" => self.sp_mchid.to_string(),
256            "sub_mchid" => self.sub_mchid.to_string(),
257            "sp_appid" => self.sp_appid.to_string(),
258            "transaction_id" => self.transaction_id.to_string(),
259            "success_time" => self.success_time,
260            "sp_openid" => self.sp_openid.to_string(),
261            "sub_openid" => self.sub_openid.to_string(),
262            "total" => self.total,
263            "payer_total" => self.payer_total,
264            "currency" => self.currency.clone(),
265            "payer_currency" => self.payer_currency.clone(),
266            "trade_state" => self.trade_state.to_string(),
267        }
268    }
269    pub fn success_time(datetime: &str) -> i64 {
270        if datetime.is_empty() {
271            return 0;
272        }
273        let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
274        datetime.timestamp()
275    }
276}
277#[derive(Debug)]
278pub enum TradeType {
279    /// 小程序与JSAPI
280    JSAPI,
281    None,
282}
283impl TradeType {
284    fn from(code: &str) -> TradeType {
285        match code {
286            "JSAPI" => TradeType::JSAPI,
287            _ => TradeType::None
288        }
289    }
290    fn to_string(&self) -> &'static str {
291        match self {
292            TradeType::JSAPI => "JSAPI",
293            TradeType::None => ""
294        }
295    }
296}
297#[derive(Debug)]
298pub enum TradeState {
299    /// 支付成功
300    SUCCESS,
301    /// 转入退款
302    REFUND,
303    /// 未支付
304    NOTPAY,
305    /// 已关闭
306    CLOSED,
307    /// 已撤销(仅付款码支付会返回)
308    REVOKED,
309    /// 用户支付中(仅付款码支付会返回)
310    USERPAYING,
311    /// 支付失败(仅付款码支付会返回)
312    PAYERROR,
313    None,
314}
315impl TradeState {
316    fn from(code: &str) -> TradeState {
317        match code {
318            "SUCCESS" => TradeState::SUCCESS,
319            "REFUND" => TradeState::REFUND,
320            "NOTPAY" => TradeState::NOTPAY,
321            "CLOSED" => TradeState::CLOSED,
322            "REVOKED" => TradeState::REVOKED,
323            "USERPAYING" => TradeState::USERPAYING,
324            "PAYERROR" => TradeState::PAYERROR,
325            _ => TradeState::None
326        }
327    }
328    fn to_string(&self) -> &'static str {
329        match self {
330            TradeState::SUCCESS => "已支付",
331            TradeState::REFUND => "已支付",
332            TradeState::NOTPAY => "待支付",
333            TradeState::CLOSED => "已关闭",
334            TradeState::REVOKED => "已关闭",
335            TradeState::USERPAYING => "待支付",
336            TradeState::PAYERROR => "支付失败",
337            TradeState::None => "待支付"
338        }
339    }
340}
341
342#[derive(Debug)]
343pub enum RefundStatus {
344    /// 退款成功
345    SUCCESS,
346    /// 退款关闭
347    CLOSED,
348    /// 退款处理中
349    PROCESSING,
350    /// 退款异常,手动处理此笔退款
351    ABNORMAL,
352    None,
353}
354impl RefundStatus {
355    fn from(code: &str) -> RefundStatus {
356        match code {
357            "SUCCESS" => RefundStatus::SUCCESS,
358            "CLOSED" => RefundStatus::CLOSED,
359            "PROCESSING" => RefundStatus::PROCESSING,
360            "ABNORMAL" => RefundStatus::ABNORMAL,
361            _ => RefundStatus::None
362        }
363    }
364    fn to_string(&self) -> &'static str {
365        match self {
366            RefundStatus::SUCCESS => "已退款",
367            RefundStatus::None => "退款中",
368            RefundStatus::CLOSED => "已关闭",
369            RefundStatus::PROCESSING => "退款中",
370            RefundStatus::ABNORMAL => "退款异常"
371        }
372    }
373}
374/// 退款回调数据
375#[derive(Debug)]
376pub struct RefundNotify {
377    /// 商户订单号
378    out_trade_no: String,
379    refund_no: String,
380    /// 服务商商户号
381    sp_mchid: String,
382    /// 子商户号
383    sub_mchid: String,
384    /// 微信内部订单号
385    transaction_id: String,
386    /// 退款订单号
387    refund_id: String,
388    /// 成功时间
389    success_time: i64,
390    /// 支付金额
391    total: f64,
392    /// 退款金额
393    refund: f64,
394    /// 实际支付金额
395    payer_total: f64,
396    /// 实际退款金额
397    payer_refund: f64,
398    /// 状态
399    status: RefundStatus,
400}
401
402impl RefundNotify {
403    pub fn json(&self) -> JsonValue {
404        object! {
405            "out_trade_no" => self.out_trade_no.clone(),
406            "sp_mchid" => self.sp_mchid.clone(),
407            "sub_mchid" => self.sub_mchid.clone(),
408            "transaction_id" => self.transaction_id.clone(),
409            "success_time" => self.success_time,
410            "total" => self.total,
411            "refund" => self.refund,
412            "payer_total" => self.payer_total,
413            "payer_refund" => self.payer_refund,
414            "status" => self.status.to_string(),
415            "refund_no"=>self.refund_no.clone(),
416            "refund_id"=>self.refund_id.clone(),
417        }
418    }
419}