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 secret: data["secret"].to_string(),
34 apikey: data["apikey"].to_string(),
35 apiv2: data["apiv2"].to_string(),
36 notify_url: data["notify_url"].to_string(),
37 })
38 }
39 "alipay" => Pay::Alipay(AliPay {
40 appid: data["appid"].to_string(),
41 sp_mchid: data["sp_mchid"].to_string(),
42 app_private: data["app_private"].to_string(),
43 app_auth_token: data["app_auth_token"].as_str().unwrap_or("").to_string(),
44 content_encryp: data["content_encryp"].to_string(),
45 alipay_public_key: data["alipay_public_key"].to_string(),
46 notify_url: data["notify_url"].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"].to_string(),
61 secret: data["secret"].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"].to_string(),
67 smertypeid: data["smertypeid"].to_string(),
68 smertype: data["smertype"].to_string(),
69 }),
70 _ => Pay::None
71 }
72 }
73}
74impl PayMode for Pay {
75 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String> {
76 match self {
77 Self::Wechat(e) => e.get_sub_mchid(sub_mchid),
78 Self::Alipay(e) => e.get_sub_mchid(sub_mchid),
79 Pay::None => Err("No login data".to_owned()),
80 &mut Pay::Ccbc(_) => todo!(),
81 &mut Pay::Yrcc(_) => todo!()
82 }
83 }
84
85 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String> {
86 match self {
87 Self::Wechat(e) => e.notify(data),
88 Self::Alipay(e) => e.notify(data),
89 Pay::None => Err("No login data".to_owned()),
90 &mut Pay::Ccbc(_) => todo!(),
91 &mut Pay::Yrcc(_) => todo!()
92 }
93 }
94
95 fn config(&mut self) -> JsonValue {
96 match self {
97 Self::Wechat(e) => object! {
98 sp_mchid:e.sp_mchid.clone(),
99 appid:e.appid.clone(),
100 },
101 Self::Alipay(e) => object! {
102 appid:e.appid.clone(),
103 sp_mchid:e.sp_mchid.clone()
104 },
105 Pay::None => object! {
106 appid:"",
107 sp_mchid:""
108 },
109 &mut Pay::Ccbc(_) => todo!(),
110 &mut Pay::Yrcc(_) => todo!()
111 }
112 }
113
114 fn login(&mut self, code: &str) -> Result<JsonValue, String> {
115 match self {
116 Self::Wechat(e) => e.login(code),
117 Self::Alipay(e) => e.login(code),
118 Pay::None => Err("No login data".to_owned()),
119 &mut Pay::Ccbc(_) => todo!(),
120 &mut Pay::Yrcc(_) => todo!()
121 }
122 }
123
124 fn auth(&mut self, code: &str) -> Result<JsonValue, String> {
125 match self {
126 Self::Wechat(e) => e.auth(code),
127 Self::Alipay(e) => e.auth(code),
128 Pay::None => Err("No login data".to_owned()),
129 &mut Pay::Ccbc(_) => todo!(),
130 &mut Pay::Yrcc(_) => todo!()
131 }
132 }
133
134 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> {
135 match self {
136 Self::Wechat(e) => e.pay(channel,
137 types,
138 sub_mchid,
139 out_trade_no,
140 description,
141 total_fee,
142 sp_openid,
143 ),
144 Self::Alipay(e) => e.pay(channel,
145 types,
146 sub_mchid,
147 out_trade_no,
148 description,
149 total_fee,
150 sp_openid,
151 ),
152 Pay::None => Err("No login data".to_owned()),
153 Pay::Ccbc(e) => e.pay(
154 channel,
155 types,
156 sub_mchid,
157 out_trade_no,
158 description,
159 total_fee,
160 sp_openid,
161 ),
162 Pay::Yrcc(e) => e.pay(
163 channel,
164 types,
165 sub_mchid,
166 out_trade_no,
167 description,
168 total_fee,
169 sp_openid,
170 )
171 }
172 }
173
174 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> {
175 match self {
176 Self::Wechat(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 Self::Alipay(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::Ccbc(e) => e.micropay(
197 channel,
198 auth_code,
199 sub_mchid,
200 out_trade_no,
201 description,
202 total_fee,
203 org_openid,
204 ip,
205 ),
206 Pay::None => Err("No login data".to_owned()),
207 &mut Pay::Yrcc(_) => todo!()
208 }
209 }
210
211 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
212 match self {
213 Self::Wechat(e) => e.close(
214 out_trade_no, sub_mchid,
215 ),
216 Self::Alipay(e) => e.close(
217 out_trade_no, sub_mchid,
218 ),
219 Pay::None => Err("No login data".to_owned()),
220 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
221 }
222 }
223
224 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
225 match self {
226 Self::Wechat(e) => e.pay_query(
227 out_trade_no,
228 sub_mchid,
229 ),
230 Self::Alipay(e) => e.pay_query(
231 out_trade_no,
232 sub_mchid,
233 ),
234 Pay::None => Err("No login data".to_owned()),
235 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
236 }
237 }
238 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
239 match self {
240 Self::Wechat(e) => e.pay_micropay_query(
241 out_trade_no,
242 sub_mchid,
243 ),
244 Self::Alipay(e) => e.pay_query(
245 out_trade_no,
246 sub_mchid,
247 ),
248 Pay::None => Err("No login data".to_owned()),
249 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
250 }
251 }
252
253 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
254 match self {
255 Self::Wechat(e) => e.pay_notify(
256 nonce,
257 ciphertext,
258 associated_data,
259 ),
260 Self::Alipay(e) => e.pay_notify(
261 nonce,
262 ciphertext,
263 associated_data,
264 ),
265 Pay::None => Err("No login data".to_owned()),
266 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
267 }
268 }
269
270 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> {
271 match self {
272 Self::Wechat(e) => e.refund(
273 sub_mchid,
274 out_trade_no,
275 transaction_id,
276 out_refund_no,
277 amount,
278 total,
279 currency,
280 ),
281 Self::Alipay(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 Pay::None => Err("No login data".to_owned()),
291 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
292 }
293 }
294
295 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> {
296 match self {
297 Self::Wechat(e) => e.micropay_refund(
298 sub_mchid,
299 out_trade_no,
300 transaction_id,
301 out_refund_no,
302 amount,
303 total,
304 currency,
305 refund_text,
306 ),
307 Self::Alipay(e) => e.refund(
308 sub_mchid,
309 out_trade_no,
310 transaction_id,
311 out_refund_no,
312 amount,
313 total,
314 currency,
315 ),
316 Pay::None => Err("No login data".to_owned()),
317 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
318 }
319 }
320
321 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
322 match self {
323 Self::Wechat(e) => e.refund_notify(
324 nonce,
325 ciphertext,
326 associated_data,
327 ),
328 Self::Alipay(e) => e.refund_notify(
329 nonce,
330 ciphertext,
331 associated_data,
332 ),
333 Pay::None => Err("No login data".to_owned()),
334 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
335 }
336 }
337
338 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
339 match self {
340 Self::Wechat(e) => e.refund_query(
341 trade_no,
342 out_refund_no,
343 sub_mchid,
344 ),
345 Self::Alipay(e) => e.refund_query(
346 trade_no,
347 out_refund_no,
348 sub_mchid,
349 ),
350 Pay::None => Err("No login data".to_owned()),
351 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
352 }
353 }
354
355 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> {
356 match self {
357 Self::Wechat(e) => e.incoming(
358 business_code,
359 contact_info,
360 subject_info,
361 business_info,
362 settlement_info,
363 bank_account_info,
364 ),
365 Self::Alipay(e) => e.incoming(
366 business_code,
367 contact_info,
368 subject_info,
369 business_info,
370 settlement_info,
371 bank_account_info,
372 ),
373 Pay::None => Err("No incoming data".to_owned()),
374 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
375 }
376 }
377}
378
379pub trait PayMode {
380 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
382 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String>;
383 fn config(&mut self) -> JsonValue;
384 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
385 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
386 #[allow(clippy::too_many_arguments)]
387 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>;
396
397 #[allow(clippy::too_many_arguments)]
398 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>;
399 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
401 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
405 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
406 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
408
409 #[allow(clippy::too_many_arguments)]
410 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>;
412 #[allow(clippy::too_many_arguments)]
413 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>;
414 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
416 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
418 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>;
420}
421
422#[derive(Debug)]
424pub struct PayNotify {
425 trade_type: TradeType,
427 out_trade_no: String,
429 sp_mchid: String,
431 sub_mchid: String,
433 sp_appid: String,
435 transaction_id: String,
437 success_time: i64,
439 sp_openid: String,
441 sub_openid: String,
443 total: f64,
445 payer_total: f64,
447 currency: String,
449 payer_currency: String,
451 trade_state: TradeState,
453}
454impl PayNotify {
455 pub fn json(&self) -> JsonValue {
456 object! {
457 "trade_type" => self.trade_type.to_string(),
458 "out_trade_no" => self.out_trade_no.to_string(),
459 "sp_mchid" => self.sp_mchid.to_string(),
460 "sub_mchid" => self.sub_mchid.to_string(),
461 "sp_appid" => self.sp_appid.to_string(),
462 "transaction_id" => self.transaction_id.to_string(),
463 "success_time" => self.success_time,
464 "sp_openid" => self.sp_openid.to_string(),
465 "sub_openid" => self.sub_openid.to_string(),
466 "total" => self.total,
467 "payer_total" => self.payer_total,
468 "currency" => self.currency.clone(),
469 "payer_currency" => self.payer_currency.clone(),
470 "trade_state" => self.trade_state.to_string(),
471 }
472 }
473 pub fn success_time(datetime: &str) -> i64 {
474 if datetime.is_empty() {
475 return 0;
476 }
477 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
478 datetime.timestamp()
479 }
480 pub fn alipay_time(datetime: &str) -> i64 {
481 if datetime.is_empty() {
482 return 0;
483 }
484 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
485 t.and_utc().timestamp()
486 }
487 pub fn datetime_to_timestamp(datetime: &str, fmt: &str) -> i64 {
488 let t = NaiveDateTime::parse_from_str(datetime, fmt).unwrap();
489 let tz = FixedOffset::east_opt(Local::now().offset().local_minus_utc()).unwrap();
490 t.and_local_timezone(tz).unwrap().timestamp()
491 }
492}
493#[derive(Debug)]
494pub enum TradeType {
495 JSAPI,
497 MICROPAY,
498 None,
499}
500impl TradeType {
501 fn from(code: &str) -> TradeType {
502 match code {
503 "JSAPI" => TradeType::JSAPI,
504 "MICROPAY" => TradeType::MICROPAY,
505 _ => TradeType::None
506 }
507 }
508 fn to_string(&self) -> &'static str {
509 match self {
510 TradeType::JSAPI => "JSAPI",
511 TradeType::MICROPAY => "MICROPAY",
512 TradeType::None => ""
513 }
514 }
515}
516#[derive(Debug)]
517pub enum TradeState {
518 SUCCESS,
520 REFUND,
522 NOTPAY,
524 CLOSED,
526 REVOKED,
528 USERPAYING,
530 PAYERROR,
532 None,
533}
534impl TradeState {
535 fn from(code: &str) -> TradeState {
536 match code {
537 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
538 "REFUND" => TradeState::REFUND,
539 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
540 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
541 "REVOKED" => TradeState::REVOKED,
542 "USERPAYING" => TradeState::USERPAYING,
543 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
544 _ => TradeState::None
545 }
546 }
547
548 fn to_string(&self) -> &'static str {
549 match self {
550 TradeState::SUCCESS => "已支付",
551 TradeState::REFUND => "已支付",
552 TradeState::NOTPAY => "待支付",
553 TradeState::CLOSED => "已关闭",
554 TradeState::REVOKED => "已关闭",
555 TradeState::USERPAYING => "待支付",
556 TradeState::PAYERROR => "支付失败",
557 TradeState::None => "待支付"
558 }
559 }
560}
561
562#[derive(Debug)]
563pub enum RefundStatus {
564 SUCCESS,
566 CLOSED,
568 PROCESSING,
570 ABNORMAL,
572 None,
573}
574impl RefundStatus {
575 fn from(code: &str) -> RefundStatus {
576 match code {
577 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
578 "CLOSED" => RefundStatus::CLOSED,
579 "PROCESSING" | "N" => RefundStatus::PROCESSING,
580 "ABNORMAL" => RefundStatus::ABNORMAL,
581 _ => RefundStatus::None
582 }
583 }
584 fn to_string(&self) -> &'static str {
585 match self {
586 RefundStatus::SUCCESS => "已退款",
587 RefundStatus::None => "退款中",
588 RefundStatus::CLOSED => "已关闭",
589 RefundStatus::PROCESSING => "退款中",
590 RefundStatus::ABNORMAL => "退款异常"
591 }
592 }
593}
594#[derive(Debug)]
596pub struct RefundNotify {
597 out_trade_no: String,
599 refund_no: String,
600 sp_mchid: String,
602 sub_mchid: String,
604 transaction_id: String,
606 refund_id: String,
608 success_time: i64,
610 total: f64,
612 refund: f64,
614 payer_total: f64,
616 payer_refund: f64,
618 status: RefundStatus,
620}
621
622impl RefundNotify {
623 pub fn json(&self) -> JsonValue {
624 object! {
625 "out_trade_no" => self.out_trade_no.clone(),
626 "sp_mchid" => self.sp_mchid.clone(),
627 "sub_mchid" => self.sub_mchid.clone(),
628 "transaction_id" => self.transaction_id.clone(),
629 "success_time" => self.success_time,
630 "total" => self.total,
631 "refund" => self.refund,
632 "payer_total" => self.payer_total,
633 "payer_refund" => self.payer_refund,
634 "status" => self.status.to_string(),
635 "refund_no"=>self.refund_no.clone(),
636 "refund_id"=>self.refund_id.clone()
637 }
638 }
639}
640
641#[derive(Debug)]
642pub enum Types {
643 Jsapi,
645 Native,
647 H5,
649 MiniJsapi,
651 App,
653 Micropay,
655}
656impl Types {
657 pub fn str(self) -> &'static str {
658 match self {
659 Types::Jsapi => "jsapi",
660 Types::Native => "native",
661 Types::H5 => "h5",
662 Types::MiniJsapi => "minijsapi",
663 Types::App => "app",
664 Types::Micropay => "micropay"
665 }
666 }
667 pub fn from(name: &str) -> Self {
668 match name {
669 "jsapi" => Types::Jsapi,
670 "native" => Types::Native,
671 "h5" => Types::H5,
672 "minijsapi" => Types::MiniJsapi,
673 "app" => Types::App,
674 "micropay" => Types::Micropay,
675 _ => Types::Jsapi
676 }
677 }
678}