br_pay/
wechat.rs

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    /// 服务商APPID
14    pub appid: String,
15    /// 密钥
16    pub secret: String,
17    /// 服务商户号
18    pub sp_mchid: String,
19    /// 证书号
20    pub serial_no: String,
21    /// API证书私钥
22    pub app_private: String,
23    /// APIv3密钥
24    pub apikey: String,
25    /// APIv2密钥
26    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(); // 秒级时间戳
81        let random_string: String = rng().sample_iter(&Alphanumeric) // 生成随机字母+数字
82                                         .take(10) // 指定长度
83                                         .map(char::from).collect();
84
85        let sign_txt = format!("{method}\n{url}\n{timestamp}\n{random_string}\n{body}\n");
86        // 加载 RSA 私钥
87        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        // 创建签名器
100        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        // 输入待签名数据
107        match signer.update(sign_txt.as_bytes()) {
108            Ok(_) => {}
109            Err(e) => {
110                return Err(e.to_string())
111            }
112        };
113        // 生成签名
114        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(); // 秒级时间戳
131        let random_string: String = rng().sample_iter(&Alphanumeric) // 生成随机字母+数字
132                                         .take(10) // 指定长度
133                                         .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        // 加载 RSA 私钥
141        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        // 创建签名器
154        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        // 输入待签名数据
161        match signer.update(sign_txt.as_bytes()) {
162            Ok(_) => {}
163            Err(e) => {
164                return Err(e.to_string())
165            }
166        };
167        // 生成签名
168        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        // 组合 Payload(带 aad)
415        let payload = Payload {
416            msg: &data,
417            aad: associated_data.as_bytes(),
418        };
419
420        // 解密
421        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        // 组合 Payload(带 aad)
591        let payload = Payload {
592            msg: &data,
593            aad: associated_data.as_bytes(),
594        };
595
596        // 解密
597        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            // 超级管理员类型 LEGAL: 经营者/法人 SUPER: 经办人
664            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}