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