1use chrono::{DateTime, 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
318pub trait PayMode {
319 fn jsapi_ticket(&mut self, url: &str) -> Result<JsonValue, String>;
321 fn get_openid(&mut self, code: &str) -> Result<JsonValue, String>;
322 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
324 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String>;
325 fn config(&mut self) -> JsonValue;
326 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
327 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
328
329 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String>;
337
338 #[allow(clippy::too_many_arguments)]
339 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>;
340 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
342 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
346 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
347 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
349
350 #[allow(clippy::too_many_arguments)]
351 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>;
353 #[allow(clippy::too_many_arguments)]
354 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>;
355 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
357 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
359}
360
361#[derive(Debug)]
363pub struct PayNotify {
364 trade_type: TradeType,
366 out_trade_no: String,
368 sp_mchid: String,
370 sub_mchid: String,
372 sp_appid: String,
374 transaction_id: String,
376 success_time: i64,
378 sp_openid: String,
380 sub_openid: String,
382 total: f64,
384 payer_total: f64,
386 currency: String,
388 payer_currency: String,
390 trade_state: TradeState,
392}
393impl PayNotify {
394 pub fn json(&self) -> JsonValue {
395 object! {
396 "trade_type" => self.trade_type.to_string(),
397 "out_trade_no" => self.out_trade_no.to_string(),
398 "sp_mchid" => self.sp_mchid.to_string(),
399 "sub_mchid" => self.sub_mchid.to_string(),
400 "sp_appid" => self.sp_appid.to_string(),
401 "transaction_id" => self.transaction_id.to_string(),
402 "success_time" => self.success_time,
403 "sp_openid" => self.sp_openid.to_string(),
404 "sub_openid" => self.sub_openid.to_string(),
405 "total" => self.total,
406 "payer_total" => self.payer_total,
407 "currency" => self.currency.clone(),
408 "payer_currency" => self.payer_currency.clone(),
409 "trade_state" => self.trade_state.to_string(),
410 }
411 }
412 pub fn success_time(datetime: &str) -> i64 {
413 if datetime.is_empty() {
414 return 0;
415 }
416 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
417 datetime.timestamp()
418 }
419 pub fn alipay_time(datetime: &str) -> i64 {
420 if datetime.is_empty() {
421 return 0;
422 }
423 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
424 t.and_utc().timestamp()
425 }
426 pub fn micropay_time(datetime: &str) -> i64 {
427 if datetime.is_empty() {
428 return 0;
429 }
430 let t = NaiveDateTime::parse_from_str(datetime, "%Y%m%d%H%M%S").unwrap();
431 t.and_utc().timestamp()
432 }
433}
434#[derive(Debug)]
435pub enum TradeType {
436 JSAPI,
438 MICROPAY,
439 None,
440}
441impl TradeType {
442 fn from(code: &str) -> TradeType {
443 match code {
444 "JSAPI" => TradeType::JSAPI,
445 "MICROPAY" => TradeType::MICROPAY,
446 _ => TradeType::None
447 }
448 }
449 fn to_string(&self) -> &'static str {
450 match self {
451 TradeType::JSAPI => "JSAPI",
452 TradeType::MICROPAY => "MICROPAY",
453 TradeType::None => ""
454 }
455 }
456}
457#[derive(Debug)]
458pub enum TradeState {
459 SUCCESS,
461 REFUND,
463 NOTPAY,
465 CLOSED,
467 REVOKED,
469 USERPAYING,
471 PAYERROR,
473 None,
474}
475impl TradeState {
476 fn from(code: &str) -> TradeState {
477 match code {
478 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
479 "REFUND" => TradeState::REFUND,
480 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
481 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
482 "REVOKED" => TradeState::REVOKED,
483 "USERPAYING" => TradeState::USERPAYING,
484 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
485 _ => TradeState::None
486 }
487 }
488
489 fn to_string(&self) -> &'static str {
490 match self {
491 TradeState::SUCCESS => "已支付",
492 TradeState::REFUND => "已支付",
493 TradeState::NOTPAY => "待支付",
494 TradeState::CLOSED => "已关闭",
495 TradeState::REVOKED => "已关闭",
496 TradeState::USERPAYING => "待支付",
497 TradeState::PAYERROR => "支付失败",
498 TradeState::None => "待支付"
499 }
500 }
501}
502
503#[derive(Debug)]
504pub enum RefundStatus {
505 SUCCESS,
507 CLOSED,
509 PROCESSING,
511 ABNORMAL,
513 None,
514}
515impl RefundStatus {
516 fn from(code: &str) -> RefundStatus {
517 match code {
518 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
519 "CLOSED" => RefundStatus::CLOSED,
520 "PROCESSING" | "N" => RefundStatus::PROCESSING,
521 "ABNORMAL" => RefundStatus::ABNORMAL,
522 _ => RefundStatus::None
523 }
524 }
525 fn to_string(&self) -> &'static str {
526 match self {
527 RefundStatus::SUCCESS => "已退款",
528 RefundStatus::None => "退款中",
529 RefundStatus::CLOSED => "已关闭",
530 RefundStatus::PROCESSING => "退款中",
531 RefundStatus::ABNORMAL => "退款异常"
532 }
533 }
534}
535#[derive(Debug)]
537pub struct RefundNotify {
538 out_trade_no: String,
540 refund_no: String,
541 sp_mchid: String,
543 sub_mchid: String,
545 transaction_id: String,
547 refund_id: String,
549 success_time: i64,
551 total: f64,
553 refund: f64,
555 payer_total: f64,
557 payer_refund: f64,
559 status: RefundStatus,
561}
562
563impl RefundNotify {
564 pub fn json(&self) -> JsonValue {
565 object! {
566 "out_trade_no" => self.out_trade_no.clone(),
567 "sp_mchid" => self.sp_mchid.clone(),
568 "sub_mchid" => self.sub_mchid.clone(),
569 "transaction_id" => self.transaction_id.clone(),
570 "success_time" => self.success_time,
571 "total" => self.total,
572 "refund" => self.refund,
573 "payer_total" => self.payer_total,
574 "payer_refund" => self.payer_refund,
575 "status" => self.status.to_string(),
576 "refund_no"=>self.refund_no.clone(),
577 "refund_id"=>self.refund_id.clone()
578 }
579 }
580}
581
582pub enum Types {
583 Jsapi,
585 Native,
587 H5,
589 MiniJsapi,
591 App,
593 Micropay,
595}
596impl Types {
597 pub fn str(self) -> &'static str {
598 match self {
599 Types::Jsapi => "jsapi",
600 Types::Native => "native",
601 Types::H5 => "h5",
602 Types::MiniJsapi => "minijsapi",
603 Types::App => "app",
604 Types::Micropay => "micropay"
605 }
606 }
607 pub fn from(name: &str) -> Self {
608 match name {
609 "jsapi" => Types::Jsapi,
610 "native" => Types::Native,
611 "h5" => Types::H5,
612 "minijsapi" => Types::MiniJsapi,
613 "app" => Types::App,
614 "micropay" => Types::Micropay,
615 _ => Types::Jsapi
616 }
617 }
618}