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