br_pay/
lib.rs

1use chrono::DateTime;
2use crate::wechat::Wechat;
3use json::{object, JsonValue};
4
5pub mod alipay;
6pub mod wechat;
7
8#[derive(Clone)]
9pub enum Pay {
10    Wechat(Wechat),
11    Alipay,
12}
13
14impl Pay {
15    pub fn new(data: JsonValue) -> Self {
16        match data["mode"].as_str().unwrap_or("") {
17            "wechat" => Pay::Wechat(Wechat {
18                appid: data["appid"].to_string(),
19                mchid: data["mchid"].to_string(),
20                serial_no: data["serial_no"].to_string(),
21                cert_path: data["cert_path"].to_string(),
22                secret: data["secret"].to_string(),
23                apikey: data["apikey"].to_string(),
24            }),
25            _ => Pay::Alipay,
26        }
27    }
28}
29impl PayMode for Pay {
30    fn login(&self, code: &str) -> Result<JsonValue, String> {
31        match self {
32            Self::Wechat(e) => e.login(code),
33            Self::Alipay => Err("Alipay alipay".to_string()),
34        }
35    }
36
37    /// jsapi 支付
38    fn jsapi(
39        &mut self,
40        sub_mchid: &str,
41        out_trade_no: &str,
42        description: &str,
43        total_fee: f64,
44        notify_url: &str,
45        sp_openid: &str,
46    ) -> Result<JsonValue, String> {
47        match self {
48            Self::Wechat(e) => e.jsapi(
49                sub_mchid,
50                out_trade_no,
51                description,
52                total_fee,
53                notify_url,
54                sp_openid,
55            ),
56            Self::Alipay => Err("Alipay alipay".to_string()),
57        }
58    }
59
60    fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
61        match self {
62            Self::Wechat(e) => e.pay_query(
63                out_trade_no,
64                sub_mchid,
65            ),
66            Self::Alipay => Err("Alipay alipay".to_string()),
67        }
68    }
69
70    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> {
71        match self {
72            Self::Wechat(e) => e.refund(
73                sub_mchid,
74                out_trade_no,
75                transaction_id,
76                out_refund_no,
77                amount,
78                total,
79                currency,
80            ),
81            Self::Alipay => Err("Alipay alipay".to_string()),
82        }
83    }
84
85    fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
86        match self {
87            Self::Wechat(e) => e.pay_notify(
88                nonce,
89                ciphertext,
90                associated_data,
91            ),
92            Self::Alipay => Err("Alipay alipay".to_string()),
93        }
94    }
95
96    fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
97        match self {
98            Self::Wechat(e) => e.refund_notify(
99                nonce,
100                ciphertext,
101                associated_data,
102            ),
103            Self::Alipay => Err("Alipay alipay".to_string()),
104        }
105    }
106
107    fn refund_query(&mut self, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
108        match self {
109            Self::Wechat(e) => e.refund_query(
110                out_refund_no,
111                sub_mchid,
112            ),
113            Self::Alipay => Err("Alipay alipay".to_string()),
114        }
115    }
116}
117
118pub trait PayMode {
119    fn login(&self, code: &str) -> Result<JsonValue, String>;
120    /// JSAPI 支付
121    /// * out_trade_no 商户订单号
122    /// * sub_mchid 子商户号
123    /// * description 商品名称
124    /// * total_fee 支付金额
125    /// * notify_url 通知地址
126    /// * sp_openid 支付客户ID
127    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>;
128    /// 订单查询
129    /// * out_trade_no 商户订单号
130    /// * sub_mchid 子商户号
131    fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
132    /// 支付成功通知
133    fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
134
135    #[allow(clippy::too_many_arguments)]
136    /// 订单退款
137    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>;
138    /// 退款通知
139    fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
140    /// 退款查询
141    fn refund_query(&mut self, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
142}
143
144/// 支付回调数据
145#[derive(Debug)]
146pub struct PayNotify {
147    /// 支付方式
148    trade_type: TradeType,
149    /// 商户订单号
150    out_trade_no: String,
151    /// 服务商商户号
152    sp_mchid: String,
153    /// 子商户号
154    sub_mchid: String,
155    /// 应用APPID
156    sp_appid: String,
157    /// 微信内部订单号
158    transaction_id: String,
159    /// 成功时间
160    success_time: i64,
161    /// 服务商用户ID
162    sp_openid: String,
163    /// 子商户用户ID
164    sub_openid: String,
165    /// 支付金额
166    total: usize,
167    /// 实际支付金额
168    payer_total: usize,
169    /// 币种
170    currency: String,
171    /// 实际支付币种
172    payer_currency: String,
173    /// 状态
174    trade_state: TradeState,
175}
176impl PayNotify {
177    pub fn json(&self) -> JsonValue {
178        object! {
179            "trade_type" => self.trade_type.to_string(),
180            "out_trade_no" => self.out_trade_no.to_string(),
181            "sp_mchid" => self.sp_mchid.to_string(),
182            "sub_mchid" => self.sub_mchid.to_string(),
183            "sp_appid" => self.sp_appid.to_string(),
184            "transaction_id" => self.transaction_id.to_string(),
185            "success_time" => self.success_time,
186            "sp_openid" => self.sp_openid.to_string(),
187            "sub_openid" => self.sub_openid.to_string(),
188            "total" => self.total,
189            "payer_total" => self.payer_total,
190            "currency" => self.currency.clone(),
191            "payer_currency" => self.payer_currency.clone(),
192            "trade_state" => self.trade_state.to_string(),
193        }
194    }
195    pub fn success_time(datetime: &str) -> i64 {
196        if datetime.is_empty() {
197            return 0;
198        }
199        let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
200        datetime.timestamp()
201    }
202}
203#[derive(Debug)]
204pub enum TradeType {
205    /// 小程序与JSAPI
206    JSAPI,
207    None,
208}
209impl TradeType {
210    fn from(code: &str) -> TradeType {
211        match code {
212            "JSAPI" => TradeType::JSAPI,
213            _ => TradeType::None
214        }
215    }
216    fn to_string(&self) -> &'static str {
217        match self {
218            TradeType::JSAPI => "JSAPI",
219            TradeType::None => ""
220        }
221    }
222}
223#[derive(Debug)]
224pub enum TradeState {
225    /// 支付成功
226    SUCCESS,
227    /// 转入退款
228    REFUND,
229    /// 未支付
230    NOTPAY,
231    /// 已关闭
232    CLOSED,
233    /// 已撤销(仅付款码支付会返回)
234    REVOKED,
235    /// 用户支付中(仅付款码支付会返回)
236    USERPAYING,
237    /// 支付失败(仅付款码支付会返回)
238    PAYERROR,
239    None,
240}
241impl TradeState {
242    fn from(code: &str) -> TradeState {
243        match code {
244            "SUCCESS" => TradeState::SUCCESS,
245            "REFUND" => TradeState::REFUND,
246            "NOTPAY" => TradeState::NOTPAY,
247            "CLOSED" => TradeState::CLOSED,
248            "REVOKED" => TradeState::REVOKED,
249            "USERPAYING" => TradeState::USERPAYING,
250            "PAYERROR" => TradeState::PAYERROR,
251            _ => TradeState::None
252        }
253    }
254    fn to_string(&self) -> &'static str {
255        match self {
256            TradeState::SUCCESS => "已支付",
257            TradeState::REFUND => "已支付",
258            TradeState::NOTPAY => "待支付",
259            TradeState::CLOSED => "已关闭",
260            TradeState::REVOKED => "已关闭",
261            TradeState::USERPAYING => "待支付",
262            TradeState::PAYERROR => "支付失败",
263            TradeState::None => "待支付"
264        }
265    }
266}
267
268#[derive(Debug)]
269pub enum RefundStatus {
270    /// 退款成功
271    SUCCESS,
272    /// 退款关闭
273    CLOSED,
274    /// 退款处理中
275    PROCESSING,
276    /// 退款异常,手动处理此笔退款
277    ABNORMAL,
278    None,
279}
280impl RefundStatus {
281    fn from(code: &str) -> RefundStatus {
282        match code {
283            "SUCCESS" => RefundStatus::SUCCESS,
284            "CLOSED" => RefundStatus::CLOSED,
285            "PROCESSING" => RefundStatus::PROCESSING,
286            "ABNORMAL" => RefundStatus::ABNORMAL,
287            _ => RefundStatus::None
288        }
289    }
290    fn to_string(&self) -> &'static str {
291        match self {
292            RefundStatus::SUCCESS => "已退款",
293            RefundStatus::None => "退款中",
294            RefundStatus::CLOSED => "已关闭",
295            RefundStatus::PROCESSING => "退款中",
296            RefundStatus::ABNORMAL => "退款异常"
297        }
298    }
299}
300/// 退款回调数据
301#[derive(Debug)]
302pub struct RefundNotify {
303    /// 商户订单号
304    out_trade_no: String,
305    refund_no: String,
306    /// 服务商商户号
307    sp_mchid: String,
308    /// 子商户号
309    sub_mchid: String,
310    /// 微信内部订单号
311    transaction_id: String,
312    /// 退款订单号
313    refund_id: String,
314    /// 成功时间
315    success_time: i64,
316    /// 支付金额
317    total: f64,
318    /// 退款金额
319    refund: f64,
320    /// 实际支付金额
321    payer_total: f64,
322    /// 实际退款金额
323    payer_refund: f64,
324    /// 状态
325    status: RefundStatus,
326}
327
328impl RefundNotify {
329    pub fn json(&self) -> JsonValue {
330        object! {
331            "out_trade_no" => self.out_trade_no.clone(),
332            "sp_mchid" => self.sp_mchid.clone(),
333            "sub_mchid" => self.sub_mchid.clone(),
334            "transaction_id" => self.transaction_id.clone(),
335            "success_time" => self.success_time,
336            "total" => self.total,
337            "refund" => self.refund,
338            "payer_total" => self.payer_total,
339            "payer_refund" => self.payer_refund,
340            "status" => self.status.to_string(),
341            "refund_no"=>self.refund_no.clone(),
342            "refund_id"=>self.refund_id.clone(),
343        }
344    }
345}