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