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,channel:&str) -> Result<JsonValue, String> {
200 match self {
201 Self::Wechat(e) => e.close(
202 out_trade_no, sub_mchid,channel
203 ),
204 Self::Alipay(e) => e.close(
205 out_trade_no, sub_mchid,
206 channel
207 ),
208 Pay::Ccbc(e) => e.close(
209 out_trade_no, sub_mchid,channel
210 ),
211 Pay::None => Err("No login data".to_owned()),
212 &mut Pay::Yrcc(_) => todo!()
213 }
214 }
215
216 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
217 match self {
218 Self::Wechat(e) => e.pay_query(
219 out_trade_no,
220 sub_mchid,
221 ),
222 Self::Alipay(e) => e.pay_query(
223 out_trade_no,
224 sub_mchid,
225 ),
226 Self::Ccbc(e) => e.pay_query(
227 out_trade_no,
228 sub_mchid,
229 ),
230 Pay::None => Err("No login data".to_owned()),
231 &mut Pay::Yrcc(_) => todo!()
232 }
233 }
234 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str,channel:&str) -> Result<JsonValue, String> {
235 match self {
236 Self::Wechat(e) => e.pay_micropay_query(
237 out_trade_no,
238 sub_mchid,
239 channel
240 ),
241 Self::Alipay(e) => e.pay_query(
242 out_trade_no,
243 sub_mchid
244 ),
245 Pay::Ccbc(e) => e.pay_micropay_query(
246 out_trade_no,
247 sub_mchid,
248 channel
249 ),
250 Pay::None => Err("No login data".to_owned()),
251 &mut Pay::Yrcc(_) => todo!()
252 }
253 }
254
255 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
256 match self {
257 Self::Wechat(e) => e.pay_notify(
258 nonce,
259 ciphertext,
260 associated_data,
261 ),
262 Self::Alipay(e) => e.pay_notify(
263 nonce,
264 ciphertext,
265 associated_data,
266 ),
267 Pay::None => Err("No login data".to_owned()),
268 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
269 }
270 }
271
272 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> {
273 match self {
274 Self::Wechat(e) => e.refund(
275 sub_mchid,
276 out_trade_no,
277 transaction_id,
278 out_refund_no,
279 amount,
280 total,
281 currency,
282 ),
283 Self::Alipay(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::Ccbc(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::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 config(&mut self) -> JsonValue;
396 #[allow(clippy::too_many_arguments)]
397 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>;
406
407 #[allow(clippy::too_many_arguments)]
408 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>;
409 fn close(&mut self, out_trade_no: &str, sub_mchid: &str,channel:&str) -> Result<JsonValue, String>;
411 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
415 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str,channel:&str) -> Result<JsonValue, String>;
416 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
418
419 #[allow(clippy::too_many_arguments)]
420 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>;
422 #[allow(clippy::too_many_arguments)]
423 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>;
424 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
426 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
428 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>;
430}
431
432#[derive(Debug)]
434pub struct PayNotify {
435 trade_type: TradeType,
437 out_trade_no: String,
439 sp_mchid: String,
441 sub_mchid: String,
443 sp_appid: String,
445 transaction_id: String,
447 success_time: i64,
449 sp_openid: String,
451 sub_openid: String,
453 total: f64,
455 payer_total: f64,
457 currency: String,
459 payer_currency: String,
461 trade_state: TradeState,
463}
464impl PayNotify {
465 pub fn json(&self) -> JsonValue {
466 object! {
467 "trade_type" => self.trade_type.to_string(),
468 "out_trade_no" => self.out_trade_no.to_string(),
469 "sp_mchid" => self.sp_mchid.to_string(),
470 "sub_mchid" => self.sub_mchid.to_string(),
471 "sp_appid" => self.sp_appid.to_string(),
472 "transaction_id" => self.transaction_id.to_string(),
473 "success_time" => self.success_time,
474 "sp_openid" => self.sp_openid.to_string(),
475 "sub_openid" => self.sub_openid.to_string(),
476 "total" => self.total,
477 "payer_total" => self.payer_total,
478 "currency" => self.currency.clone(),
479 "payer_currency" => self.payer_currency.clone(),
480 "trade_state" => self.trade_state.to_string(),
481 }
482 }
483 pub fn success_time(datetime: &str) -> i64 {
484 if datetime.is_empty() {
485 return 0;
486 }
487 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
488 datetime.timestamp()
489 }
490 pub fn alipay_time(datetime: &str) -> i64 {
491 if datetime.is_empty() {
492 return 0;
493 }
494 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
495 t.and_utc().timestamp()
496 }
497 pub fn datetime_to_timestamp(datetime: &str, fmt: &str) -> i64 {
498 let t = NaiveDateTime::parse_from_str(datetime, fmt).unwrap();
499 let tz = FixedOffset::east_opt(Local::now().offset().local_minus_utc()).unwrap();
500 t.and_local_timezone(tz).unwrap().timestamp()
501 }
502}
503#[derive(Debug)]
504pub enum TradeType {
505 JSAPI,
507 MICROPAY,
508 None,
509}
510impl TradeType {
511 fn from(code: &str) -> TradeType {
512 match code {
513 "JSAPI" => TradeType::JSAPI,
514 "MICROPAY" => TradeType::MICROPAY,
515 _ => TradeType::None
516 }
517 }
518 fn to_string(&self) -> &'static str {
519 match self {
520 TradeType::JSAPI => "JSAPI",
521 TradeType::MICROPAY => "MICROPAY",
522 TradeType::None => ""
523 }
524 }
525}
526#[derive(Debug)]
527pub enum TradeState {
528 SUCCESS,
530 REFUND,
532 NOTPAY,
534 CLOSED,
536 REVOKED,
538 USERPAYING,
540 PAYERROR,
542 None,
543}
544impl TradeState {
545 fn from(code: &str) -> TradeState {
546 match code {
547 "SUCCESS" | "TRADE_SUCCESS" | "成功" => TradeState::SUCCESS,
548 "REFUND" | "已全额退款" | "已部分退款" => TradeState::REFUND,
549 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
550 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
551 "REVOKED" => TradeState::REVOKED,
552 "USERPAYING" | "待银行确认" => TradeState::USERPAYING,
553 "PAYERROR" | "TRADE_FINISHED" | "失败" => TradeState::PAYERROR,
554 _ => TradeState::None
555 }
556 }
557
558 fn to_string(&self) -> &'static str {
559 match self {
560 TradeState::SUCCESS => "已支付",
561 TradeState::REFUND => "已退款",
562 TradeState::NOTPAY => "待支付",
563 TradeState::CLOSED => "已关闭",
564 TradeState::REVOKED => "已关闭",
565 TradeState::USERPAYING => "待支付",
566 TradeState::PAYERROR => "支付失败",
567 TradeState::None => "待支付"
568 }
569 }
570}
571
572#[derive(Debug)]
573pub enum RefundStatus {
574 SUCCESS,
576 CLOSED,
578 PROCESSING,
580 ABNORMAL,
582 None,
583}
584impl RefundStatus {
585 fn from(code: &str) -> RefundStatus {
586 match code {
587 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
588 "CLOSED" => RefundStatus::CLOSED,
589 "PROCESSING" | "N" => RefundStatus::PROCESSING,
590 "ABNORMAL" => RefundStatus::ABNORMAL,
591 _ => RefundStatus::None
592 }
593 }
594 fn to_string(&self) -> &'static str {
595 match self {
596 RefundStatus::SUCCESS => "已退款",
597 RefundStatus::None => "退款中",
598 RefundStatus::CLOSED => "已关闭",
599 RefundStatus::PROCESSING => "退款中",
600 RefundStatus::ABNORMAL => "退款异常"
601 }
602 }
603}
604#[derive(Debug)]
606pub struct RefundNotify {
607 out_trade_no: String,
609 refund_no: String,
610 sp_mchid: String,
612 sub_mchid: String,
614 transaction_id: String,
616 refund_id: String,
618 success_time: i64,
620 total: f64,
622 refund: f64,
624 payer_total: f64,
626 payer_refund: f64,
628 status: RefundStatus,
630}
631
632impl RefundNotify {
633 pub fn json(&self) -> JsonValue {
634 object! {
635 "out_trade_no" => self.out_trade_no.clone(),
636 "sp_mchid" => self.sp_mchid.clone(),
637 "sub_mchid" => self.sub_mchid.clone(),
638 "transaction_id" => self.transaction_id.clone(),
639 "success_time" => self.success_time,
640 "total" => self.total,
641 "refund" => self.refund,
642 "payer_total" => self.payer_total,
643 "payer_refund" => self.payer_refund,
644 "status" => self.status.to_string(),
645 "refund_no"=>self.refund_no.clone(),
646 "refund_id"=>self.refund_id.clone()
647 }
648 }
649}
650
651#[derive(Debug)]
652pub enum Types {
653 Jsapi,
655 Native,
657 H5,
659 MiniJsapi,
661 App,
663 Micropay,
665}
666impl Types {
667 pub fn str(self) -> &'static str {
668 match self {
669 Types::Jsapi => "jsapi",
670 Types::Native => "native",
671 Types::H5 => "h5",
672 Types::MiniJsapi => "minijsapi",
673 Types::App => "app",
674 Types::Micropay => "micropay"
675 }
676 }
677 pub fn from(name: &str) -> Self {
678 match name {
679 "jsapi" => Types::Jsapi,
680 "native" => Types::Native,
681 "h5" => Types::H5,
682 "minijsapi" => Types::MiniJsapi,
683 "app" => Types::App,
684 "micropay" => Types::Micropay,
685 _ => Types::Jsapi
686 }
687 }
688}