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