br_pay/
lib.rs

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