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