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