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