1use chrono::{DateTime, 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" => Pay::Wechat(Wechat {
20 appid: data["appid"].to_string(),
21 sp_mchid: data["sp_mchid"].to_string(),
22 serial_no: data["serial_no"].to_string(),
23 app_private: data["app_private"].to_string(),
24 secret: data["secret"].to_string(),
25 apikey: data["apikey"].to_string(),
26 apiv2: data["apiv2"].to_string(),
27 notify_url: data["notify_url"].to_string(),
28 }),
29 "alipay" => Pay::Alipay(AliPay {
30 appid: data["appid"].to_string(),
31 sp_mchid: data["sp_mchid"].to_string(),
32 app_private: data["app_private"].to_string(),
33 app_auth_token: data["app_auth_token"].as_str().unwrap_or("").to_string(),
34 content_encryp: data["content_encryp"].to_string(),
35 alipay_public_key: data["alipay_public_key"].to_string(),
36 notify_url: data["notify_url"].to_string(),
37 }),
38 _ => Pay::None
39 }
40 }
41}
42impl PayMode for Pay {
43 fn login(&mut self, code: &str) -> Result<JsonValue, String> {
44 match self {
45 Self::Wechat(e) => e.login(code),
46 Self::Alipay(e) => e.login(code),
47 Pay::None => Err("No login data".to_owned())
48 }
49 }
50
51 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
52 match self {
53 Self::Wechat(e) => e.pay_query(
54 out_trade_no,
55 sub_mchid,
56 ),
57 Self::Alipay(e) => e.pay_query(
58 out_trade_no,
59 sub_mchid,
60 ),
61 Pay::None => Err("No login data".to_owned())
62 }
63 }
64
65 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> {
66 match self {
67 Self::Wechat(e) => e.refund(
68 sub_mchid,
69 out_trade_no,
70 transaction_id,
71 out_refund_no,
72 amount,
73 total,
74 currency,
75 ),
76 Self::Alipay(e) => e.refund(
77 sub_mchid,
78 out_trade_no,
79 transaction_id,
80 out_refund_no,
81 amount,
82 total,
83 currency,
84 ),
85 Pay::None => Err("No login data".to_owned())
86 }
87 }
88
89 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
90 match self {
91 Self::Wechat(e) => e.pay_notify(
92 nonce,
93 ciphertext,
94 associated_data,
95 ),
96 Self::Alipay(e) => e.pay_notify(
97 nonce,
98 ciphertext,
99 associated_data,
100 ),
101 Pay::None => Err("No login data".to_owned())
102 }
103 }
104
105 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
106 match self {
107 Self::Wechat(e) => e.refund_notify(
108 nonce,
109 ciphertext,
110 associated_data,
111 ),
112 Self::Alipay(e) => e.refund_notify(
113 nonce,
114 ciphertext,
115 associated_data,
116 ),
117 Pay::None => Err("No login data".to_owned())
118 }
119 }
120
121 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
122 match self {
123 Self::Wechat(e) => e.refund_query(
124 trade_no,
125 out_refund_no,
126 sub_mchid,
127 ),
128 Self::Alipay(e) => e.refund_query(
129 trade_no,
130 out_refund_no,
131 sub_mchid,
132 ),
133 Pay::None => Err("No login data".to_owned())
134 }
135 }
136
137 fn auth(&mut self, code: &str) -> Result<JsonValue, String> {
138 match self {
139 Self::Wechat(e) => e.auth(code),
140 Self::Alipay(e) => e.auth(code),
141 Pay::None => Err("No login data".to_owned())
142 }
143 }
144
145 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
146 match self {
147 Self::Wechat(e) => e.close(
148 out_trade_no, sub_mchid,
149 ),
150 Self::Alipay(e) => e.close(
151 out_trade_no, sub_mchid,
152 ),
153 Pay::None => Err("No login data".to_owned())
154 }
155 }
156
157 fn config(&mut self) -> JsonValue {
158 match self {
159 Self::Wechat(e) => object! {
160 sp_mchid:e.sp_mchid.clone(),
161 appid:e.appid.clone(),
162 },
163 Self::Alipay(e) => object! {
164 appid:e.appid.clone(),
165 sp_mchid:e.sp_mchid.clone()
166 },
167 Pay::None => object! {
168 appid:"",
169 sp_mchid:""
170 }
171 }
172 }
173
174 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String> {
175 match self {
176 Self::Wechat(e) => e.pay(
177 types,
178 sub_mchid,
179 out_trade_no,
180 description,
181 total_fee,
182 sp_openid,
183 ),
184 Self::Alipay(e) => e.pay(
185 types,
186 sub_mchid,
187 out_trade_no,
188 description,
189 total_fee,
190 sp_openid,
191 ),
192 Pay::None => Err("No login data".to_owned())
193 }
194 }
195 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> {
196 match self {
197 Self::Wechat(e) => e.micropay(
198 auth_code,
199 sub_mchid,
200 out_trade_no,
201 description,
202 total_fee,
203 org_openid,
204 ip,
205 ),
206 Self::Alipay(e) => e.micropay(
207 auth_code,
208 sub_mchid,
209 out_trade_no,
210 description,
211 total_fee,
212 org_openid,
213 ip,
214 ),
215 Pay::None => Err("No login data".to_owned())
216 }
217 }
218
219 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String> {
220 match self {
221 Self::Wechat(e) => e.notify(data),
222 Self::Alipay(e) => e.notify(data),
223 Pay::None => Err("No login data".to_owned())
224 }
225 }
226
227 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String> {
228 match self {
229 Self::Wechat(e) => e.get_sub_mchid(sub_mchid),
230 Self::Alipay(e) => e.get_sub_mchid(sub_mchid),
231 Pay::None => Err("No login data".to_owned())
232 }
233 }
234
235 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
236 match self {
237 Self::Wechat(e) => e.pay_micropay_query(
238 out_trade_no,
239 sub_mchid,
240 ),
241 Self::Alipay(e) => e.pay_query(
242 out_trade_no,
243 sub_mchid,
244 ),
245 Pay::None => Err("No login data".to_owned())
246 }
247 }
248
249 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> {
250 match self {
251 Self::Wechat(e) => e.micropay_refund(
252 sub_mchid,
253 out_trade_no,
254 transaction_id,
255 out_refund_no,
256 amount,
257 total,
258 currency,
259 refund_text,
260 ),
261 Self::Alipay(e) => e.refund(
262 sub_mchid,
263 out_trade_no,
264 transaction_id,
265 out_refund_no,
266 amount,
267 total,
268 currency,
269 ),
270 Pay::None => Err("No login data".to_owned())
271 }
272 }
273}
274
275pub trait PayMode {
276 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
278 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String>;
279 fn config(&mut self) -> JsonValue;
280 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
281 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
282
283 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String>;
291
292 #[allow(clippy::too_many_arguments)]
293 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>;
294 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
296 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
300 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
301 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
303
304 #[allow(clippy::too_many_arguments)]
305 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>;
307 #[allow(clippy::too_many_arguments)]
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 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
311 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
313}
314
315#[derive(Debug)]
317pub struct PayNotify {
318 trade_type: TradeType,
320 out_trade_no: String,
322 sp_mchid: String,
324 sub_mchid: String,
326 sp_appid: String,
328 transaction_id: String,
330 success_time: i64,
332 sp_openid: String,
334 sub_openid: String,
336 total: f64,
338 payer_total: f64,
340 currency: String,
342 payer_currency: String,
344 trade_state: TradeState,
346}
347impl PayNotify {
348 pub fn json(&self) -> JsonValue {
349 object! {
350 "trade_type" => self.trade_type.to_string(),
351 "out_trade_no" => self.out_trade_no.to_string(),
352 "sp_mchid" => self.sp_mchid.to_string(),
353 "sub_mchid" => self.sub_mchid.to_string(),
354 "sp_appid" => self.sp_appid.to_string(),
355 "transaction_id" => self.transaction_id.to_string(),
356 "success_time" => self.success_time,
357 "sp_openid" => self.sp_openid.to_string(),
358 "sub_openid" => self.sub_openid.to_string(),
359 "total" => self.total,
360 "payer_total" => self.payer_total,
361 "currency" => self.currency.clone(),
362 "payer_currency" => self.payer_currency.clone(),
363 "trade_state" => self.trade_state.to_string(),
364 }
365 }
366 pub fn success_time(datetime: &str) -> i64 {
367 if datetime.is_empty() {
368 return 0;
369 }
370 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
371 datetime.timestamp()
372 }
373 pub fn alipay_time(datetime: &str) -> i64 {
374 if datetime.is_empty() {
375 return 0;
376 }
377 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
378 t.and_utc().timestamp()
379 }
380 pub fn micropay_time(datetime: &str) -> i64 {
381 if datetime.is_empty() {
382 return 0;
383 }
384 let t = NaiveDateTime::parse_from_str(datetime, "%Y%m%d%H%M%S").unwrap();
385 t.and_utc().timestamp()
386 }
387}
388#[derive(Debug)]
389pub enum TradeType {
390 JSAPI,
392 MICROPAY,
393 None,
394}
395impl TradeType {
396 fn from(code: &str) -> TradeType {
397 match code {
398 "JSAPI" => TradeType::JSAPI,
399 "MICROPAY" => TradeType::MICROPAY,
400 _ => TradeType::None
401 }
402 }
403 fn to_string(&self) -> &'static str {
404 match self {
405 TradeType::JSAPI => "JSAPI",
406 TradeType::MICROPAY => "MICROPAY",
407 TradeType::None => ""
408 }
409 }
410}
411#[derive(Debug)]
412pub enum TradeState {
413 SUCCESS,
415 REFUND,
417 NOTPAY,
419 CLOSED,
421 REVOKED,
423 USERPAYING,
425 PAYERROR,
427 None,
428}
429impl TradeState {
430 fn from(code: &str) -> TradeState {
431 match code {
432 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
433 "REFUND" => TradeState::REFUND,
434 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
435 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
436 "REVOKED" => TradeState::REVOKED,
437 "USERPAYING" => TradeState::USERPAYING,
438 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
439 _ => TradeState::None
440 }
441 }
442
443 fn to_string(&self) -> &'static str {
444 match self {
445 TradeState::SUCCESS => "已支付",
446 TradeState::REFUND => "已支付",
447 TradeState::NOTPAY => "待支付",
448 TradeState::CLOSED => "已关闭",
449 TradeState::REVOKED => "已关闭",
450 TradeState::USERPAYING => "待支付",
451 TradeState::PAYERROR => "支付失败",
452 TradeState::None => "待支付"
453 }
454 }
455}
456
457#[derive(Debug)]
458pub enum RefundStatus {
459 SUCCESS,
461 CLOSED,
463 PROCESSING,
465 ABNORMAL,
467 None,
468}
469impl RefundStatus {
470 fn from(code: &str) -> RefundStatus {
471 match code {
472 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
473 "CLOSED" => RefundStatus::CLOSED,
474 "PROCESSING" | "N" => RefundStatus::PROCESSING,
475 "ABNORMAL" => RefundStatus::ABNORMAL,
476 _ => RefundStatus::None
477 }
478 }
479 fn to_string(&self) -> &'static str {
480 match self {
481 RefundStatus::SUCCESS => "已退款",
482 RefundStatus::None => "退款中",
483 RefundStatus::CLOSED => "已关闭",
484 RefundStatus::PROCESSING => "退款中",
485 RefundStatus::ABNORMAL => "退款异常"
486 }
487 }
488}
489#[derive(Debug)]
491pub struct RefundNotify {
492 out_trade_no: String,
494 refund_no: String,
495 sp_mchid: String,
497 sub_mchid: String,
499 transaction_id: String,
501 refund_id: String,
503 success_time: i64,
505 total: f64,
507 refund: f64,
509 payer_total: f64,
511 payer_refund: f64,
513 status: RefundStatus,
515}
516
517impl RefundNotify {
518 pub fn json(&self) -> JsonValue {
519 object! {
520 "out_trade_no" => self.out_trade_no.clone(),
521 "sp_mchid" => self.sp_mchid.clone(),
522 "sub_mchid" => self.sub_mchid.clone(),
523 "transaction_id" => self.transaction_id.clone(),
524 "success_time" => self.success_time,
525 "total" => self.total,
526 "refund" => self.refund,
527 "payer_total" => self.payer_total,
528 "payer_refund" => self.payer_refund,
529 "status" => self.status.to_string(),
530 "refund_no"=>self.refund_no.clone(),
531 "refund_id"=>self.refund_id.clone()
532 }
533 }
534}
535
536pub enum Types {
537 Jsapi,
539 Native,
541 H5,
543 MiniJsapi,
545 App,
547 Micropay,
549}
550impl Types {
551 pub fn str(self) -> &'static str {
552 match self {
553 Types::Jsapi => "jsapi",
554 Types::Native => "native",
555 Types::H5 => "h5",
556 Types::MiniJsapi => "minijsapi",
557 Types::App => "app",
558 Types::Micropay => "micropay"
559 }
560 }
561 pub fn from(name: &str) -> Self {
562 match name {
563 "jsapi" => Types::Jsapi,
564 "native" => Types::Native,
565 "h5" => Types::H5,
566 "minijsapi" => Types::MiniJsapi,
567 "app" => Types::App,
568 "micropay" => Types::Micropay,
569 _ => Types::Jsapi
570 }
571 }
572}