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