br_pay/
ccbc.rs

1use chrono::Local;
2use json::{object, JsonValue};
3use log::{debug, warn};
4use xmltree::Element;
5use crate::{PayMode, PayNotify, RefundNotify, RefundStatus, TradeState, TradeType, Types};
6
7/// 建设银行
8#[derive(Clone, Debug)]
9pub struct Ccbc {
10    /// 调试
11    pub debug: bool,
12    /// 微信小程序APPID
13    pub appid: String,
14    /// 微信公众号APPID
15    pub appid_subscribe: String,
16    /// 登录密码
17    pub pass: String,
18    /// 银行服务商号
19    pub sp_mchid: String,
20    /// 通知地址
21    pub notify_url: String,
22    /// 商户柜台代码
23    pub posid: String,
24    /// 分行代码
25    pub branchid: String,
26    /// 二级商户公钥
27    pub public_key: String,
28    pub client_ip: String,
29    /// 微信服务商号
30    pub wechat_mchid: String,
31    /// 重试次
32    pub retry: usize,
33}
34
35impl Ccbc {
36    pub fn http(&mut self, url: &str, mut body: JsonValue) -> Result<JsonValue, String> {
37        let mut mac = vec![];
38        let mut path = vec![];
39        let fields = ["MAC"];
40        for (key, value) in body.entries() {
41            if value.is_empty() && fields.contains(&key) {
42                continue;
43            }
44            if key != "PUB" {
45                path.push(format!("{key}={value}"));
46            }
47            mac.push(format!("{key}={value}"));
48        }
49
50
51        let mac_text = mac.join("&");
52        let path = path.join("&");
53        if self.debug {
54            debug!("MERCHANTID=105000373721227&POSID=091864103&BRANCHID=530000000&ORDERID=96398&PAYMENT=0.01&CURCODE=01&TXCODE=530550&REMARK1=&REMARK2=&RETURNTYPE=3&TIMEOUT=&PUB=93bd9affe92c29dea12e5d79020111");
55            debug!("{mac_text:#}");
56        }
57        body["MAC"] = br_crypto::md5::encrypt_hex(mac_text.as_bytes()).into();
58        if self.debug {
59            debug!("{body:#}");
60        }
61        body.remove("PUB");
62        let mac = format!("{}&MAC={}", path, body["MAC"]);
63        if self.debug {
64            debug!("{mac:#}");
65        }
66        let urls = format!("{url}&{mac}");
67        if self.debug {
68            debug!("{urls:#}");
69        }
70        let mut http = br_reqwest::Client::new();
71
72        let res = match http.post(urls.as_str()).raw_json(body.clone()).send() {
73            Ok(e) => e,
74            Err(e) => {
75                if self.retry > 2 {
76                    return Err(e.to_string());
77                }
78                self.retry += 1;
79                warn!("建行接口重试: {}", self.retry);
80                body.remove("MAC");
81                let res = self.http(url, body.clone())?;
82                return Ok(res);
83            }
84        };
85        let res = res.body().to_string();
86        match json::parse(&res) {
87            Ok(e) => Ok(e),
88            Err(_) => Err(res)
89        }
90    }
91    pub fn http_alipay(&mut self, url: &str, mut body: JsonValue) -> Result<JsonValue, String> {
92        let mut mac = vec![];
93        let mut path = vec![];
94        let fields = ["MAC", "SUBJECT", "AREA_INFO"];
95        for (key, value) in body.entries() {
96            if value.is_empty() && fields.contains(&key) {
97                continue;
98            }
99            if fields.contains(&key) {
100                continue;
101            }
102            if key != "PUB" {
103                path.push(format!("{key}={value}"));
104            }
105            mac.push(format!("{key}={value}"));
106        }
107
108
109        let mac_text = mac.join("&");
110        let path = path.join("&");
111        body["MAC"] = br_crypto::md5::encrypt_hex(mac_text.as_bytes()).into();
112        body.remove("PUB");
113        let mac = format!("{}&MAC={}", path, body["MAC"]);
114
115        let urls = format!("{url}&{mac}");
116
117        let mut http = br_reqwest::Client::new();
118        let res = match http.post(urls.as_str()).raw_json(body.clone()).send() {
119            Ok(e) => e,
120            Err(e) => {
121                if self.retry > 2 {
122                    return Err(e.to_string());
123                }
124                self.retry += 1;
125                warn!("建行接口重试: {}", self.retry);
126                body.remove("MAC");
127                let res = self.http_alipay(url, body.clone())?;
128                return Ok(res);
129            }
130        };
131        let res = res.body().to_string();
132        match json::parse(&res) {
133            Ok(e) => Ok(e),
134            Err(_) => Err(res)
135        }
136    }
137    fn escape_unicode(&mut self, s: &str) -> String {
138        s.chars().map(|c| {
139            if c.is_ascii() {
140                c.to_string()
141            } else {
142                format!("%u{:04X}", c as u32)
143            }
144        }).collect::<String>()
145    }
146    fn _unescape_unicode(&mut self, s: &str) -> String {
147        let mut output = String::new();
148        let mut chars = s.chars().peekable();
149        while let Some(c) = chars.next() {
150            if c == '%' && chars.peek() == Some(&'u') {
151                chars.next(); // consume 'u'
152                let codepoint: String = chars.by_ref().take(4).collect();
153                if let Ok(value) = u32::from_str_radix(&codepoint, 16) {
154                    if let Some(ch) = std::char::from_u32(value) {
155                        output.push(ch);
156                    }
157                }
158            } else {
159                output.push(c);
160            }
161        }
162        output
163    }
164    pub fn http_q(&mut self, url: &str, mut body: JsonValue) -> Result<JsonValue, String> {
165        let mut path = vec![];
166        let fields = ["MAC"];
167        for (key, value) in body.entries() {
168            if value.is_empty() && fields.contains(&key) {
169                continue;
170            }
171            if key.contains("QUPWD") {
172                path.push(format!("{key}="));
173                continue;
174            }
175            path.push(format!("{key}={value}"));
176        }
177
178        let mac = path.join("&");
179        body["MAC"] = br_crypto::md5::encrypt_hex(mac.as_bytes()).into();
180
181        let mut map = vec![];
182        for (key, value) in body.entries() {
183            map.push((key, value.to_string()));
184        }
185
186        let mut http = br_reqwest::Client::new();
187
188        http.header("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36");
189
190        let res = match http.post(url).form_urlencoded(body.clone()).send() {
191            Ok(d) => d,
192            Err(e) => {
193                if self.retry > 2 {
194                    return Err(e.to_string());
195                }
196                self.retry += 1;
197                warn!("建行查询接口重试: {}", self.retry);
198                body.remove("MAC");
199                let res = self.http_q(url, body.clone())?;
200                return Ok(res);
201            }
202        };
203        let res = res.body().to_string().trim().to_string();
204        match Element::parse(res.as_bytes()) {
205            Ok(e) => Ok(xml_element_to_json(&e)),
206            Err(e) => Err(e.to_string())
207        }
208    }
209
210    pub fn http_ccb_param(&mut self, url: &str, mut body: JsonValue) -> Result<JsonValue, String> {
211        println!("url: {url}");
212        println!("public_key: {}", self.public_key);
213
214
215        let mut mac = vec![];
216        let mut path = vec![];
217        let fields = ["MAC", "ccbParam"];
218        for (key, value) in body.entries() {
219            if value.is_empty() && fields.contains(&key) {
220                continue;
221            }
222            if fields.contains(&key) {
223                continue;
224            }
225            if key != "PUB" {
226                path.push(format!("{key}={value}"));
227            }
228            mac.push(format!("{key}={value}"));
229        }
230
231
232        let mac_text = mac.join("&");
233        println!("mac: {mac_text}");
234        // MERCHANTID=105000373721227&POSID=091864103&BRANCHID=530000000&MERFLAG=1&TERMNO1=&TERMNO2=&ORDERID=202508041754271606105000373721227&QRCODE=131318016110834439&AMOUNT=0.01&TXCODE=PAY100&PROINFO=单人&REMARK1=&REMARK2=&SUB_APPID=wx2408d13eefae86
235        // MERCHANTID=105910100190000&POSID=000000000&BRANCHID=610000000&MERFLAG=1&TERMNO1=&TERMNO2=&ORDERID=202508041754271433105000373721227&QRCODE=134737690209713400&AMOUNT=0.01&TXCODE=PAY100&PROINFO=&REMARK1=&REMARK2=&SMERID=&SMERNAME=&SMERTYPEID=&SMERTYPE=&TRADECODE=&TRADENAME=&SMEPROTYPE=&PRONAME=
236        let path = path.join("&");
237        body["MAC"] = br_crypto::md5::encrypt_hex(mac_text.as_bytes()).into();
238        body.remove("PUB");
239        let mac = format!("{}&MAC={}", path, body["MAC"]);
240
241        let urls = format!("{url}&{mac}");
242
243        let mut http = br_reqwest::Client::new();
244
245        let res = match http.post(urls.as_str()).raw_json(body.clone()).send() {
246            Ok(e) => e,
247            Err(e) => {
248                if self.retry > 2 {
249                    return Err(e.to_string());
250                }
251                self.retry += 1;
252                warn!("建行接口重试: {}", self.retry);
253                body.remove("MAC");
254                let res = self.http(url, body.clone())?;
255                return Ok(res);
256            }
257        };
258        let res = res.body().to_string();
259        match json::parse(&res) {
260            Ok(e) => Ok(e),
261            Err(_) => Err(res)
262        }
263    }
264}
265impl PayMode for Ccbc {
266    fn check(&mut self) -> Result<bool, String> {
267        todo!()
268    }
269
270    fn get_sub_mchid(&mut self, _sub_mchid: &str) -> Result<JsonValue, String> {
271        todo!()
272    }
273
274    fn config(&mut self) -> JsonValue {
275        todo!()
276    }
277
278
279    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> {
280        if self.public_key.is_empty() || self.public_key.len() < 30 {
281            return Err(String::from("Public key is empty"));
282        }
283        let pubtext = self.public_key[self.public_key.len() - 30..].to_string();
284
285        let url = match channel {
286            "wechat" => "https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6",
287            "alipay" => "https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6",
288            _ => return Err(format!("Invalid channel: {channel}")),
289        };
290
291        let body = match channel {
292            "wechat" => {
293                let mut body = object! {
294                    MERCHANTID:sub_mchid,
295                    POSID:self.posid.clone(),
296                    BRANCHID:self.branchid.clone(),
297                    ORDERID:out_trade_no,
298            PAYMENT:total_fee,
299            CURCODE:"01",
300            TXCODE:"530590",
301            REMARK1:"",
302            REMARK2:"",
303            TYPE:"1",
304            PUB:pubtext,
305            GATEWAY:"0",
306            CLIENTIP:self.client_ip.clone(),
307            REGINFO:"",
308                    PROINFO: self.escape_unicode(description),
309            REFERER:"",
310            TRADE_TYPE:"",
311                    SUB_APPID: "",
312                    SUB_OPENID:sp_openid,
313            MAC:"",
314        };
315                body["TRADE_TYPE"] = match types {
316                    Types::Jsapi => {
317                        body["SUB_APPID"] = self.appid_subscribe.clone().into();
318                        "JSAPI"
319                    }
320                    Types::MiniJsapi => {
321                        body["SUB_APPID"] = self.appid.clone().into();
322                        "MINIPRO"
323                    }
324                    _ => return Err(format!("Invalid types: {types:?}")),
325                }.into();
326
327                //body["WX_CHANNELID"] = self.sp_mchid.clone().into();
328
329                //body["SMERID"] = sub_mchid.into();
330                //body["SMERNAME"] = self.escape_unicode(self.smername.clone().as_str()).into();
331                //body["SMERTYPEID"] = self.smertypeid.clone().into();
332                //body["SMERTYPE"] = self.escape_unicode(self.smertype.clone().as_str()).into();
333
334                //body["SMERNAME"] = self.escape_unicode(self.smername.clone().as_str()).into();
335                //body["SMERTYPEID"] = 1.into();
336                //body["SMERTYPE"] = self.escape_unicode("宾馆餐娱类").into();
337
338                //body["TRADECODE"] = "交易类型代码".into();
339                //body["TRADENAME"] = self.escape_unicode("消费").into();
340                //body["SMEPROTYPE"] = "商品类别代码".into();
341                //body["PRONAME"] = self.escape_unicode("商品").into();
342                body
343            }
344            "alipay" => {
345                let body = match types {
346                    Types::MiniJsapi => object! {
347                        MERCHANTID:sub_mchid,
348                        POSID:self.posid.clone(),
349                        BRANCHID:self.branchid.clone(),
350                        ORDERID:out_trade_no,
351                        PAYMENT:total_fee,
352                        CURCODE:"01",
353                        TXCODE:"530591",
354                        TRADE_TYPE:"JSAPI",
355                        USERID:sp_openid,
356                        PUB:pubtext,
357                        MAC:""
358                    },
359                    Types::H5 => object! {
360                        BRANCHID:self.branchid.clone(),
361                        MERCHANTID:sub_mchid,
362                        POSID:self.posid.clone(),
363                        TXCODE:"ZFBWAP",
364                        ORDERID:out_trade_no,
365                        AMOUNT:total_fee,
366                        TIMEOUT:"",
367                        REMARK1:"",
368                        REMARK2:"",
369                        PUB:pubtext,
370                        MAC:"",
371                        SUBJECT:description,
372                        AREA_INFO:""
373                    },
374                    Types::Jsapi => object! {
375                        MERCHANTID:sub_mchid,
376                        POSID:self.posid.clone(),
377                        BRANCHID:self.branchid.clone(),
378                        ORDERID:out_trade_no,
379                        PAYMENT:total_fee,
380                        CURCODE:"01",
381                        TXCODE:"530550",
382                        REMARK1:"",
383                        REMARK2:"",
384                        RETURNTYPE:"3",
385                        TIMEOUT:"",
386                        PUB:pubtext,
387                        MAC:""
388                    },
389                    _ => return Err(format!("Invalid types: {types:?}")),
390                };
391                body
392            }
393            _ => return Err(format!("Invalid channel: {channel}")),
394        };
395        match (channel, types) {
396            ("wechat", Types::Jsapi | Types::MiniJsapi) => {
397                let res = self.http(url, body.clone())?;
398                if res.has_key("PAYURL") {
399                    let url = res["PAYURL"].to_string();
400                    let mut http = br_reqwest::Client::new();
401
402                    let re = match http.post(url.as_str()).send() {
403                        Ok(e) => e,
404                        Err(e) => {
405                            return Err(e.to_string());
406                        }
407                    };
408                    let re = re.body().to_string();
409                    let res = match json::parse(&re) {
410                        Ok(e) => e,
411                        Err(_) => return Err(re)
412                    };
413                    if res.has_key("ERRCODE") && res["ERRCODE"] != "000000" {
414                        return Err(format!("获取支付参数: 错误码: [{}] {} 失败", res["ERRCODE"], res["ERRMSG"]));
415                    }
416                    Ok(res)
417                } else {
418                    Err(res.to_string())
419                }
420            }
421            ("alipay", Types::MiniJsapi) => {
422                let res = self.http_alipay(url, body)?;
423                if res.has_key("PAYURL") {
424                    let url = res["PAYURL"].to_string();
425                    let mut http = br_reqwest::Client::new();
426
427                    let re = match http.post(url.as_str()).send() {
428                        Ok(e) => e,
429                        Err(e) => {
430                            return Err(e.to_string());
431                        }
432                    };
433                    let re = re.body().to_string();
434                    let res = match json::parse(&re) {
435                        Ok(e) => e,
436                        Err(_) => return Err(re)
437                    };
438                    if res.has_key("ERRCODE") && res["ERRCODE"] != "000000" {
439                        return Err(format!("获取支付参数: 错误码: [{}] {} 失败", res["ERRCODE"], res["ERRMSG"]));
440                    }
441                    Ok(res)
442                } else {
443                    Err(res.to_string())
444                }
445            }
446            ("alipay", Types::H5) => {
447                let res = self.http_alipay(url, body)?;
448                if res.has_key("ERRCODE") && res["ERRCODE"] != "000000" {
449                    return Err(format!("获取支付参数: 错误码: [{}] {} 失败", res["ERRCODE"], res["ERRMSG"]));
450                }
451                Ok(res["form_data"].clone())
452            }
453            ("alipay", Types::Jsapi) => {
454                let res = self.http(url, body)?;
455                if res.has_key("PAYURL") {
456                    let url = res["PAYURL"].to_string();
457                    if self.debug {
458                        debug!("{url:#}");
459                    }
460                    let mut http = br_reqwest::Client::new();
461
462                    let re = match http.post(url.as_str()).send() {
463                        Ok(e) => e,
464                        Err(e) => {
465                            return Err(e.to_string());
466                        }
467                    };
468                    let re = re.body().to_string();
469                    let res = match json::parse(&re) {
470                        Ok(e) => e,
471                        Err(_) => return Err(re)
472                    };
473                    if self.debug {
474                        debug!("{res:#}");
475                    }
476                    if res.has_key("ERRCODE") && res["ERRCODE"] != "000000" {
477                        return Err(format!("获取支付参数: 错误码: [{}] {} 失败", res["ERRCODE"], res["ERRMSG"]));
478                    }
479                    Ok(res["QRURL"].clone())
480                } else {
481                    Err(res.to_string())
482                }
483            }
484            _ => {
485                let res = self.http(url, body)?;
486                Ok(res)
487            }
488        }
489    }
490
491    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> {
492        let mut body = object! {
493            MERCHANTID:sub_mchid,
494            POSID:self.posid.clone(),
495            BRANCHID:self.branchid.clone(),
496            ccbParam:"",
497            MERFLAG:"1",
498            TERMNO1:"",
499            TERMNO2:"",
500            ORDERID:out_trade_no,
501            QRCODE:auth_code,
502            AMOUNT:total_fee,
503            TXCODE:"PAY100",
504            PROINFO:description,
505            REMARK1:"",
506            REMARK2:"",
507            SUB_APPID:"",
508        };
509        let url = "https://ebanking2.ccb.com.cn/CCBIS/B2CMainPlat_00_BEPAY";
510
511        match channel {
512            "wechat" => {
513                body["SUB_APPID"] = self.appid.clone().into();
514            }
515            "alipay" => {}
516            _ => return Err(format!("Invalid channel: {channel}")),
517        }
518        let res = self.http_ccb_param(url, body)?;
519        Ok(res)
520    }
521
522    fn close(&mut self, _out_trade_no: &str, _sub_mchid: &str) -> Result<JsonValue, String> {
523        Ok(true.into())
524    }
525
526    fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
527        let today = Local::now().date_naive();
528        let date_str = today.format("%Y%m%d").to_string();
529
530        let order_date = &out_trade_no[0..8];
531
532        let kind = if date_str == order_date {
533            0
534        } else {
535            1
536        };
537
538        let body = object! {
539            MERCHANTID:sub_mchid,
540            BRANCHID:self.branchid.clone(),
541            POSID:self.posid.clone(),
542            ORDERDATE:order_date,
543            BEGORDERTIME:"00:00:00",
544            ENDORDERTIME:"23:59:59",
545            ORDERID:out_trade_no,
546            QUPWD:self.pass.clone(),
547            TXCODE:"410408",
548            TYPE:"0",
549            KIND:kind,
550            STATUS:"1",
551            SEL_TYPE:"3",
552            PAGE:"1",
553            OPERATOR:"",
554            CHANNEL:"",
555            MAC:""
556        };
557        let res = self.http_q("https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain", body)?;
558        if res["RETURN_CODE"] != "000000" {
559            if res["RETURN_MSG"].eq("流水记录不存在") {
560                let res = PayNotify {
561                    trade_type: TradeType::None,
562                    out_trade_no: "".to_string(),
563                    sp_mchid: "".to_string(),
564                    sub_mchid: "".to_string(),
565                    sp_appid: "".to_string(),
566                    transaction_id: "".to_string(),
567                    success_time: 0,
568                    sp_openid: "".to_string(),
569                    sub_openid: "".to_string(),
570                    total: 0.0,
571                    payer_total: 0.0,
572                    currency: "".to_string(),
573                    payer_currency: "".to_string(),
574                    trade_state: TradeState::NOTPAY,
575                };
576                return Ok(res.json());
577            }
578            return Err(res["RETURN_MSG"].to_string());
579        }
580        let data = res["QUERYORDER"].clone();
581        let res = PayNotify {
582            trade_type: TradeType::None,
583            out_trade_no: data["ORDERID"].to_string(),
584            sp_mchid: "".to_string(),
585            sub_mchid: sub_mchid.to_string(),
586            sp_appid: "".to_string(),
587            transaction_id: data["ORDERID"].to_string(),
588            success_time: PayNotify::datetime_to_timestamp(data["ORDERDATE"].as_str().unwrap_or(""), "%Y%m%d%H%M%S"),
589            sp_openid: "".to_string(),
590            sub_openid: "".to_string(),
591            total: data["AMOUNT"].as_f64().unwrap_or(0.0),
592            currency: "CNY".to_string(),
593            payer_total: data["AMOUNT"].as_f64().unwrap_or(0.0),
594            payer_currency: "CNY".to_string(),
595            trade_state: TradeState::from(data["STATUS"].as_str().unwrap()),
596        };
597        Ok(res.json())
598    }
599
600    fn pay_micropay_query(&mut self, _out_trade_no: &str, _sub_mchid: &str) -> Result<JsonValue, String> {
601        Err("暂未开通".to_string())
602    }
603
604    fn pay_notify(&mut self, _nonce: &str, _ciphertext: &str, _associated_data: &str) -> Result<JsonValue, String> {
605        Err("暂未开通".to_string())
606    }
607
608    fn refund(&mut self, _sub_mchid: &str, _out_trade_no: &str, _transaction_id: &str, _out_refund_no: &str, _amount: f64, _total: f64, _currency: &str) -> Result<JsonValue, String> {
609        //
610        //let mut http = br_reqwest::Client::new();
611        //http.set_cert_p12("br-pay/examples/1822131-1.pfx", "1822131");
612        //let txt = fs::read_to_string("br-pay/examples/jsyh/退款/reund.xml").unwrap();
613        //http.post("https://merchant.ccb.com").form_urlencoded(object! {
614        //    requestXml:txt
615        //});
616        //let res = http.send()?;
617        Err("暂未开通".to_string())
618    }
619
620    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> {
621        Err("暂未开通".to_string())
622    }
623
624    fn refund_notify(&mut self, _nonce: &str, _ciphertext: &str, _associated_data: &str) -> Result<JsonValue, String> {
625        Err("暂未开通".to_string())
626    }
627
628    fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
629        let today = Local::now().date_naive();
630        let date_str = today.format("%Y%m%d").to_string();
631        let body = object! {
632            MERCHANTID:sub_mchid,
633            BRANCHID:self.branchid.clone(),
634            POSID:self.posid.clone(),
635            ORDERDATE:date_str,
636            BEGORDERTIME:"00:00:00",
637            ENDORDERTIME:"23:59:59",
638            ORDERID:out_refund_no,
639            QUPWD:self.pass.clone(),
640            TXCODE:"410408",
641            TYPE:"1",
642            KIND:"0",
643            STATUS:"1",
644            SEL_TYPE:"3",
645            PAGE:"1",
646            OPERATOR:"",
647            CHANNEL:"",
648            MAC:""
649        };
650        let res = self.http_q("https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain", body)?;
651        if res["RETURN_CODE"] != "000000" {
652            if res["RETURN_MSG"].eq("流水记录不存在") {
653                let res = PayNotify {
654                    trade_type: TradeType::None,
655                    out_trade_no: "".to_string(),
656                    sp_mchid: "".to_string(),
657                    sub_mchid: "".to_string(),
658                    sp_appid: "".to_string(),
659                    transaction_id: "".to_string(),
660                    success_time: 0,
661                    sp_openid: "".to_string(),
662                    sub_openid: "".to_string(),
663                    total: 0.0,
664                    payer_total: 0.0,
665                    currency: "".to_string(),
666                    payer_currency: "".to_string(),
667                    trade_state: TradeState::NOTPAY,
668                };
669                return Ok(res.json());
670            }
671            return Err(res["RETURN_MSG"].to_string());
672        }
673        println!("refund_query: {res:#}");
674        let data = res["QUERYORDER"].clone();
675
676        let res = RefundNotify {
677            out_trade_no: trade_no.to_string(),
678            refund_no: out_refund_no.to_string(),
679            sp_mchid: "".to_string(),
680            sub_mchid: sub_mchid.to_string(),
681            transaction_id: data["ORDERID"].to_string(),
682            refund_id: data["refund_id"].to_string(),
683            success_time: PayNotify::datetime_to_timestamp(data["ORDERDATE"].as_str().unwrap_or(""), "%Y%m%d%H%M%S"),
684            total: data["AMOUNT"].as_f64().unwrap_or(0.0),
685            payer_total: data["amount"]["total"].to_string().parse::<f64>().unwrap(),
686            refund: data["amount"]["refund"].to_string().parse::<f64>().unwrap(),
687            payer_refund: data["amount"]["refund"].to_string().parse::<f64>().unwrap(),
688            status: RefundStatus::from(data["STATUS"].as_str().unwrap()),
689        };
690
691        Ok(res.json())
692    }
693
694    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> {
695        todo!()
696    }
697}
698
699fn xml_element_to_json(elem: &Element) -> JsonValue {
700    let mut obj = object! {};
701
702    for child in &elem.children {
703        if let xmltree::XMLNode::Element(e) = child {
704            obj[e.name.clone()] = xml_element_to_json(e);
705        }
706    }
707
708    match elem.get_text() {
709        None => obj,
710        Some(text) => JsonValue::from(text.to_string()),
711    }
712}