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