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