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