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