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