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(code),
139 Self::Alipay(e) => e.auth(code),
140 Pay::None => Err("No login data".to_owned())
141 }
142 }
143
144 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
145 match self {
146 Self::Wechat(e) => e.close(
147 out_trade_no, sub_mchid,
148 ),
149 Self::Alipay(e) => e.close(
150 out_trade_no, sub_mchid,
151 ),
152 Pay::None => Err("No login data".to_owned())
153 }
154 }
155
156 fn config(&mut self) -> JsonValue {
157 match self {
158 Self::Wechat(e) => object! {
159 sp_mchid:e.sp_mchid.clone(),
160 appid:e.appid.clone(),
161 },
162 Self::Alipay(e) => object! {
163 appid:e.appid.clone(),
164 sp_mchid:e.sp_mchid.clone()
165 },
166 Pay::None => object! {
167 appid:"",
168 sp_mchid:""
169 }
170 }
171 }
172
173 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String> {
174 match self {
175 Self::Wechat(e) => e.pay(
176 types,
177 sub_mchid,
178 out_trade_no,
179 description,
180 total_fee,
181 sp_openid,
182 ),
183 Self::Alipay(e) => e.pay(
184 types,
185 sub_mchid,
186 out_trade_no,
187 description,
188 total_fee,
189 sp_openid,
190 ),
191 Pay::None => Err("No login data".to_owned())
192 }
193 }
194
195 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String> {
196 match self {
197 Self::Wechat(e) => e.notify(data),
198 Self::Alipay(e) => e.notify(data),
199 Pay::None => Err("No login data".to_owned())
200 }
201 }
202
203 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String> {
204 match self {
205 Self::Wechat(e) => e.get_sub_mchid(sub_mchid),
206 Self::Alipay(e) => e.get_sub_mchid(sub_mchid),
207 Pay::None => Err("No login data".to_owned())
208 }
209 }
210}
211
212pub trait PayMode {
213 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
215 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String>;
216 fn config(&mut self) -> JsonValue;
217 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
218 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
219
220 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String>;
228 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
230 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
234 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
236
237 #[allow(clippy::too_many_arguments)]
238 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>;
240 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
242 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
244}
245
246#[derive(Debug)]
248pub struct PayNotify {
249 trade_type: TradeType,
251 out_trade_no: String,
253 sp_mchid: String,
255 sub_mchid: String,
257 sp_appid: String,
259 transaction_id: String,
261 success_time: i64,
263 sp_openid: String,
265 sub_openid: String,
267 total: usize,
269 payer_total: usize,
271 currency: String,
273 payer_currency: String,
275 trade_state: TradeState,
277}
278impl PayNotify {
279 pub fn json(&self) -> JsonValue {
280 object! {
281 "trade_type" => self.trade_type.to_string(),
282 "out_trade_no" => self.out_trade_no.to_string(),
283 "sp_mchid" => self.sp_mchid.to_string(),
284 "sub_mchid" => self.sub_mchid.to_string(),
285 "sp_appid" => self.sp_appid.to_string(),
286 "transaction_id" => self.transaction_id.to_string(),
287 "success_time" => self.success_time,
288 "sp_openid" => self.sp_openid.to_string(),
289 "sub_openid" => self.sub_openid.to_string(),
290 "total" => self.total,
291 "payer_total" => self.payer_total,
292 "currency" => self.currency.clone(),
293 "payer_currency" => self.payer_currency.clone(),
294 "trade_state" => self.trade_state.to_string(),
295 }
296 }
297 pub fn success_time(datetime: &str) -> i64 {
298 if datetime.is_empty() {
299 return 0;
300 }
301 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
302 datetime.timestamp()
303 }
304 pub fn alipay_time(datetime: &str) -> i64 {
305 if datetime.is_empty() {
306 return 0;
307 }
308 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
309 t.and_utc().timestamp()
310 }
311}
312#[derive(Debug)]
313pub enum TradeType {
314 JSAPI,
316 None,
317}
318impl TradeType {
319 fn from(code: &str) -> TradeType {
320 match code {
321 "JSAPI" => TradeType::JSAPI,
322 _ => TradeType::None
323 }
324 }
325 fn to_string(&self) -> &'static str {
326 match self {
327 TradeType::JSAPI => "JSAPI",
328 TradeType::None => ""
329 }
330 }
331}
332#[derive(Debug)]
333pub enum TradeState {
334 SUCCESS,
336 REFUND,
338 NOTPAY,
340 CLOSED,
342 REVOKED,
344 USERPAYING,
346 PAYERROR,
348 None,
349}
350impl TradeState {
351 fn from(code: &str) -> TradeState {
352 match code {
353 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
354 "REFUND" => TradeState::REFUND,
355 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
356 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
357 "REVOKED" => TradeState::REVOKED,
358 "USERPAYING" => TradeState::USERPAYING,
359 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
360 _ => TradeState::None
361 }
362 }
363
364 fn to_string(&self) -> &'static str {
365 match self {
366 TradeState::SUCCESS => "已支付",
367 TradeState::REFUND => "已支付",
368 TradeState::NOTPAY => "待支付",
369 TradeState::CLOSED => "已关闭",
370 TradeState::REVOKED => "已关闭",
371 TradeState::USERPAYING => "待支付",
372 TradeState::PAYERROR => "支付失败",
373 TradeState::None => "待支付"
374 }
375 }
376}
377
378#[derive(Debug)]
379pub enum RefundStatus {
380 SUCCESS,
382 CLOSED,
384 PROCESSING,
386 ABNORMAL,
388 None,
389}
390impl RefundStatus {
391 fn from(code: &str) -> RefundStatus {
392 match code {
393 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
394 "CLOSED" => RefundStatus::CLOSED,
395 "PROCESSING" | "N" => RefundStatus::PROCESSING,
396 "ABNORMAL" => RefundStatus::ABNORMAL,
397 _ => RefundStatus::None
398 }
399 }
400 fn to_string(&self) -> &'static str {
401 match self {
402 RefundStatus::SUCCESS => "已退款",
403 RefundStatus::None => "退款中",
404 RefundStatus::CLOSED => "已关闭",
405 RefundStatus::PROCESSING => "退款中",
406 RefundStatus::ABNORMAL => "退款异常"
407 }
408 }
409}
410#[derive(Debug)]
412pub struct RefundNotify {
413 out_trade_no: String,
415 refund_no: String,
416 sp_mchid: String,
418 sub_mchid: String,
420 transaction_id: String,
422 refund_id: String,
424 success_time: i64,
426 total: f64,
428 refund: f64,
430 payer_total: f64,
432 payer_refund: f64,
434 status: RefundStatus,
436}
437
438impl RefundNotify {
439 pub fn json(&self) -> JsonValue {
440 object! {
441 "out_trade_no" => self.out_trade_no.clone(),
442 "sp_mchid" => self.sp_mchid.clone(),
443 "sub_mchid" => self.sub_mchid.clone(),
444 "transaction_id" => self.transaction_id.clone(),
445 "success_time" => self.success_time,
446 "total" => self.total,
447 "refund" => self.refund,
448 "payer_total" => self.payer_total,
449 "payer_refund" => self.payer_refund,
450 "status" => self.status.to_string(),
451 "refund_no"=>self.refund_no.clone(),
452 "refund_id"=>self.refund_id.clone()
453 }
454 }
455}
456
457pub enum Types {
458 Jsapi,
460 Native,
462 H5,
464 MiniJsapi,
466 App,
468 Micropay,
470}
471impl Types {
472 pub fn str(self) -> &'static str {
473 match self {
474 Types::Jsapi => "jsapi",
475 Types::Native => "native",
476 Types::H5 => "h5",
477 Types::MiniJsapi => "minijsapi",
478 Types::App => "app",
479 Types::Micropay => "micropay"
480 }
481 }
482 pub fn from(name: &str) -> Self {
483 match name {
484 "jsapi" => Types::Jsapi,
485 "native" => Types::Native,
486 "h5" => Types::H5,
487 "minijsapi" => Types::MiniJsapi,
488 "app" => Types::App,
489 "micropay" => Types::Micropay,
490 _ => Types::Jsapi
491 }
492 }
493}