br_pay/
lib.rs

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