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"].to_string(),
60 secret: data["secret"].to_string(),
61 mchid: data["mchid"].to_string(),
62 sp_mchid: data["sp_mchid"].to_string(),
63 notify_url: data["notify_url"].as_str().unwrap_or("").to_string(),
64 posid: data["posid"].to_string(),
65 branchid: data["branchid"].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"].to_string(),
70 client_ip: data["client_ip"].to_string(),
71 }),
72 _ => Pay::None
73 }
74 }
75}
76impl PayMode for Pay {
77 fn check(&mut self) -> Result<bool, String> {
78 match self {
79 Self::Wechat(e) => e.check(),
80 Self::Alipay(_) => todo!(),
81 Self::Ccbc(_) => todo!(),
82 Self::Yrcc(_) => todo!(),
83 Self::None => Err("No login data".to_owned()),
84 }
85 }
86
87 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String> {
88 match self {
89 Self::Wechat(e) => e.get_sub_mchid(sub_mchid),
90 Self::Alipay(e) => e.get_sub_mchid(sub_mchid),
91 Pay::None => Err("No login data".to_owned()),
92 &mut Pay::Ccbc(_) => todo!(),
93 &mut Pay::Yrcc(_) => todo!()
94 }
95 }
96
97 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String> {
98 match self {
99 Self::Wechat(e) => e.notify(data),
100 Self::Alipay(e) => e.notify(data),
101 Pay::None => Err("No login data".to_owned()),
102 &mut Pay::Ccbc(_) => todo!(),
103 &mut Pay::Yrcc(_) => todo!()
104 }
105 }
106
107 fn config(&mut self) -> JsonValue {
108 match self {
109 Self::Wechat(e) => object! {
110 sp_mchid:e.sp_mchid.clone(),
111 appid:e.appid.clone(),
112 },
113 Self::Alipay(e) => object! {
114 appid:e.appid.clone(),
115 sp_mchid:e.sp_mchid.clone()
116 },
117 Pay::None => object! {
118 appid:"",
119 sp_mchid:""
120 },
121 Pay::Ccbc(e) => object! {
122 appid:e.appid.clone(),
123 sp_mchid:e.sp_mchid.clone()
124 },
125 Pay::Yrcc(e) => object! {
126 appid:e.appid.clone(),
127 sp_mchid:e.sp_mchid.clone()
128 },
129 }
130 }
131
132 fn login(&mut self, code: &str) -> Result<JsonValue, String> {
133 match self {
134 Self::Wechat(_) => todo!(),
135 Self::Alipay(e) => e.login(code),
136 Pay::None => Err("No login data".to_owned()),
137 &mut Pay::Ccbc(_) => todo!(),
138 &mut Pay::Yrcc(_) => todo!()
139 }
140 }
141
142 fn auth(&mut self, code: &str) -> Result<JsonValue, String> {
143 match self {
144 Self::Wechat(e) => e.auth(code),
145 Self::Alipay(e) => e.auth(code),
146 Pay::None => Err("No login data".to_owned()),
147 &mut Pay::Ccbc(_) => todo!(),
148 &mut Pay::Yrcc(_) => todo!()
149 }
150 }
151
152 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> {
153 match self {
154 Self::Wechat(e) => e.pay(channel,
155 types,
156 sub_mchid,
157 out_trade_no,
158 description,
159 total_fee,
160 sp_openid,
161 ),
162 Self::Alipay(e) => e.pay(channel,
163 types,
164 sub_mchid,
165 out_trade_no,
166 description,
167 total_fee,
168 sp_openid,
169 ),
170 Pay::None => Err("No login data".to_owned()),
171 Pay::Ccbc(e) => e.pay(
172 channel,
173 types,
174 sub_mchid,
175 out_trade_no,
176 description,
177 total_fee,
178 sp_openid,
179 ),
180 Pay::Yrcc(e) => e.pay(
181 channel,
182 types,
183 sub_mchid,
184 out_trade_no,
185 description,
186 total_fee,
187 sp_openid,
188 )
189 }
190 }
191
192 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> {
193 match self {
194 Self::Wechat(e) => e.micropay(
195 channel,
196 auth_code,
197 sub_mchid,
198 out_trade_no,
199 description,
200 total_fee,
201 org_openid,
202 ip,
203 ),
204 Self::Alipay(e) => e.micropay(
205 channel,
206 auth_code,
207 sub_mchid,
208 out_trade_no,
209 description,
210 total_fee,
211 org_openid,
212 ip,
213 ),
214 Pay::Ccbc(e) => e.micropay(
215 channel,
216 auth_code,
217 sub_mchid,
218 out_trade_no,
219 description,
220 total_fee,
221 org_openid,
222 ip,
223 ),
224 Pay::None => Err("No login data".to_owned()),
225 &mut Pay::Yrcc(_) => todo!()
226 }
227 }
228
229 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
230 match self {
231 Self::Wechat(e) => e.close(
232 out_trade_no, sub_mchid,
233 ),
234 Self::Alipay(e) => e.close(
235 out_trade_no, sub_mchid,
236 ),
237 Pay::Ccbc(e) => e.close(
238 out_trade_no, sub_mchid,
239 ),
240 Pay::None => Err("No login data".to_owned()),
241 &mut Pay::Yrcc(_) => todo!()
242 }
243 }
244
245 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
246 match self {
247 Self::Wechat(e) => e.pay_query(
248 out_trade_no,
249 sub_mchid,
250 ),
251 Self::Alipay(e) => e.pay_query(
252 out_trade_no,
253 sub_mchid,
254 ),
255 Pay::None => Err("No login data".to_owned()),
256 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
257 }
258 }
259 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
260 match self {
261 Self::Wechat(e) => e.pay_micropay_query(
262 out_trade_no,
263 sub_mchid,
264 ),
265 Self::Alipay(e) => e.pay_query(
266 out_trade_no,
267 sub_mchid,
268 ),
269 Pay::None => Err("No login data".to_owned()),
270 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
271 }
272 }
273
274 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
275 match self {
276 Self::Wechat(e) => e.pay_notify(
277 nonce,
278 ciphertext,
279 associated_data,
280 ),
281 Self::Alipay(e) => e.pay_notify(
282 nonce,
283 ciphertext,
284 associated_data,
285 ),
286 Pay::None => Err("No login data".to_owned()),
287 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
288 }
289 }
290
291 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> {
292 match self {
293 Self::Wechat(e) => e.refund(
294 sub_mchid,
295 out_trade_no,
296 transaction_id,
297 out_refund_no,
298 amount,
299 total,
300 currency,
301 ),
302 Self::Alipay(e) => e.refund(
303 sub_mchid,
304 out_trade_no,
305 transaction_id,
306 out_refund_no,
307 amount,
308 total,
309 currency,
310 ),
311 Pay::None => Err("No login data".to_owned()),
312 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
313 }
314 }
315
316 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> {
317 match self {
318 Self::Wechat(e) => e.micropay_refund(
319 sub_mchid,
320 out_trade_no,
321 transaction_id,
322 out_refund_no,
323 amount,
324 total,
325 currency,
326 refund_text,
327 ),
328 Self::Alipay(e) => e.refund(
329 sub_mchid,
330 out_trade_no,
331 transaction_id,
332 out_refund_no,
333 amount,
334 total,
335 currency,
336 ),
337 Pay::None => Err("No login data".to_owned()),
338 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
339 }
340 }
341
342 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
343 match self {
344 Self::Wechat(e) => e.refund_notify(
345 nonce,
346 ciphertext,
347 associated_data,
348 ),
349 Self::Alipay(e) => e.refund_notify(
350 nonce,
351 ciphertext,
352 associated_data,
353 ),
354 Pay::None => Err("No login data".to_owned()),
355 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
356 }
357 }
358
359 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
360 match self {
361 Self::Wechat(e) => e.refund_query(
362 trade_no,
363 out_refund_no,
364 sub_mchid,
365 ),
366 Self::Alipay(e) => e.refund_query(
367 trade_no,
368 out_refund_no,
369 sub_mchid,
370 ),
371 Pay::None => Err("No login data".to_owned()),
372 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
373 }
374 }
375
376 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> {
377 match self {
378 Self::Wechat(e) => e.incoming(
379 business_code,
380 contact_info,
381 subject_info,
382 business_info,
383 settlement_info,
384 bank_account_info,
385 ),
386 Self::Alipay(e) => e.incoming(
387 business_code,
388 contact_info,
389 subject_info,
390 business_info,
391 settlement_info,
392 bank_account_info,
393 ),
394 Pay::None => Err("No incoming data".to_owned()),
395 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
396 }
397 }
398}
399
400pub trait PayMode {
401 fn check(&mut self) -> Result<bool, String>;
403 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
405 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String>;
406 fn config(&mut self) -> JsonValue;
407 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
408 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
409 #[allow(clippy::too_many_arguments)]
410 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>;
419
420 #[allow(clippy::too_many_arguments)]
421 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>;
422 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
424 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
428 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
429 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
431
432 #[allow(clippy::too_many_arguments)]
433 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>;
435 #[allow(clippy::too_many_arguments)]
436 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>;
437 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
439 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
441 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>;
443}
444
445#[derive(Debug)]
447pub struct PayNotify {
448 trade_type: TradeType,
450 out_trade_no: String,
452 sp_mchid: String,
454 sub_mchid: String,
456 sp_appid: String,
458 transaction_id: String,
460 success_time: i64,
462 sp_openid: String,
464 sub_openid: String,
466 total: f64,
468 payer_total: f64,
470 currency: String,
472 payer_currency: String,
474 trade_state: TradeState,
476}
477impl PayNotify {
478 pub fn json(&self) -> JsonValue {
479 object! {
480 "trade_type" => self.trade_type.to_string(),
481 "out_trade_no" => self.out_trade_no.to_string(),
482 "sp_mchid" => self.sp_mchid.to_string(),
483 "sub_mchid" => self.sub_mchid.to_string(),
484 "sp_appid" => self.sp_appid.to_string(),
485 "transaction_id" => self.transaction_id.to_string(),
486 "success_time" => self.success_time,
487 "sp_openid" => self.sp_openid.to_string(),
488 "sub_openid" => self.sub_openid.to_string(),
489 "total" => self.total,
490 "payer_total" => self.payer_total,
491 "currency" => self.currency.clone(),
492 "payer_currency" => self.payer_currency.clone(),
493 "trade_state" => self.trade_state.to_string(),
494 }
495 }
496 pub fn success_time(datetime: &str) -> i64 {
497 if datetime.is_empty() {
498 return 0;
499 }
500 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
501 datetime.timestamp()
502 }
503 pub fn alipay_time(datetime: &str) -> i64 {
504 if datetime.is_empty() {
505 return 0;
506 }
507 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
508 t.and_utc().timestamp()
509 }
510 pub fn datetime_to_timestamp(datetime: &str, fmt: &str) -> i64 {
511 let t = NaiveDateTime::parse_from_str(datetime, fmt).unwrap();
512 let tz = FixedOffset::east_opt(Local::now().offset().local_minus_utc()).unwrap();
513 t.and_local_timezone(tz).unwrap().timestamp()
514 }
515}
516#[derive(Debug)]
517pub enum TradeType {
518 JSAPI,
520 MICROPAY,
521 None,
522}
523impl TradeType {
524 fn from(code: &str) -> TradeType {
525 match code {
526 "JSAPI" => TradeType::JSAPI,
527 "MICROPAY" => TradeType::MICROPAY,
528 _ => TradeType::None
529 }
530 }
531 fn to_string(&self) -> &'static str {
532 match self {
533 TradeType::JSAPI => "JSAPI",
534 TradeType::MICROPAY => "MICROPAY",
535 TradeType::None => ""
536 }
537 }
538}
539#[derive(Debug)]
540pub enum TradeState {
541 SUCCESS,
543 REFUND,
545 NOTPAY,
547 CLOSED,
549 REVOKED,
551 USERPAYING,
553 PAYERROR,
555 None,
556}
557impl TradeState {
558 fn from(code: &str) -> TradeState {
559 match code {
560 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
561 "REFUND" => TradeState::REFUND,
562 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
563 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
564 "REVOKED" => TradeState::REVOKED,
565 "USERPAYING" => TradeState::USERPAYING,
566 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
567 _ => TradeState::None
568 }
569 }
570
571 fn to_string(&self) -> &'static str {
572 match self {
573 TradeState::SUCCESS => "已支付",
574 TradeState::REFUND => "已支付",
575 TradeState::NOTPAY => "待支付",
576 TradeState::CLOSED => "已关闭",
577 TradeState::REVOKED => "已关闭",
578 TradeState::USERPAYING => "待支付",
579 TradeState::PAYERROR => "支付失败",
580 TradeState::None => "待支付"
581 }
582 }
583}
584
585#[derive(Debug)]
586pub enum RefundStatus {
587 SUCCESS,
589 CLOSED,
591 PROCESSING,
593 ABNORMAL,
595 None,
596}
597impl RefundStatus {
598 fn from(code: &str) -> RefundStatus {
599 match code {
600 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
601 "CLOSED" => RefundStatus::CLOSED,
602 "PROCESSING" | "N" => RefundStatus::PROCESSING,
603 "ABNORMAL" => RefundStatus::ABNORMAL,
604 _ => RefundStatus::None
605 }
606 }
607 fn to_string(&self) -> &'static str {
608 match self {
609 RefundStatus::SUCCESS => "已退款",
610 RefundStatus::None => "退款中",
611 RefundStatus::CLOSED => "已关闭",
612 RefundStatus::PROCESSING => "退款中",
613 RefundStatus::ABNORMAL => "退款异常"
614 }
615 }
616}
617#[derive(Debug)]
619pub struct RefundNotify {
620 out_trade_no: String,
622 refund_no: String,
623 sp_mchid: String,
625 sub_mchid: String,
627 transaction_id: String,
629 refund_id: String,
631 success_time: i64,
633 total: f64,
635 refund: f64,
637 payer_total: f64,
639 payer_refund: f64,
641 status: RefundStatus,
643}
644
645impl RefundNotify {
646 pub fn json(&self) -> JsonValue {
647 object! {
648 "out_trade_no" => self.out_trade_no.clone(),
649 "sp_mchid" => self.sp_mchid.clone(),
650 "sub_mchid" => self.sub_mchid.clone(),
651 "transaction_id" => self.transaction_id.clone(),
652 "success_time" => self.success_time,
653 "total" => self.total,
654 "refund" => self.refund,
655 "payer_total" => self.payer_total,
656 "payer_refund" => self.payer_refund,
657 "status" => self.status.to_string(),
658 "refund_no"=>self.refund_no.clone(),
659 "refund_id"=>self.refund_id.clone()
660 }
661 }
662}
663
664#[derive(Debug)]
665pub enum Types {
666 Jsapi,
668 Native,
670 H5,
672 MiniJsapi,
674 App,
676 Micropay,
678}
679impl Types {
680 pub fn str(self) -> &'static str {
681 match self {
682 Types::Jsapi => "jsapi",
683 Types::Native => "native",
684 Types::H5 => "h5",
685 Types::MiniJsapi => "minijsapi",
686 Types::App => "app",
687 Types::Micropay => "micropay"
688 }
689 }
690 pub fn from(name: &str) -> Self {
691 match name {
692 "jsapi" => Types::Jsapi,
693 "native" => Types::Native,
694 "h5" => Types::H5,
695 "minijsapi" => Types::MiniJsapi,
696 "app" => Types::App,
697 "micropay" => Types::Micropay,
698 _ => Types::Jsapi
699 }
700 }
701}