1use std::collections::HashMap;
2use crate::{PayMode, PayNotify, RefundNotify, RefundStatus, TradeState, TradeType, Types};
3use base64::engine::general_purpose::STANDARD;
4use base64::{Engine};
5use json::{object, JsonValue};
6use aes_gcm::{Aes256Gcm, Key, KeyInit, Nonce};
7use aes_gcm::aead::{Aead, Payload};
8use br_reqwest::Method;
9
10
11#[derive(Clone, Debug)]
12pub struct Wechat {
13 pub appid: String,
15 pub sp_mchid: String,
17 pub serial_no: String,
19 pub app_private: String,
21 pub apikey: String,
23 pub apiv2: String,
25 pub notify_url: String,
26
27}
28
29use chrono::{DateTime, Local, Utc};
30use log::error;
31use openssl::hash::MessageDigest;
32use openssl::pkey::{PKey};
33use openssl::rsa::Rsa;
34use openssl::sign::Signer;
35use rand::distr::Alphanumeric;
36use rand::{rng, Rng};
37
38impl Wechat {
39 pub fn http(&mut self, url: &str, method: Method, body: JsonValue) -> Result<JsonValue, String> {
40 let sign = self.sign(method.to_str().to_uppercase().as_str(), url, body.to_string().as_str())?;
41 let mut http = br_reqwest::Client::new();
42 let url = format!("https://api.mch.weixin.qq.com{url}");
43 let send = match method {
44 Method::GET => http.get(url.as_str()),
45 Method::POST => http.post(url.as_str()).raw_json(body),
46 _ => http.post(url.as_str()),
47 };
48 match send.header("Accept", "application/json").header("User-Agent", "api").header("Content-Type", "application/json").header("Authorization", sign.as_str()).send()?.json() {
49 Ok(e) => Ok(e),
50 Err(e) => Err(e)
51 }
52 }
53
54 pub fn sign_v2(&mut self, body: JsonValue) -> Result<String, String> {
55 let mut map = HashMap::new();
56 for (key, value) in body.entries() {
57 if key == "sign" {
58 continue;
59 }
60 if value.is_empty() {
61 continue;
62 }
63 map.insert(key, value);
64 }
65 let mut keys: Vec<_> = map.keys().cloned().collect();
66 keys.sort();
67 let mut txt = vec![];
68 for key in keys {
69 txt.push(format!("{}={}", key, map.get(&key).unwrap()));
70 }
71 let txt = txt.join("&");
72 let string_sign_temp = format!("{}&key={}", txt, self.apiv2);
73
74 let sign = br_crypto::md5::encrypt_hex(string_sign_temp.as_bytes()).to_uppercase();
75 Ok(sign)
76 }
77
78 pub fn sign(&mut self, method: &str, url: &str, body: &str) -> Result<String, String> {
79 let timestamp = Utc::now().timestamp(); let random_string: String = rng().sample_iter(&Alphanumeric) .take(10) .map(char::from).collect();
83
84 let sign_txt = format!("{method}\n{url}\n{timestamp}\n{random_string}\n{body}\n");
85 let rsa = match Rsa::private_key_from_pem(self.app_private.as_bytes()) {
87 Ok(e) => e,
88 Err(e) => {
89 return Err(e.to_string())
90 }
91 };
92 let pkey = match PKey::from_rsa(rsa) {
93 Ok(e) => e,
94 Err(e) => {
95 return Err(format!("Failed to create PKey: {e}"))
96 }
97 };
98 let mut signer = match Signer::new(MessageDigest::sha256(), &pkey) {
100 Ok(e) => e,
101 Err(e) => {
102 return Err(format!("Failed to create signer:{e}"));
103 }
104 };
105 match signer.update(sign_txt.as_bytes()) {
107 Ok(_) => {}
108 Err(e) => {
109 return Err(e.to_string())
110 }
111 };
112 let signature = match signer.sign_to_vec() {
114 Ok(e) => e,
115 Err(e) => {
116 return Err(format!("Failed to sign: {e}"));
117 }
118 };
119 let signature_b64 = STANDARD.encode(signature);
120 let sign = format!(
121 r#"WECHATPAY2-SHA256-RSA2048 mchid="{}",nonce_str="{random_string}",signature="{signature_b64}",timestamp="{timestamp}",serial_no="{}""#,
122 self.sp_mchid.as_str(),
123 self.serial_no
124 );
125 Ok(sign)
126 }
127
128 pub fn paysign(&mut self, prepay_id: &str) -> Result<JsonValue, String> {
129 let timestamp = Utc::now().timestamp(); let random_string: String = rng().sample_iter(&Alphanumeric) .take(10) .map(char::from).collect();
133
134 let sign_txt = format!(
135 "{}\n{timestamp}\n{random_string}\n{prepay_id}\n",
136 self.appid
137 );
138
139 let rsa = match Rsa::private_key_from_pem(self.app_private.as_bytes()) {
141 Ok(e) => e,
142 Err(e) => {
143 return Err(e.to_string())
144 }
145 };
146 let pkey = match PKey::from_rsa(rsa) {
147 Ok(e) => e,
148 Err(e) => {
149 return Err(format!("Failed to create PKey: {e}"))
150 }
151 };
152 let mut signer = match Signer::new(MessageDigest::sha256(), &pkey) {
154 Ok(e) => e,
155 Err(e) => {
156 return Err(format!("Failed to create signer:{e}"));
157 }
158 };
159 match signer.update(sign_txt.as_bytes()) {
161 Ok(_) => {}
162 Err(e) => {
163 return Err(e.to_string())
164 }
165 };
166 let signature = match signer.sign_to_vec() {
168 Ok(e) => e,
169 Err(e) => {
170 return Err(format!("Failed to sign: {e}"));
171 }
172 };
173 let signature_b64 = STANDARD.encode(signature);
174 let sign = signature_b64;
175 Ok(object! {
176 timeStamp:timestamp,
177 nonceStr:random_string,
178 package:prepay_id,
179 signType:"RSA",
180 paySign:sign
181 })
182 }
183}
184impl PayMode for Wechat {
185 fn check(&mut self) -> Result<bool, String> {
186 let timestamp = Utc::now().timestamp(); let now = Local::now();
188 let formatted = now.format("%Y%m%d").to_string();
189 let order_no = format!("test_{formatted}_{timestamp}");
190 match self.clone().pay("", Types::MiniJsapi, self.sp_mchid.as_str(), order_no.as_str(), "测试", 0.01, "") {
191 Ok(_) => Ok(true),
192 Err(e) => {
193 if e.contains("受理机构发起支付时, 子商户mchid不能与自身mchid相同") {
194 return Ok(true);
195 }
196 Ok(false)
197 }
198 }
199 }
200
201 fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String> {
202 let url = format!("/v3/apply4sub/sub_merchants/{sub_mchid}/settlement");
203 let res = self.http(url.as_str(), Method::GET, "".into())?;
204 if res.has_key("verify_result") && res["verify_result"] == "VERIFY_SUCCESS" {
205 return Ok(true.into());
206 }
207 Err(res.to_string())
208 }
209
210 fn notify(&mut self, _data: JsonValue) -> Result<JsonValue, String> {
211 todo!()
212 }
213
214 fn config(&mut self) -> JsonValue {
215 todo!()
216 }
217
218 fn login(&mut self, _code: &str) -> Result<JsonValue, String> {
219 todo!()
220 }
221
222
223 fn auth(&mut self, _code: &str) -> Result<JsonValue, String> {
224 todo!()
225 }
226 fn pay(&mut self, _channel: &str, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String> {
227 let url = match types {
228 Types::Jsapi => "/v3/pay/partner/transactions/jsapi",
229 Types::Native => "/v3/pay/partner/transactions/native",
230 Types::H5 => "/v3/pay/partner/transactions/h5",
231 Types::MiniJsapi => "/v3/pay/partner/transactions/jsapi",
232 Types::App => "/v3/pay/partner/transactions/app",
233 Types::Micropay => "/pay/micropay"
234 };
235 let total = format!("{:.0}", total_fee * 100.0);
236 let mut body = object! {
237 "sp_appid" => self.appid.clone(),
238 "sp_mchid"=> self.sp_mchid.clone(),
239 "sub_mchid"=> sub_mchid,
240 "description"=>description,
241 "out_trade_no"=>out_trade_no,
242 "notify_url"=>self.notify_url.clone(),
243 "support_fapiao"=>true,
244 "amount"=>object! {
245 total: total.parse::<i64>().unwrap(),
246 currency:"CNY"
247 }
248 };
249 match types {
250 Types::Native => {}
251 _ => {
252 body["payer"] = object! {
253 sp_openid:sp_openid
254 };
255 }
256 };
257 match self.http(url, Method::POST, body) {
258 Ok(e) => {
259 match types {
260 Types::Native => {
261 if e.has_key("code_url") {
262 Ok(e["code_url"].clone())
263 } else {
264 Err(e["message"].to_string())
265 }
266 }
267 Types::Jsapi | Types::MiniJsapi => {
268 if e.has_key("prepay_id") {
269 let signinfo = self.paysign(format!("prepay_id={}", e["prepay_id"]).as_str())?;
270 Ok(signinfo)
271 } else {
272 Err(e["message"].to_string())
273 }
274 }
275 _ => {
276 Ok(e)
277 }
278 }
279 }
280 Err(e) => Err(e),
281 }
282 }
283
284 fn micropay(&mut self, _channel: &str, auth_code: &str, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, org_openid: &str, ip: &str) -> Result<JsonValue, String> {
285 let url = "/pay/micropay";
286 let total = format!("{:.0}", total_fee * 100.0);
287
288 let nonce_str: String = rand::rng().sample_iter(&Alphanumeric).take(32).map(char::from).collect();
289
290 let mut body = object! {
291 "appid": self.appid.clone(),
292 "mch_id"=> self.sp_mchid.clone(),
293 "sub_mch_id"=> sub_mchid,
294 "nonce_str"=>nonce_str,
295 "body"=> description,
296 "out_trade_no"=>out_trade_no,
297 "total_fee"=>total.parse::<i64>().unwrap(),
298 "fee_type":"CNY",
299 "spbill_create_ip":ip,
300 "device_info":org_openid,
301 "auth_code":auth_code
302 };
303 body["sign"] = self.sign_v2(body.clone())?.into();
304 let mut xml = vec!["<xml>".to_owned()];
305 for (key, value) in body.entries() {
306 let t = format!("<{}>{}</{00}>", key, value.clone().clone());
307 xml.push(t);
308 }
309 xml.push("</xml>".to_owned());
310 let xml = xml.join("");
311 let mut http = br_reqwest::Client::new();
312 match http.post(format!("https://api.mch.weixin.qq.com{url}").as_str()).header("Content-Type", "application/xml").raw_xml(xml.into()).send()?.xml() {
313 Ok(e) => Ok(e),
314 Err(e) => Err(e),
315 }
316 }
317
318 fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
319 let url = format!("/v3/pay/partner/transactions/out-trade-no/{out_trade_no}/close");
320 let body = object! {
321 "sp_mchid"=> self.sp_mchid.clone(),
322 "sub_mchid"=> sub_mchid
323 };
324 match self.http(&url, Method::POST, body) {
325 Ok(_) => Ok(true.into()),
326 Err(e) => Err(e)
327 }
328 }
329
330 fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
331 let url = format!(
332 "/v3/pay/partner/transactions/out-trade-no/{}?sub_mchid={}&sp_mchid={}",
333 out_trade_no, sub_mchid, self.sp_mchid
334 );
335 match self.http(&url, Method::GET, "".into()) {
336 Ok(e) => {
337 if e.has_key("message") {
338 return Err(e["message"].to_string());
339 }
340 let res = PayNotify {
341 trade_type: TradeType::from(e["trade_type"].to_string().as_str()),
342 out_trade_no: e["out_trade_no"].as_str().unwrap().to_string(),
343 sp_mchid: e["sp_mchid"].as_str().unwrap().to_string(),
344 sub_mchid: e["sub_mchid"].as_str().unwrap().to_string(),
345 sp_appid: e["sp_appid"].as_str().unwrap().to_string(),
346 transaction_id: e["transaction_id"].to_string(),
347 success_time: PayNotify::success_time(e["success_time"].as_str().unwrap_or("")),
348 sp_openid: e["payer"]["sp_openid"].to_string(),
349 sub_openid: e["payer"]["sub_openid"].to_string(),
350 total: e["amount"]["total"].as_f64().unwrap_or(0.0) / 100.0,
351 currency: e["amount"]["currency"].to_string(),
352 payer_total: e["amount"]["payer_total"].as_f64().unwrap_or(0.0) / 100.0,
353 payer_currency: e["amount"]["payer_currency"].to_string(),
354 trade_state: TradeState::from(e["trade_state"].as_str().unwrap()),
355 };
356 Ok(res.json())
357 }
358 Err(e) => Err(e),
359 }
360 }
361
362 fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
363 let nonce_str: String = rand::rng().sample_iter(&Alphanumeric).take(32).map(char::from).collect();
364 let mut body = object! {
365 "appid": self.appid.clone(),
366 "mch_id"=> self.sp_mchid.clone(),
367 "sub_mch_id"=> sub_mchid,
368 "nonce_str"=>nonce_str,
369 "out_trade_no"=>out_trade_no
370 };
371 body["sign"] = self.sign_v2(body.clone())?.into();
372 let mut xml = vec!["<xml>".to_owned()];
373 for (key, value) in body.entries() {
374 let t = format!("<{}>{}</{00}>", key, value.clone().clone());
375 xml.push(t);
376 }
377 xml.push("</xml>".to_owned());
378 let xml = xml.join("");
379 let mut http = br_reqwest::Client::new();
380 match http.post("https://api.mch.weixin.qq.com/pay/orderquery".to_string().as_str()).header("Content-Type", "application/xml").raw_xml(xml.into()).send()?.xml() {
381 Ok(e) => {
382 if e.has_key("result_code") && e["result_code"] != "SUCCESS" {
383 error!("pay_micropay_query: {e:#}");
384 return Err(e["return_msg"].to_string());
385 }
386
387 let res = PayNotify {
388 trade_type: TradeType::from(e["trade_type"].to_string().as_str()),
389 out_trade_no: e["out_trade_no"].as_str().unwrap().to_string(),
390 sp_mchid: e["mch_id"].as_str().unwrap().to_string(),
391 sub_mchid: e["sub_mch_id"].as_str().unwrap().to_string(),
392 sp_appid: e["appid"].as_str().unwrap().to_string(),
393 transaction_id: e["transaction_id"].to_string(),
394 success_time: PayNotify::datetime_to_timestamp(e["time_end"].as_str().unwrap_or(""), "%Y%m%d%H%M%S"),
395 sp_openid: e["device_info"].to_string(),
396 sub_openid: e["openid"].to_string(),
397 total: e["total_fee"].to_string().parse::<f64>().unwrap_or(0.0) / 100.0,
398 currency: e["fee_type"].to_string(),
399 payer_total: e["cash_fee"].to_string().parse::<f64>().unwrap_or(0.0) / 100.0,
400 payer_currency: e["cash_fee_type"].to_string(),
401 trade_state: TradeState::from(e["trade_state"].as_str().unwrap()),
402 };
403 Ok(res.json())
404 }
405 Err(e) => Err(e),
406 }
407 }
408 fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
409 if self.apikey.is_empty() {
410 return Err("apikey 不能为空".to_string());
411 }
412 let key = Key::<Aes256Gcm>::from_slice(self.apikey.as_bytes());
413 let cipher = Aes256Gcm::new(key);
414 let nonce = Nonce::from_slice(nonce.as_bytes());
415 let data = match STANDARD.decode(ciphertext) {
416 Ok(e) => e,
417 Err(e) => return Err(format!("Invalid data received from API :{e}"))
418 };
419 let payload = Payload {
421 msg: &data,
422 aad: associated_data.as_bytes(),
423 };
424
425 let plaintext = match cipher.decrypt(nonce, payload) {
427 Ok(e) => e,
428 Err(e) => {
429 return Err(format!("解密 API:{e}"));
430 }
431 };
432 let rr = match String::from_utf8(plaintext) {
433 Ok(d) => d,
434 Err(_) => return Err("utf8 error".to_string())
435 };
436 let json = match json::parse(rr.as_str()) {
437 Ok(e) => e,
438 Err(_) => return Err("json error".to_string())
439 };
440 let res = PayNotify {
441 trade_type: TradeType::from(json["trade_type"].as_str().unwrap()),
442 out_trade_no: json["out_trade_no"].as_str().unwrap().to_string(),
443 sp_mchid: json["sp_mchid"].as_str().unwrap().to_string(),
444 sub_mchid: json["sub_mchid"].as_str().unwrap().to_string(),
445 sp_appid: json["sp_appid"].as_str().unwrap().to_string(),
446 transaction_id: json["transaction_id"].as_str().unwrap().to_string(),
447 success_time: PayNotify::success_time(json["success_time"].as_str().unwrap_or("")),
448 sp_openid: json["payer"]["sp_openid"].as_str().unwrap().to_string(),
449 sub_openid: json["payer"]["sub_openid"].as_str().unwrap().to_string(),
450 total: json["amount"]["total"].to_string().parse::<f64>().unwrap_or(0.0) / 100.0,
451 payer_total: json["amount"]["payer_total"].to_string().parse::<f64>().unwrap_or(0.0) / 100.0,
452 currency: json["amount"]["currency"].to_string(),
453 payer_currency: json["amount"]["payer_currency"].to_string(),
454 trade_state: TradeState::from(json["trade_state"].as_str().unwrap()),
455 };
456 Ok(res.json())
457 }
458
459 fn refund(
460 &mut self,
461 sub_mchid: &str,
462 out_trade_no: &str,
463 transaction_id: &str,
464 out_refund_no: &str,
465 amount: f64,
466 total: f64,
467 currency: &str,
468 ) -> Result<JsonValue, String> {
469 let url = "/v3/refund/domestic/refunds";
470
471 let refund = format!("{:.0}", amount * 100.0);
472 let total = format!("{:.0}", total * 100.0);
473
474 let body = object! {
475 "sub_mchid"=> sub_mchid,
476 "transaction_id"=>transaction_id,
477 "out_trade_no"=>out_trade_no,
478 "out_refund_no"=>out_refund_no,
479 "amount"=>object! {
480 refund: refund.parse::<i64>().unwrap(),
481 total: total.parse::<i64>().unwrap(),
482 currency:currency
483 }
484 };
485 match self.http(url, Method::POST, body) {
486 Ok(e) => {
487 if e.is_empty() {
488 return Err("已执行".to_string());
489 }
490 if e.has_key("message") {
491 return Err(e["message"].to_string());
492 }
493 let mut refund_time = 0.0;
494 if e.has_key("success_time") {
495 let success_time = e["success_time"].as_str().unwrap_or("").to_string();
496 if !success_time.is_empty() {
497 let datetime = DateTime::parse_from_rfc3339(success_time.as_str()).unwrap();
498 refund_time = datetime.timestamp() as f64;
499 }
500 }
501
502 let status = match e["status"].as_str().unwrap() {
503 "PROCESSING" => "退款中",
504 "SUCCESS" => "已退款",
505 _ => "无退款",
506 };
507 let info = object! {
508 refund_id: e["refund_id"].clone(),
509 user_received_account:e["user_received_account"].clone(),
510 status:status,
511 refund_time:refund_time,
512 out_refund_no: e["out_refund_no"].clone(),
513 };
514 Ok(info)
515 }
516 Err(e) => Err(e)
517 }
518 }
519
520 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> {
521 let refund = format!("{:.0}", amount * 100.0);
522 let total = format!("{:.0}", total * 100.0);
523
524
525 let nonce_str: String = rand::rng().sample_iter(&Alphanumeric).take(32).map(char::from).collect();
526 let mut body = object! {
527 "appid": self.appid.clone(),
528 "mch_id"=> self.sp_mchid.clone(),
529 "sub_mch_id"=> sub_mchid,
530 "nonce_str"=>nonce_str,
531 "out_trade_no"=>out_trade_no,
532 "transaction_id"=>transaction_id,
533 "out_refund_no"=>out_refund_no,
534 "total_fee"=>total,
535 "refund_fee"=>refund,
536 "refund_fee_type"=> currency,
537 "refund_desc"=>refund_text
538 };
539 body["sign"] = self.sign_v2(body.clone())?.into();
540 let mut xml = vec!["<xml>".to_owned()];
541 for (key, value) in body.entries() {
542 let t = format!("<{}>{}</{00}>", key, value.clone().clone());
543 xml.push(t);
544 }
545 xml.push("</xml>".to_owned());
546 let xml = xml.join("");
547 let mut http = br_reqwest::Client::new();
548 match http.post("https://api.mch.weixin.qq.com/secapi/pay/refund".to_string().as_str()).header("Content-Type", "application/xml").raw_xml(xml.into()).send()?.xml() {
549 Ok(e) => {
550 println!("{e:#}");
551 if e.is_empty() {
552 return Err("已执行".to_string());
553 }
554 if e.has_key("message") {
555 return Err(e["message"].to_string());
556 }
557 let mut refund_time = 0.0;
558 if e.has_key("success_time") {
559 let success_time = e["success_time"].as_str().unwrap_or("").to_string();
560 if !success_time.is_empty() {
561 let datetime = DateTime::parse_from_rfc3339(success_time.as_str()).unwrap();
562 refund_time = datetime.timestamp() as f64;
563 }
564 }
565
566 let status = match e["status"].as_str().unwrap() {
567 "PROCESSING" => "退款中",
568 "SUCCESS" => "已退款",
569 _ => "无退款",
570 };
571 let info = object! {
572 refund_id: e["refund_id"].clone(),
573 user_received_account:e["user_received_account"].clone(),
574 status:status,
575 refund_time:refund_time,
576 out_refund_no: e["out_refund_no"].clone(),
577 };
578 Ok(info)
579 }
580 Err(e) => Err(e),
581 }
582 }
583
584 fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
585 if self.apikey.is_empty() {
586 return Err("apikey 不能为空".to_string());
587 }
588 let key = Key::<Aes256Gcm>::from_slice(self.apikey.as_bytes());
589 let cipher = Aes256Gcm::new(key);
590 let nonce = Nonce::from_slice(nonce.as_bytes());
591 let data = match STANDARD.decode(ciphertext) {
592 Ok(e) => e,
593 Err(e) => return Err(format!("Invalid data received from API :{e}"))
594 };
595 let payload = Payload {
597 msg: &data,
598 aad: associated_data.as_bytes(),
599 };
600
601 let plaintext = match cipher.decrypt(nonce, payload) {
603 Ok(e) => e,
604 Err(e) => {
605 return Err(format!("解密 API:{e}"));
606 }
607 };
608 let rr = match String::from_utf8(plaintext) {
609 Ok(d) => d,
610 Err(_) => return Err("utf8 error".to_string())
611 };
612 let json = match json::parse(rr.as_str()) {
613 Ok(e) => e,
614 Err(_) => return Err("json error".to_string())
615 };
616 let res = RefundNotify {
617 out_trade_no: json["out_trade_no"].to_string(),
618 refund_no: json["out_refund_no"].to_string(),
619 refund_id: json["refund_id"].to_string(),
620 sp_mchid: json["sp_mchid"].as_str().unwrap().to_string(),
621 sub_mchid: json["sub_mchid"].as_str().unwrap().to_string(),
622 transaction_id: json["transaction_id"].as_str().unwrap().to_string(),
623 success_time: PayNotify::success_time(json["success_time"].as_str().unwrap_or("")),
624 total: json["amount"]["total"].as_f64().unwrap_or(0.0) / 100.0,
625 refund: json["amount"]["refund"].as_f64().unwrap_or(0.0) / 100.0,
626 payer_total: json["amount"]["payer_total"].as_f64().unwrap() / 100.0,
627 payer_refund: json["amount"]["payer_refund"].as_f64().unwrap() / 100.0,
628 status: RefundStatus::from(json["refund_status"].as_str().unwrap()),
629 };
630 Ok(res.json())
631 }
632
633 fn refund_query(&mut self, _trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
634 let url = format!("/v3/refund/domestic/refunds/{out_refund_no}?sub_mchid={sub_mchid}");
635 match self.http(&url, Method::GET, "".into()) {
636 Ok(e) => {
637 if e.is_empty() {
638 return Err("已执行".to_string());
639 }
640 if e.has_key("message") {
641 return Err(e["message"].to_string());
642 }
643
644
645 let res = RefundNotify {
646 out_trade_no: e["out_trade_no"].to_string(),
647 refund_no: e["out_refund_no"].to_string(),
648 sp_mchid: "".to_string(),
649 sub_mchid: sub_mchid.to_string(),
650 transaction_id: e["transaction_id"].to_string(),
651 refund_id: e["refund_id"].to_string(),
652 success_time: PayNotify::success_time(e["success_time"].as_str().unwrap_or("")),
653 total: e["amount"]["total"].to_string().parse::<f64>().unwrap(),
654 payer_total: e["amount"]["total"].to_string().parse::<f64>().unwrap(),
655 refund: e["amount"]["refund"].to_string().parse::<f64>().unwrap(),
656 payer_refund: e["amount"]["refund"].to_string().parse::<f64>().unwrap(),
657 status: RefundStatus::from(e["status"].as_str().unwrap()),
658 };
659
660 Ok(res.json())
661 }
662 Err(e) => Err(e),
663 }
664 }
665
666 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> {
667 let contact_info_data = object! {
668 contact_type:contact_info["contact_type"].clone(),
670 contact_name:contact_info["contact_name"].clone(),
671 };
672
673 let body = object! {
674 business_code:business_code,
675 contact_info:contact_info_data
676 };
677 println!("{body:#}");
678 match self.http("/v3/applyment4sub/applyment/", Method::POST, body) {
679 Ok(e) => {
680 println!("{e:#}");
681 if e.is_empty() {
682 return Err("已执行".to_string());
683 }
684 if e.has_key("message") {
685 return Err(e["message"].to_string());
686 }
687 Ok(e)
688 }
689 Err(e) => Err(e),
690 }
691 }
692}