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 notify_url: data["notify_url"].to_string(),
27 }),
28 "alipay" => Pay::Alipay(AliPay {
29 appid: data["appid"].to_string(),
30 sp_mchid: data["sp_mchid"].to_string(),
31 app_private: data["app_private"].to_string(),
32 app_auth_token: data["app_auth_token"].as_str().unwrap_or("").to_string(),
33 content_encryp: data["content_encryp"].to_string(),
34 alipay_public_key: data["alipay_public_key"].to_string(),
35 notify_url: data["notify_url"].to_string(),
36 }),
37 _ => Pay::None
38 }
39 }
40}
41impl PayMode for Pay {
42 fn login(&mut self, code: &str) -> Result<JsonValue, String> {
43 match self {
44 Self::Wechat(e) => e.login(code),
45 Self::Alipay(e) => e.login(code),
46 Pay::None => Err("No login data".to_owned())
47 }
48 }
49
50 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
51 match self {
52 Self::Wechat(e) => e.pay_query(
53 out_trade_no,
54 sub_mchid,
55 ),
56 Self::Alipay(e) => e.pay_query(
57 out_trade_no,
58 sub_mchid,
59 ),
60 Pay::None => Err("No login data".to_owned())
61 }
62 }
63
64 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> {
65 match self {
66 Self::Wechat(e) => e.refund(
67 sub_mchid,
68 out_trade_no,
69 transaction_id,
70 out_refund_no,
71 amount,
72 total,
73 currency,
74 ),
75 Self::Alipay(e) => e.refund(
76 sub_mchid,
77 out_trade_no,
78 transaction_id,
79 out_refund_no,
80 amount,
81 total,
82 currency,
83 ),
84 Pay::None => Err("No login data".to_owned())
85 }
86 }
87
88 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
89 match self {
90 Self::Wechat(e) => e.pay_notify(
91 nonce,
92 ciphertext,
93 associated_data,
94 ),
95 Self::Alipay(e) => e.pay_notify(
96 nonce,
97 ciphertext,
98 associated_data,
99 ),
100 Pay::None => Err("No login data".to_owned())
101 }
102 }
103
104 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
105 match self {
106 Self::Wechat(e) => e.refund_notify(
107 nonce,
108 ciphertext,
109 associated_data,
110 ),
111 Self::Alipay(e) => e.refund_notify(
112 nonce,
113 ciphertext,
114 associated_data,
115 ),
116 Pay::None => Err("No login data".to_owned())
117 }
118 }
119
120 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
121 match self {
122 Self::Wechat(e) => e.refund_query(
123 trade_no,
124 out_refund_no,
125 sub_mchid,
126 ),
127 Self::Alipay(e) => e.refund_query(
128 trade_no,
129 out_refund_no,
130 sub_mchid,
131 ),
132 Pay::None => Err("No login data".to_owned())
133 }
134 }
135
136 fn auth(&mut self, code: &str) -> Result<JsonValue, String> {
137 match self {
138 Self::Wechat(e) => e.auth(
139 code
140 ),
141 Self::Alipay(e) => e.auth(
142 code
143 ),
144 Pay::None => Err("No login data".to_owned())
145 }
146 }
147
148 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
149 match self {
150 Self::Wechat(e) => e.close(
151 out_trade_no, sub_mchid,
152 ),
153 Self::Alipay(e) => e.close(
154 out_trade_no, sub_mchid,
155 ),
156 Pay::None => Err("No login data".to_owned())
157 }
158 }
159
160 fn config(&mut self) -> JsonValue {
161 match self {
162 Self::Wechat(e) => object! {
163 sp_mchid:e.sp_mchid.clone(),
164 appid:e.appid.clone(),
165 },
166 Self::Alipay(e) => object! {
167 appid:e.appid.clone(),
168 sp_mchid:e.sp_mchid.clone()
169 },
170 Pay::None => object! {
171 appid:"",
172 sp_mchid:""
173 }
174 }
175 }
176
177 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String> {
178 match self {
179 Self::Wechat(e) => e.pay(
180 types,
181 sub_mchid,
182 out_trade_no,
183 description,
184 total_fee,
185 sp_openid,
186 ),
187 Self::Alipay(e) => e.pay(
188 types,
189 sub_mchid,
190 out_trade_no,
191 description,
192 total_fee,
193 sp_openid,
194 ),
195 Pay::None => Err("No login data".to_owned())
196 }
197 }
198}
199
200pub trait PayMode {
201 fn config(&mut self) -> JsonValue;
202 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
203 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
204
205 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String>;
213 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
215 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
219 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
221
222 #[allow(clippy::too_many_arguments)]
223 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>;
225 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
227 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
229}
230
231#[derive(Debug)]
233pub struct PayNotify {
234 trade_type: TradeType,
236 out_trade_no: String,
238 sp_mchid: String,
240 sub_mchid: String,
242 sp_appid: String,
244 transaction_id: String,
246 success_time: i64,
248 sp_openid: String,
250 sub_openid: String,
252 total: usize,
254 payer_total: usize,
256 currency: String,
258 payer_currency: String,
260 trade_state: TradeState,
262}
263impl PayNotify {
264 pub fn json(&self) -> JsonValue {
265 object! {
266 "trade_type" => self.trade_type.to_string(),
267 "out_trade_no" => self.out_trade_no.to_string(),
268 "sp_mchid" => self.sp_mchid.to_string(),
269 "sub_mchid" => self.sub_mchid.to_string(),
270 "sp_appid" => self.sp_appid.to_string(),
271 "transaction_id" => self.transaction_id.to_string(),
272 "success_time" => self.success_time,
273 "sp_openid" => self.sp_openid.to_string(),
274 "sub_openid" => self.sub_openid.to_string(),
275 "total" => self.total,
276 "payer_total" => self.payer_total,
277 "currency" => self.currency.clone(),
278 "payer_currency" => self.payer_currency.clone(),
279 "trade_state" => self.trade_state.to_string(),
280 }
281 }
282 pub fn success_time(datetime: &str) -> i64 {
283 if datetime.is_empty() {
284 return 0;
285 }
286 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
287 datetime.timestamp()
288 }
289 pub fn alipay_time(datetime: &str) -> i64 {
290 if datetime.is_empty() {
291 return 0;
292 }
293 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
294 t.and_utc().timestamp()
295 }
296}
297#[derive(Debug)]
298pub enum TradeType {
299 JSAPI,
301 None,
302}
303impl TradeType {
304 fn from(code: &str) -> TradeType {
305 match code {
306 "JSAPI" => TradeType::JSAPI,
307 _ => TradeType::None
308 }
309 }
310 fn to_string(&self) -> &'static str {
311 match self {
312 TradeType::JSAPI => "JSAPI",
313 TradeType::None => ""
314 }
315 }
316}
317#[derive(Debug)]
318pub enum TradeState {
319 SUCCESS,
321 REFUND,
323 NOTPAY,
325 CLOSED,
327 REVOKED,
329 USERPAYING,
331 PAYERROR,
333 None,
334}
335impl TradeState {
336 fn from(code: &str) -> TradeState {
337 match code {
338 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
339 "REFUND" => TradeState::REFUND,
340 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
341 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
342 "REVOKED" => TradeState::REVOKED,
343 "USERPAYING" => TradeState::USERPAYING,
344 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
345 _ => TradeState::None
346 }
347 }
348
349 fn to_string(&self) -> &'static str {
350 match self {
351 TradeState::SUCCESS => "已支付",
352 TradeState::REFUND => "已支付",
353 TradeState::NOTPAY => "待支付",
354 TradeState::CLOSED => "已关闭",
355 TradeState::REVOKED => "已关闭",
356 TradeState::USERPAYING => "待支付",
357 TradeState::PAYERROR => "支付失败",
358 TradeState::None => "待支付"
359 }
360 }
361}
362
363#[derive(Debug)]
364pub enum RefundStatus {
365 SUCCESS,
367 CLOSED,
369 PROCESSING,
371 ABNORMAL,
373 None,
374}
375impl RefundStatus {
376 fn from(code: &str) -> RefundStatus {
377 match code {
378 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
379 "CLOSED" => RefundStatus::CLOSED,
380 "PROCESSING" | "N" => RefundStatus::PROCESSING,
381 "ABNORMAL" => RefundStatus::ABNORMAL,
382 _ => RefundStatus::None
383 }
384 }
385 fn to_string(&self) -> &'static str {
386 match self {
387 RefundStatus::SUCCESS => "已退款",
388 RefundStatus::None => "退款中",
389 RefundStatus::CLOSED => "已关闭",
390 RefundStatus::PROCESSING => "退款中",
391 RefundStatus::ABNORMAL => "退款异常"
392 }
393 }
394}
395#[derive(Debug)]
397pub struct RefundNotify {
398 out_trade_no: String,
400 refund_no: String,
401 sp_mchid: String,
403 sub_mchid: String,
405 transaction_id: String,
407 refund_id: String,
409 success_time: i64,
411 total: f64,
413 refund: f64,
415 payer_total: f64,
417 payer_refund: f64,
419 status: RefundStatus,
421}
422
423impl RefundNotify {
424 pub fn json(&self) -> JsonValue {
425 object! {
426 "out_trade_no" => self.out_trade_no.clone(),
427 "sp_mchid" => self.sp_mchid.clone(),
428 "sub_mchid" => self.sub_mchid.clone(),
429 "transaction_id" => self.transaction_id.clone(),
430 "success_time" => self.success_time,
431 "total" => self.total,
432 "refund" => self.refund,
433 "payer_total" => self.payer_total,
434 "payer_refund" => self.payer_refund,
435 "status" => self.status.to_string(),
436 "refund_no"=>self.refund_no.clone(),
437 "refund_id"=>self.refund_id.clone(),
438 }
439 }
440}
441
442pub enum Types {
443 Jsapi,
445 Native,
447 H5,
449 MiniJsapi,
451 App,
453 Micropay,
455}
456impl Types {
457 pub fn str(self) -> &'static str {
458 match self {
459 Types::Jsapi => "jsapi",
460 Types::Native => "native",
461 Types::H5 => "h5",
462 Types::MiniJsapi => "minijsapi",
463 Types::App => "app",
464 Types::Micropay => "micropay"
465 }
466 }
467 pub fn from(name: &str) -> Self {
468 match name {
469 "jsapi" => Types::Jsapi,
470 "native" => Types::Native,
471 "h5" => Types::H5,
472 "minijsapi" => Types::MiniJsapi,
473 "app" => Types::App,
474 "micropay" => Types::Micropay,
475 _ => Types::Jsapi
476 }
477 }
478}