br_pay/
lib.rs

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