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