br_pay/
lib.rs

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