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