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