br_pay/
lib.rs

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