br_pay/
lib.rs

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