br_pay/
lib.rs

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