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 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String> {
200 match self {
201 Self::Wechat(e) => e.notify(data),
202 Self::Alipay(e) => e.notify(data),
203 Pay::None => Err("No login data".to_owned())
204 }
205 }
206}
207
208pub trait PayMode {
209 fn notify(&mut self, data: JsonValue) -> Result<JsonValue, String>;
210 fn config(&mut self) -> JsonValue;
211 fn login(&mut self, code: &str) -> Result<JsonValue, String>;
212 fn auth(&mut self, code: &str) -> Result<JsonValue, String>;
213
214 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String>;
222 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
224 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
228 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
230
231 #[allow(clippy::too_many_arguments)]
232 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>;
234 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
236 fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
238}
239
240#[derive(Debug)]
242pub struct PayNotify {
243 trade_type: TradeType,
245 out_trade_no: String,
247 sp_mchid: String,
249 sub_mchid: String,
251 sp_appid: String,
253 transaction_id: String,
255 success_time: i64,
257 sp_openid: String,
259 sub_openid: String,
261 total: usize,
263 payer_total: usize,
265 currency: String,
267 payer_currency: String,
269 trade_state: TradeState,
271}
272impl PayNotify {
273 pub fn json(&self) -> JsonValue {
274 object! {
275 "trade_type" => self.trade_type.to_string(),
276 "out_trade_no" => self.out_trade_no.to_string(),
277 "sp_mchid" => self.sp_mchid.to_string(),
278 "sub_mchid" => self.sub_mchid.to_string(),
279 "sp_appid" => self.sp_appid.to_string(),
280 "transaction_id" => self.transaction_id.to_string(),
281 "success_time" => self.success_time,
282 "sp_openid" => self.sp_openid.to_string(),
283 "sub_openid" => self.sub_openid.to_string(),
284 "total" => self.total,
285 "payer_total" => self.payer_total,
286 "currency" => self.currency.clone(),
287 "payer_currency" => self.payer_currency.clone(),
288 "trade_state" => self.trade_state.to_string(),
289 }
290 }
291 pub fn success_time(datetime: &str) -> i64 {
292 if datetime.is_empty() {
293 return 0;
294 }
295 let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
296 datetime.timestamp()
297 }
298 pub fn alipay_time(datetime: &str) -> i64 {
299 if datetime.is_empty() {
300 return 0;
301 }
302 let t = NaiveDateTime::parse_from_str(datetime, "%Y-%m-%d %H:%M:%S").unwrap();
303 t.and_utc().timestamp()
304 }
305}
306#[derive(Debug)]
307pub enum TradeType {
308 JSAPI,
310 None,
311}
312impl TradeType {
313 fn from(code: &str) -> TradeType {
314 match code {
315 "JSAPI" => TradeType::JSAPI,
316 _ => TradeType::None
317 }
318 }
319 fn to_string(&self) -> &'static str {
320 match self {
321 TradeType::JSAPI => "JSAPI",
322 TradeType::None => ""
323 }
324 }
325}
326#[derive(Debug)]
327pub enum TradeState {
328 SUCCESS,
330 REFUND,
332 NOTPAY,
334 CLOSED,
336 REVOKED,
338 USERPAYING,
340 PAYERROR,
342 None,
343}
344impl TradeState {
345 fn from(code: &str) -> TradeState {
346 match code {
347 "SUCCESS" | "TRADE_SUCCESS" => TradeState::SUCCESS,
348 "REFUND" => TradeState::REFUND,
349 "NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
350 "CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
351 "REVOKED" => TradeState::REVOKED,
352 "USERPAYING" => TradeState::USERPAYING,
353 "PAYERROR" | "TRADE_FINISHED" => TradeState::PAYERROR,
354 _ => TradeState::None
355 }
356 }
357
358 fn to_string(&self) -> &'static str {
359 match self {
360 TradeState::SUCCESS => "已支付",
361 TradeState::REFUND => "已支付",
362 TradeState::NOTPAY => "待支付",
363 TradeState::CLOSED => "已关闭",
364 TradeState::REVOKED => "已关闭",
365 TradeState::USERPAYING => "待支付",
366 TradeState::PAYERROR => "支付失败",
367 TradeState::None => "待支付"
368 }
369 }
370}
371
372#[derive(Debug)]
373pub enum RefundStatus {
374 SUCCESS,
376 CLOSED,
378 PROCESSING,
380 ABNORMAL,
382 None,
383}
384impl RefundStatus {
385 fn from(code: &str) -> RefundStatus {
386 match code {
387 "SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
388 "CLOSED" => RefundStatus::CLOSED,
389 "PROCESSING" | "N" => RefundStatus::PROCESSING,
390 "ABNORMAL" => RefundStatus::ABNORMAL,
391 _ => RefundStatus::None
392 }
393 }
394 fn to_string(&self) -> &'static str {
395 match self {
396 RefundStatus::SUCCESS => "已退款",
397 RefundStatus::None => "退款中",
398 RefundStatus::CLOSED => "已关闭",
399 RefundStatus::PROCESSING => "退款中",
400 RefundStatus::ABNORMAL => "退款异常"
401 }
402 }
403}
404#[derive(Debug)]
406pub struct RefundNotify {
407 out_trade_no: String,
409 refund_no: String,
410 sp_mchid: String,
412 sub_mchid: String,
414 transaction_id: String,
416 refund_id: String,
418 success_time: i64,
420 total: f64,
422 refund: f64,
424 payer_total: f64,
426 payer_refund: f64,
428 status: RefundStatus,
430}
431
432impl RefundNotify {
433 pub fn json(&self) -> JsonValue {
434 object! {
435 "out_trade_no" => self.out_trade_no.clone(),
436 "sp_mchid" => self.sp_mchid.clone(),
437 "sub_mchid" => self.sub_mchid.clone(),
438 "transaction_id" => self.transaction_id.clone(),
439 "success_time" => self.success_time,
440 "total" => self.total,
441 "refund" => self.refund,
442 "payer_total" => self.payer_total,
443 "payer_refund" => self.payer_refund,
444 "status" => self.status.to_string(),
445 "refund_no"=>self.refund_no.clone(),
446 "refund_id"=>self.refund_id.clone()
447 }
448 }
449}
450
451pub enum Types {
452 Jsapi,
454 Native,
456 H5,
458 MiniJsapi,
460 App,
462 Micropay,
464}
465impl Types {
466 pub fn str(self) -> &'static str {
467 match self {
468 Types::Jsapi => "jsapi",
469 Types::Native => "native",
470 Types::H5 => "h5",
471 Types::MiniJsapi => "minijsapi",
472 Types::App => "app",
473 Types::Micropay => "micropay"
474 }
475 }
476 pub fn from(name: &str) -> Self {
477 match name {
478 "jsapi" => Types::Jsapi,
479 "native" => Types::Native,
480 "h5" => Types::H5,
481 "minijsapi" => Types::MiniJsapi,
482 "app" => Types::App,
483 "micropay" => Types::Micropay,
484 _ => Types::Jsapi
485 }
486 }
487}