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