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