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