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