br_pay/
lib.rs

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