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