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