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 sp_mchid: String,
17    /// 证书号
18    pub serial_no: String,
19    /// API证书私钥
20    pub app_private: String,
21    /// APIv3密钥
22    pub apikey: String,
23    /// APIv2密钥
24    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(); // 秒级时间戳
80        let random_string: String = rng().sample_iter(&Alphanumeric) // 生成随机字母+数字
81                                         .take(10) // 指定长度
82                                         .map(char::from).collect();
83
84        let sign_txt = format!("{method}\n{url}\n{timestamp}\n{random_string}\n{body}\n");
85        // 加载 RSA 私钥
86        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        // 创建签名器
99        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        // 输入待签名数据
106        match signer.update(sign_txt.as_bytes()) {
107            Ok(_) => {}
108            Err(e) => {
109                return Err(e.to_string())
110            }
111        };
112        // 生成签名
113        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(); // 秒级时间戳
130        let random_string: String = rng().sample_iter(&Alphanumeric) // 生成随机字母+数字
131                                         .take(10) // 指定长度
132                                         .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        // 加载 RSA 私钥
140        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        // 创建签名器
153        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        // 输入待签名数据
160        match signer.update(sign_txt.as_bytes()) {
161            Ok(_) => {}
162            Err(e) => {
163                return Err(e.to_string())
164            }
165        };
166        // 生成签名
167        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(); // 获取微秒级时间戳
187        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        // 组合 Payload(带 aad)
420        let payload = Payload {
421            msg: &data,
422            aad: associated_data.as_bytes(),
423        };
424
425        // 解密
426        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        // 组合 Payload(带 aad)
596        let payload = Payload {
597            msg: &data,
598            aad: associated_data.as_bytes(),
599        };
600
601        // 解密
602        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            // 超级管理员类型 LEGAL: 经营者/法人 SUPER: 经办人
669            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}