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 appid_mini: data["appid_mini"].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"].as_str().unwrap_or("").to_string(),
61 branchid: data["branchid"].as_str().unwrap_or("").to_string(),
62 appid_subscribe: data["appid_subscribe"].as_str().unwrap_or("").to_string(),
63 sp_mchid: data["sp_mchid"].as_str().unwrap_or("").to_string(),
64 sp_user_id: data["sp_user_id"].as_str().unwrap_or("").to_string(),
65 sp_pass: data["sp_pass"].as_str().unwrap_or("").to_string(),
66 sp_posid: data["sp_posid"].as_str().unwrap_or("").to_string(),
67 notify_url: data["notify_url"].as_str().unwrap_or("").to_string(),
68 sub_posid: data["sub_posid"].as_str().unwrap_or("").to_string(),
69 public_key: data["public_key"].as_str().unwrap_or("").to_string(),
70 client_ip: data["client_ip"].as_str().unwrap_or("").to_string(),
71 retry: 0,
72 debug: data["debug"].as_bool().unwrap_or(false),
73 query_url: data["query_url"].as_str().unwrap_or("").to_string(),
74 }),
75 _ => Pay::None
76 }
77 }
78}
79impl PayMode for Pay {
80 fn check(&mut self) -> Result<bool, String> {
81 match self {
82 Self::Wechat(e) => e.check(),
83 Self::Alipay(e) => e.check(),
84 Self::Ccbc(_) => todo!(),
85 Self::Yrcc(_) => todo!(),
86 Self::None => Err("No login data".to_owned()),
87 }
88 }
89
90 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String> {
91 match self {
92 Self::Wechat(e) => e.get_sub_mchid(sub_mchid),
93 Self::Alipay(e) => e.get_sub_mchid(sub_mchid),
94 Pay::None => Err("No login data".to_owned()),
95 &mut Pay::Ccbc(_) => todo!(),
96 &mut Pay::Yrcc(_) => todo!()
97 }
98 }
99
100
101 fn config(&mut self) -> JsonValue {
102 match self {
103 Self::Wechat(e) => object! {
104 sp_mchid:e.sp_mchid.clone(),
105 appid:e.appid.clone(),
106 },
107 Self::Alipay(e) => object! {
108 appid:e.appid.clone(),
109 sp_mchid:e.sp_mchid.clone()
110 },
111 Pay::None => object! {
112 appid:"",
113 sp_mchid:""
114 },
115 Pay::Ccbc(e) => object! {
116 appid:e.appid.clone(),
117 sp_mchid:e.sp_mchid.clone()
118 },
119 Pay::Yrcc(e) => object! {
120 appid:e.appid.clone(),
121 sp_mchid:e.sp_mchid.clone()
122 },
123 }
124 }
125 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> {
126 match self {
127 Self::Wechat(e) => e.pay(channel,
128 types,
129 sub_mchid,
130 out_trade_no,
131 description,
132 total_fee,
133 sp_openid,
134 ),
135 Self::Alipay(e) => e.pay(channel,
136 types,
137 sub_mchid,
138 out_trade_no,
139 description,
140 total_fee,
141 sp_openid,
142 ),
143 Pay::None => Err("No login data".to_owned()),
144 Pay::Ccbc(e) => e.pay(
145 channel,
146 types,
147 sub_mchid,
148 out_trade_no,
149 description,
150 total_fee,
151 sp_openid,
152 ),
153 Pay::Yrcc(e) => e.pay(
154 channel,
155 types,
156 sub_mchid,
157 out_trade_no,
158 description,
159 total_fee,
160 sp_openid,
161 )
162 }
163 }
164
165 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> {
166 match self {
167 Self::Wechat(e) => e.micropay(
168 channel,
169 auth_code,
170 sub_mchid,
171 out_trade_no,
172 description,
173 total_fee,
174 org_openid,
175 ip,
176 ),
177 Self::Alipay(e) => e.micropay(
178 channel,
179 auth_code,
180 sub_mchid,
181 out_trade_no,
182 description,
183 total_fee,
184 org_openid,
185 ip,
186 ),
187 Pay::Ccbc(e) => e.micropay(
188 channel,
189 auth_code,
190 sub_mchid,
191 out_trade_no,
192 description,
193 total_fee,
194 org_openid,
195 ip,
196 ),
197 Pay::None => Err("No login data".to_owned()),
198 &mut Pay::Yrcc(_) => todo!()
199 }
200 }
201
202 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
203 match self {
204 Self::Wechat(e) => e.close(
205 out_trade_no, sub_mchid,
206 ),
207 Self::Alipay(e) => e.close(
208 out_trade_no, sub_mchid,
209 ),
210 Pay::Ccbc(e) => e.close(
211 out_trade_no, sub_mchid,
212 ),
213 Pay::None => Err("No login data".to_owned()),
214 &mut Pay::Yrcc(_) => todo!()
215 }
216 }
217
218 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
219 match self {
220 Self::Wechat(e) => e.pay_query(
221 out_trade_no,
222 sub_mchid,
223 ),
224 Self::Alipay(e) => e.pay_query(
225 out_trade_no,
226 sub_mchid,
227 ),
228 Self::Ccbc(e) => e.pay_query(
229 out_trade_no,
230 sub_mchid,
231 ),
232 Pay::None => Err("No login data".to_owned()),
233 &mut Pay::Yrcc(_) => todo!()
234 }
235 }
236 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str, channel: &str) -> Result<JsonValue, String> {
237 match self {
238 Self::Wechat(e) => e.pay_micropay_query(
239 out_trade_no,
240 sub_mchid,
241 channel,
242 ),
243 Self::Alipay(e) => e.pay_query(
244 out_trade_no,
245 sub_mchid,
246 ),
247 Pay::Ccbc(e) => e.pay_micropay_query(
248 out_trade_no,
249 sub_mchid,
250 channel,
251 ),
252 Pay::None => Err("No login data".to_owned()),
253 &mut Pay::Yrcc(_) => todo!()
254 }
255 }
256
257 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
258 match self {
259 Self::Wechat(e) => e.pay_notify(
260 nonce,
261 ciphertext,
262 associated_data,
263 ),
264 Self::Alipay(e) => e.pay_notify(
265 nonce,
266 ciphertext,
267 associated_data,
268 ),
269 Pay::None => Err("No login data".to_owned()),
270 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
271 }
272 }
273
274 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> {
275 match self {
276 Self::Wechat(e) => e.refund(
277 sub_mchid,
278 out_trade_no,
279 transaction_id,
280 out_refund_no,
281 amount,
282 total,
283 currency,
284 ),
285 Self::Alipay(e) => e.refund(
286 sub_mchid,
287 out_trade_no,
288 transaction_id,
289 out_refund_no,
290 amount,
291 total,
292 currency,
293 ),
294 Self::Ccbc(e) => e.refund(
295 sub_mchid,
296 out_trade_no,
297 transaction_id,
298 out_refund_no,
299 amount,
300 total,
301 currency,
302 ),
303 Pay::None => Err("No login data".to_owned()),
304 &mut Pay::Yrcc(_) => todo!()
305 }
306 }
307
308 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> {
309 match self {
310 Self::Wechat(e) => e.micropay_refund(
311 sub_mchid,
312 out_trade_no,
313 transaction_id,
314 out_refund_no,
315 amount,
316 total,
317 currency,
318 refund_text,
319 ),
320 Self::Alipay(e) => e.refund(
321 sub_mchid,
322 out_trade_no,
323 transaction_id,
324 out_refund_no,
325 amount,
326 total,
327 currency,
328 ),
329 Pay::None => Err("No login data".to_owned()),
330 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
331 }
332 }
333
334 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
335 match self {
336 Self::Wechat(e) => e.refund_notify(
337 nonce,
338 ciphertext,
339 associated_data,
340 ),
341 Self::Alipay(e) => e.refund_notify(
342 nonce,
343 ciphertext,
344 associated_data,
345 ),
346 Pay::None => Err("No login data".to_owned()),
347 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
348 }
349 }
350
351 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
352 match self {
353 Self::Wechat(e) => e.refund_query(
354 trade_no,
355 out_refund_no,
356 sub_mchid,
357 ),
358 Self::Alipay(e) => e.refund_query(
359 trade_no,
360 out_refund_no,
361 sub_mchid,
362 ),
363 Pay::Ccbc(e) => e.refund_query(
364 trade_no,
365 out_refund_no,
366 sub_mchid),
367 Pay::None => Err("No login data".to_owned()),
368 &mut Pay::Yrcc(_) => todo!()
369 }
370 }
371
372 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> {
373 match self {
374 Self::Wechat(e) => e.incoming(
375 business_code,
376 contact_info,
377 subject_info,
378 business_info,
379 settlement_info,
380 bank_account_info,
381 ),
382 Self::Alipay(e) => e.incoming(
383 business_code,
384 contact_info,
385 subject_info,
386 business_info,
387 settlement_info,
388 bank_account_info,
389 ),
390 Pay::None => Err("No incoming data".to_owned()),
391 &mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!()
392 }
393 }
394}
395
396pub trait PayMode {
397 fn check(&mut self) -> Result<bool, String>;
399 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
401 fn config(&mut self) -> JsonValue;
402 #[allow(clippy::too_many_arguments)]
403 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>;
412
413 #[allow(clippy::too_many_arguments)]
414 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>;
415 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
417 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
421 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str, channel: &str) -> Result<JsonValue, String>;
422 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
424
425 #[allow(clippy::too_many_arguments)]
426 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>;
428 #[allow(clippy::too_many_arguments)]
429 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>;
430 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
432 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
434 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>;
436}
437
438#[derive(Debug)]
440pub struct PayNotify {
441 trade_type: TradeType,
443 out_trade_no: String,
445 sp_mchid: String,
447 sub_mchid: String,
449 sp_appid: String,
451 transaction_id: String,
453 success_time: i64,
455 sp_openid: String,
457 sub_openid: String,
459 total: f64,
461 payer_total: f64,
463 currency: String,
465 payer_currency: String,
467 trade_state: TradeState,
469}
470impl PayNotify {
471 pub fn json(&self) -> JsonValue {
472 object! {
473 "trade_type" => self.trade_type.to_string(),
474 "out_trade_no" => self.out_trade_no.to_string(),
475 "sp_mchid" => self.sp_mchid.to_string(),
476 "sub_mchid" => self.sub_mchid.to_string(),
477 "sp_appid" => self.sp_appid.to_string(),
478 "transaction_id" => self.transaction_id.to_string(),
479 "success_time" => self.success_time,
480 "sp_openid" => self.sp_openid.to_string(),
481 "sub_openid" => self.sub_openid.to_string(),
482 "total" => self.total,
483 "payer_total" => self.payer_total,
484 "currency" => self.currency.clone(),
485 "payer_currency" => self.payer_currency.clone(),
486 "trade_state" => self.trade_state.to_string(),
487 }
488 }
489 pub fn success_time(datetime: &str) -> i64 {
490 if datetime.is_empty() {
491 return 0;
492 }
493 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
494 datetime.timestamp()
495 }
496 pub fn datetime_to_timestamp(datetime: &str, fmt: &str) -> i64 {
497 let t = NaiveDateTime::parse_from_str(datetime, fmt).unwrap();
498 let tz = FixedOffset::east_opt(Local::now().offset().local_minus_utc()).unwrap();
499 t.and_local_timezone(tz).unwrap().timestamp()
500 }
501}
502#[derive(Debug)]
503pub enum TradeType {
504 JSAPI,
506 MICROPAY,
507 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}