1use chrono::{DateTime, FixedOffset, Local, NaiveDateTime};
2use crate::wechat::{Wechat, ACCESS_TOKEN, TICKET};
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" => {
20 let is_none = ACCESS_TOKEN.lock().unwrap().get(&*data["appid"].to_string()).is_none();
21 let access_token = if is_none {
22 "".to_string()
23 } else {
24 ACCESS_TOKEN.lock().unwrap().get(&*data["appid"].to_string()).unwrap().clone()["access_token"].to_string()
25 };
26 let expires_in = if is_none {
27 0
28 } else {
29 ACCESS_TOKEN.lock().unwrap().get(&*data["appid"].to_string()).unwrap().clone()["expires_in"].as_i64().unwrap()
30 };
31
32 let is_none = TICKET.lock().unwrap().get(&*data["appid"].to_string()).is_none();
33 let ticket = if is_none {
34 "".to_string()
35 } else {
36 TICKET.lock().unwrap().get(&*data["appid"].to_string()).unwrap().clone()["ticket"].to_string()
37 };
38 let ticket_expires_in = if is_none {
39 0
40 } else {
41 TICKET.lock().unwrap().get(&*data["appid"].to_string()).unwrap().clone()["ticket_expires_in"].as_i64().unwrap()
42 };
43 Pay::Wechat(Wechat {
44 appid: data["appid"].to_string(),
45 sp_mchid: data["sp_mchid"].to_string(),
46 serial_no: data["serial_no"].to_string(),
47 app_private: data["app_private"].to_string(),
48 secret: data["secret"].to_string(),
49 apikey: data["apikey"].to_string(),
50 apiv2: data["apiv2"].to_string(),
51 notify_url: data["notify_url"].to_string(),
52 access_token,
53 expires_in,
54 ticket,
55 ticket_expires_in,
56 })
57 }
58 "alipay" => Pay::Alipay(AliPay {
59 appid: data["appid"].to_string(),
60 sp_mchid: data["sp_mchid"].to_string(),
61 app_private: data["app_private"].to_string(),
62 app_auth_token: data["app_auth_token"].as_str().unwrap_or("").to_string(),
63 content_encryp: data["content_encryp"].to_string(),
64 alipay_public_key: data["alipay_public_key"].to_string(),
65 notify_url: data["notify_url"].to_string(),
66 }),
67 _ => Pay::None
68 }
69 }
70}
71impl PayMode for Pay {
72 fn jsapi_ticket(&mut self, url: &str) -> Result<JsonValue, String> {
73 match self {
74 Self::Wechat(e) => e.jsapi_ticket(url),
75 _ => Err("No login data".to_owned())
76 }
77 }
78
79 fn get_openid(&mut self, code: &str) -> Result<JsonValue, String> {
80 match self {
81 Self::Wechat(e) => e.get_openid(code),
82 _ => Err("No login data".to_owned())
83 }
84 }
85
86 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String> {
87 match self {
88 Self::Wechat(e) => e.get_sub_mchid(sub_mchid),
89 Self::Alipay(e) => e.get_sub_mchid(sub_mchid),
90 Pay::None => Err("No login data".to_owned())
91 }
92 }
93
94 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String> {
95 match self {
96 Self::Wechat(e) => e.notify(data),
97 Self::Alipay(e) => e.notify(data),
98 Pay::None => Err("No login data".to_owned())
99 }
100 }
101
102 fn config(&mut self) -> JsonValue {
103 match self {
104 Self::Wechat(e) => object! {
105 sp_mchid:e.sp_mchid.clone(),
106 appid:e.appid.clone(),
107 },
108 Self::Alipay(e) => object! {
109 appid:e.appid.clone(),
110 sp_mchid:e.sp_mchid.clone()
111 },
112 Pay::None => object! {
113 appid:"",
114 sp_mchid:""
115 }
116 }
117 }
118
119 fn login(&mut self, code: &str) -> Result<JsonValue, String> {
120 match self {
121 Self::Wechat(e) => e.login(code),
122 Self::Alipay(e) => e.login(code),
123 Pay::None => Err("No login data".to_owned())
124 }
125 }
126
127 fn auth(&mut self, code: &str) -> Result<JsonValue, String> {
128 match self {
129 Self::Wechat(e) => e.auth(code),
130 Self::Alipay(e) => e.auth(code),
131 Pay::None => Err("No login data".to_owned())
132 }
133 }
134
135 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String> {
136 match self {
137 Self::Wechat(e) => e.pay(
138 types,
139 sub_mchid,
140 out_trade_no,
141 description,
142 total_fee,
143 sp_openid,
144 ),
145 Self::Alipay(e) => e.pay(
146 types,
147 sub_mchid,
148 out_trade_no,
149 description,
150 total_fee,
151 sp_openid,
152 ),
153 Pay::None => Err("No login data".to_owned())
154 }
155 }
156
157 fn micropay(&mut self, auth_code: &str, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, org_openid: &str, ip: &str) -> Result<JsonValue, String> {
158 match self {
159 Self::Wechat(e) => e.micropay(
160 auth_code,
161 sub_mchid,
162 out_trade_no,
163 description,
164 total_fee,
165 org_openid,
166 ip,
167 ),
168 Self::Alipay(e) => e.micropay(
169 auth_code,
170 sub_mchid,
171 out_trade_no,
172 description,
173 total_fee,
174 org_openid,
175 ip,
176 ),
177 Pay::None => Err("No login data".to_owned())
178 }
179 }
180
181 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
182 match self {
183 Self::Wechat(e) => e.close(
184 out_trade_no, sub_mchid,
185 ),
186 Self::Alipay(e) => e.close(
187 out_trade_no, sub_mchid,
188 ),
189 Pay::None => Err("No login data".to_owned())
190 }
191 }
192
193 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
194 match self {
195 Self::Wechat(e) => e.pay_query(
196 out_trade_no,
197 sub_mchid,
198 ),
199 Self::Alipay(e) => e.pay_query(
200 out_trade_no,
201 sub_mchid,
202 ),
203 Pay::None => Err("No login data".to_owned())
204 }
205 }
206 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
207 match self {
208 Self::Wechat(e) => e.pay_micropay_query(
209 out_trade_no,
210 sub_mchid,
211 ),
212 Self::Alipay(e) => e.pay_query(
213 out_trade_no,
214 sub_mchid,
215 ),
216 Pay::None => Err("No login data".to_owned())
217 }
218 }
219
220 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
221 match self {
222 Self::Wechat(e) => e.pay_notify(
223 nonce,
224 ciphertext,
225 associated_data,
226 ),
227 Self::Alipay(e) => e.pay_notify(
228 nonce,
229 ciphertext,
230 associated_data,
231 ),
232 Pay::None => Err("No login data".to_owned())
233 }
234 }
235
236 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> {
237 match self {
238 Self::Wechat(e) => e.refund(
239 sub_mchid,
240 out_trade_no,
241 transaction_id,
242 out_refund_no,
243 amount,
244 total,
245 currency,
246 ),
247 Self::Alipay(e) => e.refund(
248 sub_mchid,
249 out_trade_no,
250 transaction_id,
251 out_refund_no,
252 amount,
253 total,
254 currency,
255 ),
256 Pay::None => Err("No login data".to_owned())
257 }
258 }
259
260 fn micropay_refund(&mut self, sub_mchid: &str, out_trade_no: &str, transaction_id: &str, out_refund_no: &str, amount: f64, total: f64, currency: &str, refund_text: &str) -> Result<JsonValue, String> {
261 match self {
262 Self::Wechat(e) => e.micropay_refund(
263 sub_mchid,
264 out_trade_no,
265 transaction_id,
266 out_refund_no,
267 amount,
268 total,
269 currency,
270 refund_text,
271 ),
272 Self::Alipay(e) => e.refund(
273 sub_mchid,
274 out_trade_no,
275 transaction_id,
276 out_refund_no,
277 amount,
278 total,
279 currency,
280 ),
281 Pay::None => Err("No login data".to_owned())
282 }
283 }
284
285 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
286 match self {
287 Self::Wechat(e) => e.refund_notify(
288 nonce,
289 ciphertext,
290 associated_data,
291 ),
292 Self::Alipay(e) => e.refund_notify(
293 nonce,
294 ciphertext,
295 associated_data,
296 ),
297 Pay::None => Err("No login data".to_owned())
298 }
299 }
300
301 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
302 match self {
303 Self::Wechat(e) => e.refund_query(
304 trade_no,
305 out_refund_no,
306 sub_mchid,
307 ),
308 Self::Alipay(e) => e.refund_query(
309 trade_no,
310 out_refund_no,
311 sub_mchid,
312 ),
313 Pay::None => Err("No login data".to_owned())
314 }
315 }
316
317 fn incoming(&mut self, business_code: &str, contact_info: JsonValue, subject_info: JsonValue, business_info: JsonValue, settlement_info: JsonValue, bank_account_info: JsonValue) -> Result<JsonValue, String> {
318 match self {
319 Self::Wechat(e) => e.incoming(
320 business_code,
321 contact_info,
322 subject_info,
323 business_info,
324 settlement_info,
325 bank_account_info,
326 ),
327 Self::Alipay(e) => e.incoming(
328 business_code,
329 contact_info,
330 subject_info,
331 business_info,
332 settlement_info,
333 bank_account_info,
334 ),
335 Pay::None => Err("No incoming data".to_owned())
336 }
337 }
338}
339
340pub trait PayMode {
341 fn jsapi_ticket(&mut self, url: &str) -> Result<JsonValue, String>;
343 fn get_openid(&mut self, code: &str) -> Result<JsonValue, String>;
344 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
346 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String>;
347 fn config(&mut self) -> JsonValue;
348 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
349 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
350
351 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String>;
359
360 #[allow(clippy::too_many_arguments)]
361 fn micropay(&mut self, auth_code: &str, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, org_openid: &str, ip: &str) -> Result<JsonValue, String>;
362 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
364 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
368 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
369 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
371
372 #[allow(clippy::too_many_arguments)]
373 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>;
375 #[allow(clippy::too_many_arguments)]
376 fn micropay_refund(&mut self, sub_mchid: &str, out_trade_no: &str, transaction_id: &str, out_refund_no: &str, amount: f64, total: f64, currency: &str, refund_text: &str) -> Result<JsonValue, String>;
377 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
379 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
381 fn incoming(&mut self, business_code: &str, contact_info: JsonValue, subject_info: JsonValue, business_info: JsonValue, settlement_info: JsonValue, bank_account_info: JsonValue) -> Result<JsonValue, String>;
383}
384
385#[derive(Debug)]
387pub struct PayNotify {
388 trade_type: TradeType,
390 out_trade_no: String,
392 sp_mchid: String,
394 sub_mchid: String,
396 sp_appid: String,
398 transaction_id: String,
400 success_time: i64,
402 sp_openid: String,
404 sub_openid: String,
406 total: f64,
408 payer_total: f64,
410 currency: String,
412 payer_currency: String,
414 trade_state: TradeState,
416}
417impl PayNotify {
418 pub fn json(&self) -> JsonValue {
419 object! {
420 "trade_type" => self.trade_type.to_string(),
421 "out_trade_no" => self.out_trade_no.to_string(),
422 "sp_mchid" => self.sp_mchid.to_string(),
423 "sub_mchid" => self.sub_mchid.to_string(),
424 "sp_appid" => self.sp_appid.to_string(),
425 "transaction_id" => self.transaction_id.to_string(),
426 "success_time" => self.success_time,
427 "sp_openid" => self.sp_openid.to_string(),
428 "sub_openid" => self.sub_openid.to_string(),
429 "total" => self.total,
430 "payer_total" => self.payer_total,
431 "currency" => self.currency.clone(),
432 "payer_currency" => self.payer_currency.clone(),
433 "trade_state" => self.trade_state.to_string(),
434 }
435 }
436 pub fn success_time(datetime: &str) -> i64 {
437 if datetime.is_empty() {
438 return 0;
439 }
440 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
441 datetime.timestamp()
442 }
443 pub fn alipay_time(datetime: &str) -> i64 {
444 if datetime.is_empty() {
445 return 0;
446 }
447 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
448 t.and_utc().timestamp()
449 }
450 pub fn datetime_to_timestamp(datetime: &str, fmt: &str) -> i64 {
451 let t = NaiveDateTime::parse_from_str(datetime, fmt).unwrap();
452 let tz = FixedOffset::east_opt(Local::now().offset().local_minus_utc()).unwrap();
453 t.and_local_timezone(tz).unwrap().timestamp()
454 }
455}
456#[derive(Debug)]
457pub enum TradeType {
458 JSAPI,
460 MICROPAY,
461 None,
462}
463impl TradeType {
464 fn from(code: &str) -> TradeType {
465 match code {
466 "JSAPI" => TradeType::JSAPI,
467 "MICROPAY" => TradeType::MICROPAY,
468 _ => TradeType::None
469 }
470 }
471 fn to_string(&self) -> &'static str {
472 match self {
473 TradeType::JSAPI => "JSAPI",
474 TradeType::MICROPAY => "MICROPAY",
475 TradeType::None => ""
476 }
477 }
478}
479#[derive(Debug)]
480pub enum TradeState {
481 SUCCESS,
483 REFUND,
485 NOTPAY,
487 CLOSED,
489 REVOKED,
491 USERPAYING,
493 PAYERROR,
495 None,
496}
497impl TradeState {
498 fn from(code: &str) -> TradeState {
499 match code {
500 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
501 "REFUND" => TradeState::REFUND,
502 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
503 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
504 "REVOKED" => TradeState::REVOKED,
505 "USERPAYING" => TradeState::USERPAYING,
506 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
507 _ => TradeState::None
508 }
509 }
510
511 fn to_string(&self) -> &'static str {
512 match self {
513 TradeState::SUCCESS => "已支付",
514 TradeState::REFUND => "已支付",
515 TradeState::NOTPAY => "待支付",
516 TradeState::CLOSED => "已关闭",
517 TradeState::REVOKED => "已关闭",
518 TradeState::USERPAYING => "待支付",
519 TradeState::PAYERROR => "支付失败",
520 TradeState::None => "待支付"
521 }
522 }
523}
524
525#[derive(Debug)]
526pub enum RefundStatus {
527 SUCCESS,
529 CLOSED,
531 PROCESSING,
533 ABNORMAL,
535 None,
536}
537impl RefundStatus {
538 fn from(code: &str) -> RefundStatus {
539 match code {
540 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
541 "CLOSED" => RefundStatus::CLOSED,
542 "PROCESSING" | "N" => RefundStatus::PROCESSING,
543 "ABNORMAL" => RefundStatus::ABNORMAL,
544 _ => RefundStatus::None
545 }
546 }
547 fn to_string(&self) -> &'static str {
548 match self {
549 RefundStatus::SUCCESS => "已退款",
550 RefundStatus::None => "退款中",
551 RefundStatus::CLOSED => "已关闭",
552 RefundStatus::PROCESSING => "退款中",
553 RefundStatus::ABNORMAL => "退款异常"
554 }
555 }
556}
557#[derive(Debug)]
559pub struct RefundNotify {
560 out_trade_no: String,
562 refund_no: String,
563 sp_mchid: String,
565 sub_mchid: String,
567 transaction_id: String,
569 refund_id: String,
571 success_time: i64,
573 total: f64,
575 refund: f64,
577 payer_total: f64,
579 payer_refund: f64,
581 status: RefundStatus,
583}
584
585impl RefundNotify {
586 pub fn json(&self) -> JsonValue {
587 object! {
588 "out_trade_no" => self.out_trade_no.clone(),
589 "sp_mchid" => self.sp_mchid.clone(),
590 "sub_mchid" => self.sub_mchid.clone(),
591 "transaction_id" => self.transaction_id.clone(),
592 "success_time" => self.success_time,
593 "total" => self.total,
594 "refund" => self.refund,
595 "payer_total" => self.payer_total,
596 "payer_refund" => self.payer_refund,
597 "status" => self.status.to_string(),
598 "refund_no"=>self.refund_no.clone(),
599 "refund_id"=>self.refund_id.clone()
600 }
601 }
602}
603
604pub enum Types {
605 Jsapi,
607 Native,
609 H5,
611 MiniJsapi,
613 App,
615 Micropay,
617}
618impl Types {
619 pub fn str(self) -> &'static str {
620 match self {
621 Types::Jsapi => "jsapi",
622 Types::Native => "native",
623 Types::H5 => "h5",
624 Types::MiniJsapi => "minijsapi",
625 Types::App => "app",
626 Types::Micropay => "micropay"
627 }
628 }
629 pub fn from(name: &str) -> Self {
630 match name {
631 "jsapi" => Types::Jsapi,
632 "native" => Types::Native,
633 "h5" => Types::H5,
634 "minijsapi" => Types::MiniJsapi,
635 "app" => Types::App,
636 "micropay" => Types::Micropay,
637 _ => Types::Jsapi
638 }
639 }
640}