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
236pub trait PayMode {
237 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
239 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String>;
240 fn config(&mut self) -> JsonValue;
241 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
242 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
243
244 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String>;
252
253 #[allow(clippy::too_many_arguments)]
254 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>;
255 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
257 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
261 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
263
264 #[allow(clippy::too_many_arguments)]
265 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>;
267 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
269 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
271}
272
273#[derive(Debug)]
275pub struct PayNotify {
276 trade_type: TradeType,
278 out_trade_no: String,
280 sp_mchid: String,
282 sub_mchid: String,
284 sp_appid: String,
286 transaction_id: String,
288 success_time: i64,
290 sp_openid: String,
292 sub_openid: String,
294 total: usize,
296 payer_total: usize,
298 currency: String,
300 payer_currency: String,
302 trade_state: TradeState,
304}
305impl PayNotify {
306 pub fn json(&self) -> JsonValue {
307 object! {
308 "trade_type" => self.trade_type.to_string(),
309 "out_trade_no" => self.out_trade_no.to_string(),
310 "sp_mchid" => self.sp_mchid.to_string(),
311 "sub_mchid" => self.sub_mchid.to_string(),
312 "sp_appid" => self.sp_appid.to_string(),
313 "transaction_id" => self.transaction_id.to_string(),
314 "success_time" => self.success_time,
315 "sp_openid" => self.sp_openid.to_string(),
316 "sub_openid" => self.sub_openid.to_string(),
317 "total" => self.total,
318 "payer_total" => self.payer_total,
319 "currency" => self.currency.clone(),
320 "payer_currency" => self.payer_currency.clone(),
321 "trade_state" => self.trade_state.to_string(),
322 }
323 }
324 pub fn success_time(datetime: &str) -> i64 {
325 if datetime.is_empty() {
326 return 0;
327 }
328 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
329 datetime.timestamp()
330 }
331 pub fn alipay_time(datetime: &str) -> i64 {
332 if datetime.is_empty() {
333 return 0;
334 }
335 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
336 t.and_utc().timestamp()
337 }
338}
339#[derive(Debug)]
340pub enum TradeType {
341 JSAPI,
343 None,
344}
345impl TradeType {
346 fn from(code: &str) -> TradeType {
347 match code {
348 "JSAPI" => TradeType::JSAPI,
349 _ => TradeType::None
350 }
351 }
352 fn to_string(&self) -> &'static str {
353 match self {
354 TradeType::JSAPI => "JSAPI",
355 TradeType::None => ""
356 }
357 }
358}
359#[derive(Debug)]
360pub enum TradeState {
361 SUCCESS,
363 REFUND,
365 NOTPAY,
367 CLOSED,
369 REVOKED,
371 USERPAYING,
373 PAYERROR,
375 None,
376}
377impl TradeState {
378 fn from(code: &str) -> TradeState {
379 match code {
380 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
381 "REFUND" => TradeState::REFUND,
382 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
383 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
384 "REVOKED" => TradeState::REVOKED,
385 "USERPAYING" => TradeState::USERPAYING,
386 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
387 _ => TradeState::None
388 }
389 }
390
391 fn to_string(&self) -> &'static str {
392 match self {
393 TradeState::SUCCESS => "已支付",
394 TradeState::REFUND => "已支付",
395 TradeState::NOTPAY => "待支付",
396 TradeState::CLOSED => "已关闭",
397 TradeState::REVOKED => "已关闭",
398 TradeState::USERPAYING => "待支付",
399 TradeState::PAYERROR => "支付失败",
400 TradeState::None => "待支付"
401 }
402 }
403}
404
405#[derive(Debug)]
406pub enum RefundStatus {
407 SUCCESS,
409 CLOSED,
411 PROCESSING,
413 ABNORMAL,
415 None,
416}
417impl RefundStatus {
418 fn from(code: &str) -> RefundStatus {
419 match code {
420 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
421 "CLOSED" => RefundStatus::CLOSED,
422 "PROCESSING" | "N" => RefundStatus::PROCESSING,
423 "ABNORMAL" => RefundStatus::ABNORMAL,
424 _ => RefundStatus::None
425 }
426 }
427 fn to_string(&self) -> &'static str {
428 match self {
429 RefundStatus::SUCCESS => "已退款",
430 RefundStatus::None => "退款中",
431 RefundStatus::CLOSED => "已关闭",
432 RefundStatus::PROCESSING => "退款中",
433 RefundStatus::ABNORMAL => "退款异常"
434 }
435 }
436}
437#[derive(Debug)]
439pub struct RefundNotify {
440 out_trade_no: String,
442 refund_no: String,
443 sp_mchid: String,
445 sub_mchid: String,
447 transaction_id: String,
449 refund_id: String,
451 success_time: i64,
453 total: f64,
455 refund: f64,
457 payer_total: f64,
459 payer_refund: f64,
461 status: RefundStatus,
463}
464
465impl RefundNotify {
466 pub fn json(&self) -> JsonValue {
467 object! {
468 "out_trade_no" => self.out_trade_no.clone(),
469 "sp_mchid" => self.sp_mchid.clone(),
470 "sub_mchid" => self.sub_mchid.clone(),
471 "transaction_id" => self.transaction_id.clone(),
472 "success_time" => self.success_time,
473 "total" => self.total,
474 "refund" => self.refund,
475 "payer_total" => self.payer_total,
476 "payer_refund" => self.payer_refund,
477 "status" => self.status.to_string(),
478 "refund_no"=>self.refund_no.clone(),
479 "refund_id"=>self.refund_id.clone()
480 }
481 }
482}
483
484pub enum Types {
485 Jsapi,
487 Native,
489 H5,
491 MiniJsapi,
493 App,
495 Micropay,
497}
498impl Types {
499 pub fn str(self) -> &'static str {
500 match self {
501 Types::Jsapi => "jsapi",
502 Types::Native => "native",
503 Types::H5 => "h5",
504 Types::MiniJsapi => "minijsapi",
505 Types::App => "app",
506 Types::Micropay => "micropay"
507 }
508 }
509 pub fn from(name: &str) -> Self {
510 match name {
511 "jsapi" => Types::Jsapi,
512 "native" => Types::Native,
513 "h5" => Types::H5,
514 "minijsapi" => Types::MiniJsapi,
515 "app" => Types::App,
516 "micropay" => Types::Micropay,
517 _ => Types::Jsapi
518 }
519 }
520}