1use chrono::{DateTime, FixedOffset, Local, NaiveDateTime};
2use crate::wechat::{Wechat};
3use json::{object, JsonValue};
4use crate::alipay::AliPay;
5
6pub mod alipay;
7pub mod wechat;
8
9#[derive(Clone)]
10pub enum Pay {
11 Wechat(Wechat),
12 Alipay(AliPay),
13 None,
14}
15
16impl Pay {
17 pub fn new(data: JsonValue) -> Self {
18 match data["mode"].as_str().unwrap_or("") {
19 "wechat" => {
20 Pay::Wechat(Wechat {
21 appid: data["appid"].to_string(),
22 sp_mchid: data["sp_mchid"].to_string(),
23 serial_no: data["serial_no"].to_string(),
24 app_private: data["app_private"].to_string(),
25 secret: data["secret"].to_string(),
26 apikey: data["apikey"].to_string(),
27 apiv2: data["apiv2"].to_string(),
28 notify_url: data["notify_url"].to_string()
29 })
30 }
31 "alipay" => Pay::Alipay(AliPay {
32 appid: data["appid"].to_string(),
33 sp_mchid: data["sp_mchid"].to_string(),
34 app_private: data["app_private"].to_string(),
35 app_auth_token: data["app_auth_token"].as_str().unwrap_or("").to_string(),
36 content_encryp: data["content_encryp"].to_string(),
37 alipay_public_key: data["alipay_public_key"].to_string(),
38 notify_url: data["notify_url"].to_string(),
39 }),
40 _ => Pay::None
41 }
42 }
43}
44impl PayMode for Pay {
45
46 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String> {
47 match self {
48 Self::Wechat(e) => e.get_sub_mchid(sub_mchid),
49 Self::Alipay(e) => e.get_sub_mchid(sub_mchid),
50 Pay::None => Err("No login data".to_owned())
51 }
52 }
53
54 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String> {
55 match self {
56 Self::Wechat(e) => e.notify(data),
57 Self::Alipay(e) => e.notify(data),
58 Pay::None => Err("No login data".to_owned())
59 }
60 }
61
62 fn config(&mut self) -> JsonValue {
63 match self {
64 Self::Wechat(e) => object! {
65 sp_mchid:e.sp_mchid.clone(),
66 appid:e.appid.clone(),
67 },
68 Self::Alipay(e) => object! {
69 appid:e.appid.clone(),
70 sp_mchid:e.sp_mchid.clone()
71 },
72 Pay::None => object! {
73 appid:"",
74 sp_mchid:""
75 }
76 }
77 }
78
79 fn login(&mut self, code: &str) -> Result<JsonValue, String> {
80 match self {
81 Self::Wechat(e) => e.login(code),
82 Self::Alipay(e) => e.login(code),
83 Pay::None => Err("No login data".to_owned())
84 }
85 }
86
87 fn auth(&mut self, code: &str) -> Result<JsonValue, String> {
88 match self {
89 Self::Wechat(e) => e.auth(code),
90 Self::Alipay(e) => e.auth(code),
91 Pay::None => Err("No login data".to_owned())
92 }
93 }
94
95 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String> {
96 match self {
97 Self::Wechat(e) => e.pay(
98 types,
99 sub_mchid,
100 out_trade_no,
101 description,
102 total_fee,
103 sp_openid,
104 ),
105 Self::Alipay(e) => e.pay(
106 types,
107 sub_mchid,
108 out_trade_no,
109 description,
110 total_fee,
111 sp_openid,
112 ),
113 Pay::None => Err("No login data".to_owned())
114 }
115 }
116
117 fn micropay(&mut self, auth_code: &str, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, org_openid: &str, ip: &str) -> Result<JsonValue, String> {
118 match self {
119 Self::Wechat(e) => e.micropay(
120 auth_code,
121 sub_mchid,
122 out_trade_no,
123 description,
124 total_fee,
125 org_openid,
126 ip,
127 ),
128 Self::Alipay(e) => e.micropay(
129 auth_code,
130 sub_mchid,
131 out_trade_no,
132 description,
133 total_fee,
134 org_openid,
135 ip,
136 ),
137 Pay::None => Err("No login data".to_owned())
138 }
139 }
140
141 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
142 match self {
143 Self::Wechat(e) => e.close(
144 out_trade_no, sub_mchid,
145 ),
146 Self::Alipay(e) => e.close(
147 out_trade_no, sub_mchid,
148 ),
149 Pay::None => Err("No login data".to_owned())
150 }
151 }
152
153 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
154 match self {
155 Self::Wechat(e) => e.pay_query(
156 out_trade_no,
157 sub_mchid,
158 ),
159 Self::Alipay(e) => e.pay_query(
160 out_trade_no,
161 sub_mchid,
162 ),
163 Pay::None => Err("No login data".to_owned())
164 }
165 }
166 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
167 match self {
168 Self::Wechat(e) => e.pay_micropay_query(
169 out_trade_no,
170 sub_mchid,
171 ),
172 Self::Alipay(e) => e.pay_query(
173 out_trade_no,
174 sub_mchid,
175 ),
176 Pay::None => Err("No login data".to_owned())
177 }
178 }
179
180 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
181 match self {
182 Self::Wechat(e) => e.pay_notify(
183 nonce,
184 ciphertext,
185 associated_data,
186 ),
187 Self::Alipay(e) => e.pay_notify(
188 nonce,
189 ciphertext,
190 associated_data,
191 ),
192 Pay::None => Err("No login data".to_owned())
193 }
194 }
195
196 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> {
197 match self {
198 Self::Wechat(e) => e.refund(
199 sub_mchid,
200 out_trade_no,
201 transaction_id,
202 out_refund_no,
203 amount,
204 total,
205 currency,
206 ),
207 Self::Alipay(e) => e.refund(
208 sub_mchid,
209 out_trade_no,
210 transaction_id,
211 out_refund_no,
212 amount,
213 total,
214 currency,
215 ),
216 Pay::None => Err("No login data".to_owned())
217 }
218 }
219
220 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> {
221 match self {
222 Self::Wechat(e) => e.micropay_refund(
223 sub_mchid,
224 out_trade_no,
225 transaction_id,
226 out_refund_no,
227 amount,
228 total,
229 currency,
230 refund_text,
231 ),
232 Self::Alipay(e) => e.refund(
233 sub_mchid,
234 out_trade_no,
235 transaction_id,
236 out_refund_no,
237 amount,
238 total,
239 currency,
240 ),
241 Pay::None => Err("No login data".to_owned())
242 }
243 }
244
245 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
246 match self {
247 Self::Wechat(e) => e.refund_notify(
248 nonce,
249 ciphertext,
250 associated_data,
251 ),
252 Self::Alipay(e) => e.refund_notify(
253 nonce,
254 ciphertext,
255 associated_data,
256 ),
257 Pay::None => Err("No login data".to_owned())
258 }
259 }
260
261 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
262 match self {
263 Self::Wechat(e) => e.refund_query(
264 trade_no,
265 out_refund_no,
266 sub_mchid,
267 ),
268 Self::Alipay(e) => e.refund_query(
269 trade_no,
270 out_refund_no,
271 sub_mchid,
272 ),
273 Pay::None => Err("No login data".to_owned())
274 }
275 }
276
277 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> {
278 match self {
279 Self::Wechat(e) => e.incoming(
280 business_code,
281 contact_info,
282 subject_info,
283 business_info,
284 settlement_info,
285 bank_account_info,
286 ),
287 Self::Alipay(e) => e.incoming(
288 business_code,
289 contact_info,
290 subject_info,
291 business_info,
292 settlement_info,
293 bank_account_info,
294 ),
295 Pay::None => Err("No incoming data".to_owned())
296 }
297 }
298}
299
300pub trait PayMode {
301 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
303 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String>;
304 fn config(&mut self) -> JsonValue;
305 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
306 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
307
308 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String>;
316
317 #[allow(clippy::too_many_arguments)]
318 fn micropay(&mut self, auth_code: &str, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, org_openid: &str, ip: &str) -> Result<JsonValue, String>;
319 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
321 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
325 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
326 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
328
329 #[allow(clippy::too_many_arguments)]
330 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>;
332 #[allow(clippy::too_many_arguments)]
333 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>;
334 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
336 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
338 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>;
340}
341
342#[derive(Debug)]
344pub struct PayNotify {
345 trade_type: TradeType,
347 out_trade_no: String,
349 sp_mchid: String,
351 sub_mchid: String,
353 sp_appid: String,
355 transaction_id: String,
357 success_time: i64,
359 sp_openid: String,
361 sub_openid: String,
363 total: f64,
365 payer_total: f64,
367 currency: String,
369 payer_currency: String,
371 trade_state: TradeState,
373}
374impl PayNotify {
375 pub fn json(&self) -> JsonValue {
376 object! {
377 "trade_type" => self.trade_type.to_string(),
378 "out_trade_no" => self.out_trade_no.to_string(),
379 "sp_mchid" => self.sp_mchid.to_string(),
380 "sub_mchid" => self.sub_mchid.to_string(),
381 "sp_appid" => self.sp_appid.to_string(),
382 "transaction_id" => self.transaction_id.to_string(),
383 "success_time" => self.success_time,
384 "sp_openid" => self.sp_openid.to_string(),
385 "sub_openid" => self.sub_openid.to_string(),
386 "total" => self.total,
387 "payer_total" => self.payer_total,
388 "currency" => self.currency.clone(),
389 "payer_currency" => self.payer_currency.clone(),
390 "trade_state" => self.trade_state.to_string(),
391 }
392 }
393 pub fn success_time(datetime: &str) -> i64 {
394 if datetime.is_empty() {
395 return 0;
396 }
397 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
398 datetime.timestamp()
399 }
400 pub fn alipay_time(datetime: &str) -> i64 {
401 if datetime.is_empty() {
402 return 0;
403 }
404 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
405 t.and_utc().timestamp()
406 }
407 pub fn datetime_to_timestamp(datetime: &str, fmt: &str) -> i64 {
408 let t = NaiveDateTime::parse_from_str(datetime, fmt).unwrap();
409 let tz = FixedOffset::east_opt(Local::now().offset().local_minus_utc()).unwrap();
410 t.and_local_timezone(tz).unwrap().timestamp()
411 }
412}
413#[derive(Debug)]
414pub enum TradeType {
415 JSAPI,
417 MICROPAY,
418 None,
419}
420impl TradeType {
421 fn from(code: &str) -> TradeType {
422 match code {
423 "JSAPI" => TradeType::JSAPI,
424 "MICROPAY" => TradeType::MICROPAY,
425 _ => TradeType::None
426 }
427 }
428 fn to_string(&self) -> &'static str {
429 match self {
430 TradeType::JSAPI => "JSAPI",
431 TradeType::MICROPAY => "MICROPAY",
432 TradeType::None => ""
433 }
434 }
435}
436#[derive(Debug)]
437pub enum TradeState {
438 SUCCESS,
440 REFUND,
442 NOTPAY,
444 CLOSED,
446 REVOKED,
448 USERPAYING,
450 PAYERROR,
452 None,
453}
454impl TradeState {
455 fn from(code: &str) -> TradeState {
456 match code {
457 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
458 "REFUND" => TradeState::REFUND,
459 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
460 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
461 "REVOKED" => TradeState::REVOKED,
462 "USERPAYING" => TradeState::USERPAYING,
463 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
464 _ => TradeState::None
465 }
466 }
467
468 fn to_string(&self) -> &'static str {
469 match self {
470 TradeState::SUCCESS => "已支付",
471 TradeState::REFUND => "已支付",
472 TradeState::NOTPAY => "待支付",
473 TradeState::CLOSED => "已关闭",
474 TradeState::REVOKED => "已关闭",
475 TradeState::USERPAYING => "待支付",
476 TradeState::PAYERROR => "支付失败",
477 TradeState::None => "待支付"
478 }
479 }
480}
481
482#[derive(Debug)]
483pub enum RefundStatus {
484 SUCCESS,
486 CLOSED,
488 PROCESSING,
490 ABNORMAL,
492 None,
493}
494impl RefundStatus {
495 fn from(code: &str) -> RefundStatus {
496 match code {
497 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
498 "CLOSED" => RefundStatus::CLOSED,
499 "PROCESSING" | "N" => RefundStatus::PROCESSING,
500 "ABNORMAL" => RefundStatus::ABNORMAL,
501 _ => RefundStatus::None
502 }
503 }
504 fn to_string(&self) -> &'static str {
505 match self {
506 RefundStatus::SUCCESS => "已退款",
507 RefundStatus::None => "退款中",
508 RefundStatus::CLOSED => "已关闭",
509 RefundStatus::PROCESSING => "退款中",
510 RefundStatus::ABNORMAL => "退款异常"
511 }
512 }
513}
514#[derive(Debug)]
516pub struct RefundNotify {
517 out_trade_no: String,
519 refund_no: String,
520 sp_mchid: String,
522 sub_mchid: String,
524 transaction_id: String,
526 refund_id: String,
528 success_time: i64,
530 total: f64,
532 refund: f64,
534 payer_total: f64,
536 payer_refund: f64,
538 status: RefundStatus,
540}
541
542impl RefundNotify {
543 pub fn json(&self) -> JsonValue {
544 object! {
545 "out_trade_no" => self.out_trade_no.clone(),
546 "sp_mchid" => self.sp_mchid.clone(),
547 "sub_mchid" => self.sub_mchid.clone(),
548 "transaction_id" => self.transaction_id.clone(),
549 "success_time" => self.success_time,
550 "total" => self.total,
551 "refund" => self.refund,
552 "payer_total" => self.payer_total,
553 "payer_refund" => self.payer_refund,
554 "status" => self.status.to_string(),
555 "refund_no"=>self.refund_no.clone(),
556 "refund_id"=>self.refund_id.clone()
557 }
558 }
559}
560
561pub enum Types {
562 Jsapi,
564 Native,
566 H5,
568 MiniJsapi,
570 App,
572 Micropay,
574}
575impl Types {
576 pub fn str(self) -> &'static str {
577 match self {
578 Types::Jsapi => "jsapi",
579 Types::Native => "native",
580 Types::H5 => "h5",
581 Types::MiniJsapi => "minijsapi",
582 Types::App => "app",
583 Types::Micropay => "micropay"
584 }
585 }
586 pub fn from(name: &str) -> Self {
587 match name {
588 "jsapi" => Types::Jsapi,
589 "native" => Types::Native,
590 "h5" => Types::H5,
591 "minijsapi" => Types::MiniJsapi,
592 "app" => Types::App,
593 "micropay" => Types::Micropay,
594 _ => Types::Jsapi
595 }
596 }
597}