一、業務背景 微信自動退款串接基於酷客多小程式商城系統,為方便財務人員進行訂單退款而開發,將酷客多小程式系統財務退款流程和微信退款系統打通。實現一個系統管理運營。 二、業務流程設計 1.退款單狀態:待退款、退款中、退款完成、自動退款失敗等 2.由於微信申請退款介面接受請求後不會立即進行退款處理,微信 ...
一、業務背景
微信自動退款串接基於酷客多小程式商城系統,為方便財務人員進行訂單退款而開發,將酷客多小程式系統財務退款流程和微信退款系統打通。實現一個系統管理運營。
二、業務流程設計
1.退款單狀態:待退款、退款中、退款完成、自動退款失敗等
2.由於微信申請退款介面接受請求後不會立即進行退款處理,微信此處有延遲,因此在實際業務串接中,不能依據申請退款介面調用是否成功來修改業務系統中退款單的狀態;必須以微信退款通知的狀態或者自行調用查看微信退款狀態介面的狀態為準
微信退款串接流程圖,見下圖
三、微信後臺配置
1.申請退款介面調用需要微信證書;因此需要先到微信商戶平臺——賬戶中心——API安全中下載證書;
此證書是用於調用申請退款介面時使用;需要先安裝到系統中
2.到微信商戶平臺開啟退款通知配置;並配置退款通知介面地址;此地址用於接受退款結果通知的
四、微信申請退款介面串接常見問題
1.參數錯誤問題,介面要求商戶訂單號、退款單號、退款金額、訂單金額為業務要求必傳欄位;
a)其中商戶訂單號為你要退的訂單支付時傳入的訂單號
b)退款單號為當前微信商戶號下唯一編號,按照微信官方文檔生成規則即可
c)指當前訂單多次退款金額合計不得超過訂單金額
2.申請退款介面調用前;安裝微信API證書,調用時需帶著證書請求
3.接受不到微信退款通知信息問題
a)檢查是否在微信商戶平臺配置開啟退款通知,並配置通知路徑
b)檢查接受通知介面是否正式訪問
c)通知介面必須是部署在伺服器,且外網正常訪問得
4.接受微信退款結果通知介面;無法解密參數問題
微信公佈解密步驟如下:
(1)對加密串A做base64解碼,得到加密串B
(2)對商戶key做md5,得到32位小寫key* ( key設置路徑:微信商戶平臺(pay.weixin.qq.com)-->賬戶設置-->API安全-->密鑰設置 )
(3)用key*對加密串B做AES-256-ECB解密(PKCS7Padding)
實際串接時註意
a) 微信通知信息數據格式採用xml;從xml節點中取出加密數據時 ,第一步用base64解碼步驟省略,因為從xml節點取出得數據已是base64解碼過的字元串了
b)商戶key做md5加密;此處註意商戶key指得再微信商戶平臺配置得32商戶密鑰;md5加密也一定註意是32位小寫;
五、關鍵代碼塊
1.微信官方未提供得代碼; 解密微信退款通知信息方法
/// <summary>
/// 解密微信支付退款結果通知
/// </summary>
/// <param name="s">要解密字元串</param>
/// <param name="shkey">商戶key</param>
/// <returns></returns>
public static string DecodeReqInfo(string s, string
shkey)
{
string result = null;
string key =
System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(shkey,
"md5").ToLower(); //32位小寫md5加密
result =
DecodeAES256ECB(s, key);
return result ;
}
/// <summary>
/// AES-256-ECB字元解密
/// </summary>
/// <param name="s">要解密字元串</param>
/// <param name="key">密鑰</param>
/// <returns></returns>
public static string DecodeAES256ECB(string s,
string key)
{
string result = null;
try
{
byte[] keyArray =
UTF8Encoding.UTF8.GetBytes(key);
byte[] toEncryptArray =
Convert.FromBase64String(s);
RijndaelManaged rDel =
new RijndaelManaged();
rDel.Key = keyArray;
rDel.Mode =
CipherMode.ECB;
rDel.Padding =
PaddingMode.PKCS7;
ICryptoTransform
cTransform = rDel.CreateDecryptor();
byte[] resultArray =
cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
result = UTF8Encoding.UTF8.GetString(resultArray);
}
catch { }
return result ;
}
2.微信退款介面串接代碼段, 此代碼是按照微信官方提供demo修改使用;
/// <summary>
/// 微信退款介面
/// </summary>
/// <param
name="transaction_id"></param>
/// <param name="out_trade_no">訂單號</param>
/// <param name="out_refund_no">退款單號</param>
/// <param name="total_fee">總金額</param>
/// <param name="refund_fee">退款金額</param>
/// <param name="wxPayConfig">微信配置參數類</param>
/// <returns></returns>
public static string Run(string transaction_id,
string out_trade_no, string out_refund_no, string total_fee, string refund_fee,
WxPayConfig wxPayConfig)
{
Log.Info("Refund",
"Refund is processing...");
WxPayData data = new WxPayData();
if
(!string.IsNullOrEmpty(transaction_id))//微信訂單號存在的條件下,則已微信訂單號為準
{
data.SetValue("transaction_id", transaction_id);
}
else//微信訂單號不存在,才根據商戶訂單號去退款
{
data.SetValue("out_trade_no", out_trade_no);
}
data.SetValue("total_fee",
total_fee);//訂單總金額
data.SetValue("refund_fee",
refund_fee);//退款金額
data.SetValue("out_refund_no", out_refund_no);//隨機生成商戶退款單號
data.SetValue("op_user_id",
wxPayConfig.MCHID);//操作員,預設為商戶號
WxPayData result = Refund(data,
wxPayConfig);//提交退款申請給API,接收返回數據
Log.Info("Refund",
"Refund process complete, result : " + result.ToXml());
return result.ToPrintStr();
}
/**
*
* 申請退款
* @param WxPayData inputObj 提交給申請退款API的參數
* @param int timeOut 超時時間
* @throws WxPayException
* @return 成功時返回介面調用結果,其他拋異常
*/
public static WxPayData Refund(WxPayData inputObj,WxPayConfig
wxConfig ,int timeOut = 6)
{
string url =
"https://api.mch.weixin.qq.com/secapi/pay/refund";
//檢測必填參數
if
(!inputObj.IsSet("out_trade_no") &&
!inputObj.IsSet("transaction_id"))
{
throw new
WxPayException("退款申請介面中,out_trade_no、transaction_id至少填一個!");
}
else if
(!inputObj.IsSet("out_refund_no"))
{
throw new
WxPayException("退款申請介面中,缺少必填參數out_refund_no!");
}
else if
(!inputObj.IsSet("total_fee"))
{
throw new
WxPayException("退款申請介面中,缺少必填參數total_fee!");
}
else if
(!inputObj.IsSet("refund_fee"))
{
throw new
WxPayException("退款申請介面中,缺少必填參數refund_fee!");
}
else if
(!inputObj.IsSet("op_user_id"))
{
throw new
WxPayException("退款申請介面中,缺少必填參數op_user_id!");
}
inputObj.SetValue("appid",
wxConfig.APPID);//公眾賬號ID
inputObj.SetValue("mch_id",
wxConfig.MCHID);//商戶號
inputObj.SetValue("nonce_str",
Guid.NewGuid().ToString().Replace("-", ""));//隨機字元串
inputObj.SetValue("sign",
inputObj.MakeSign(wxConfig));//簽名
string xml = inputObj.ToXml();
var start = DateTime.Now;
Log.Debug("WxPayApi",
"Refund request : " + xml);
string response =
HttpService.Post(xml, url, true, timeOut, wxConfig);//調用HTTP通信介面提交數據到API
Log.Debug("WxPayApi",
"Refund response : " + response);
var end = DateTime.Now;
int timeCost = (int)((end -
start).TotalMilliseconds);//獲得介面耗時
//將xml格式的結果轉換為對象以返回
WxPayData result = new WxPayData();
result.FromXml(response, wxConfig);
//ReportCostTime(url, timeCost,
result, wxConfig);//測速上報
return result;
}
/// <summary>
/// 微信支付協議介面數據類,所有的API介面通信都依賴這個數據結構,
/// 在調用介面之前先填充各個欄位的值,然後進行介面通信,
/// 這樣設計的好處是可擴展性強,用戶可隨意對協議進行更改而不用重新設計數據結構,
/// 還可以隨意組合出不同的協議數據包,不用為每個協議設計一個數據包結構
/// </summary>
public class WxPayData
{
public WxPayData()
{
}
//採用排序的Dictionary的好處是方便對數據包進行簽名,不用再簽名之前再做一次排序
private SortedDictionary<string, object>
m_values = new SortedDictionary<string, object>();
/**
* 設置某個欄位的值
* @param key 欄位名
* @param value 欄位值
*/
public void SetValue(string key, object value)
{
m_values[key] = value;
}
/**
* 根據欄位名獲取某個欄位的值
* @param key 欄位名
* @return key對應的欄位值
*/
public object GetValue(string key)
{
object o = null;
m_values.TryGetValue(key, out o);
return o;
}
/**
* 判斷某個欄位是否已設置
* @param key 欄位名
* @return 若欄位key已被設置,則返回true,否則返回false
*/
public bool IsSet(string key)
{
object o = null;
m_values.TryGetValue(key, out o);
if (null != o)
return true;
else
return false;
}
/**
* @將Dictionary轉成xml
* @return 經轉換得到的xml串
* @throws WxPayException
**/
public string ToXml()
{
//數據為空時不能轉化為xml格式
if (0 == m_values.Count)
{
Log.Error(this.GetType().ToString(), "WxPayData數據為空!");
throw new
WxPayException("WxPayData數據為空!");
}
string xml = "<xml>";
foreach (KeyValuePair<string,
object> pair in m_values)
{
//欄位值不能為null,會影響後續流程
if (pair.Value == null)
{
Log.Error(this.GetType().ToString(), "WxPayData內部含有值為null的欄位!");
throw new
WxPayException("WxPayData內部含有值為null的欄位!");
}
if
(pair.Value.GetType() == typeof(int))
{
xml +=
"<" + pair.Key + ">" + pair.Value + "</"
+ pair.Key + ">";
}
else if
(pair.Value.GetType() == typeof(string))
{
xml +=
"<" + pair.Key + ">" + "<![CDATA[" +
pair.Value + "]]></" + pair.Key + ">";
}
else//除了string和int類型不能含有其他數據類型
{
Log.Error(this.GetType().ToString(), "WxPayData欄位數據類型錯誤!");
throw new
WxPayException("WxPayData欄位數據類型錯誤!");
}
}
xml += "</xml>";
return xml;
}
/**
* @將xml轉為WxPayData對象並返回對象內部的數據
* @param string 待轉換的xml串
* @return 經轉換得到的Dictionary
* @throws WxPayException
*/
public SortedDictionary<string, object>
FromXml(string xml, WxPayConfig wxConfig)
{
if (string.IsNullOrEmpty(xml))
{
Log.Error(this.GetType().ToString(), "將空的xml串轉換為WxPayData不合法!");
throw new
WxPayException("將空的xml串轉換為WxPayData不合法!");
}
XmlDocument xmlDoc = new
XmlDocument();
xmlDoc.LoadXml(xml);
XmlNode xmlNode =
xmlDoc.FirstChild;//獲取到根節點<xml>
XmlNodeList nodes =
xmlNode.ChildNodes;
foreach (XmlNode xn in nodes)
{
XmlElement xe =
(XmlElement)xn;
m_values[xe.Name] =
xe.InnerText;//獲取xml的鍵值對到WxPayData內部的數據中
}
try
{
//2015-06-29 錯誤是沒有簽名
if(m_values["return_code"]
!= "SUCCESS")
{
return
m_values;
}
CheckSign(wxConfig);//驗證簽名,不通過會拋異常
}
catch(WxPayException ex)
{
throw new
WxPayException(ex.Message);
}
return m_values;
}
/**
* @Dictionary格式轉化成url參數格式
* @ return url格式串, 該串不包含sign欄位值
*/
public string ToUrl()
{
string buff = "";
foreach (KeyValuePair<string,
object> pair in m_values)
{
if (pair.Value == null)
{
Log.Error(this.GetType().ToString(), "WxPayData內部含有值為null的欄位!");
throw new
WxPayException("WxPayData內部含有值為null的欄位!");
}
if (pair.Key !=
"sign" && pair.Value.ToString() != "")
{
buff +=
pair.Key + "=" + pair.Value + "&";
}
}
buff = buff.Trim('&');
return buff;
}
/**
* @Dictionary格式化成Json
* @return json串數據
*/
public string ToJson()
{
string jsonStr =
JsonMapper.ToJson(m_values);
return jsonStr;
}
/**
* @values格式化成能在Web頁面上顯示的結果(因為web頁面上不能直接輸出xml格式的字元串)
*/
public string ToPrintStr()
{
string str = "";
foreach (KeyValuePair<string,
object> pair in m_values)
{
if (pair.Value == null)
{
Log.Error(this.GetType().ToString(), "WxPayData內部含有值為null的欄位!");
throw new
WxPayException("WxPayData內部含有值為null的欄位!");
}
str +=
string.Format("{0}={1}<br>", pair.Key, pair.Value.ToString());
}
Log.Debug(this.GetType().ToString(),
"Print in Web Page : " + str);
return str;
}
/**
* @生成簽名,詳見簽名生成演算法
* @return 簽名, sign欄位不參加簽名
*/
public string MakeSign(WxPayConfig wxConfig)
{
//轉url格式
string str = ToUrl();
//在string後加入API KEY
str += "&key=" +
wxConfig.KEY;
//MD5加密
var md5 = MD5.Create();
var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
var sb = new StringBuilder();
foreach (byte b in bs)
{
sb.Append(b.ToString("x2"));
}
//所有字元轉為大寫
return sb.ToString().ToUpper();
}
/**
*
* 檢測簽名是否正確
* 正確返回true,錯誤拋異常
*/
public bool CheckSign(WxPayConfig wxConfig)
{
//如果沒有設置簽名,則跳過檢測
if (!IsSet("sign"))
{
Log.Error(this.GetType().ToString(), "WxPayData簽名存在但不合法!");
throw new
WxPayException("WxPayData簽名存在但不合法!");
}
//如果設置了簽名但是簽名為空,則拋異常
else if(GetValue("sign") ==
null || GetValue("sign").ToString() == "")
{
Log.Error(this.GetType().ToString(),
"WxPayData簽名存在但不合法!");
throw new
WxPayException("WxPayData簽名存在但不合法!");
}
//獲取接收到的簽名
string return_sign =
GetValue("sign").ToString();
//在本地計算新的簽名
string cal_sign = MakeSign(wxConfig);
if (cal_sign == return_sign)
{
return true;
}
Log.Error(this.GetType().ToString(),
"WxPayData簽名驗證錯誤!");
throw new WxPayException("WxPayData簽名驗證錯誤!");
}
/**
* @獲取Dictionary
*/
public SortedDictionary<string, object>
GetValues()
{
return m_values;
}
}
///
<summary>
/// 微信支付協議介面數據類,所有的API介面通信都依賴這個數據結構,
/// 在調用介面之前先填充各個欄位的值,然後進行介面通信,
/// 這樣設計的好處是可擴展性強,用戶可隨意對協議進行更改而不用重新設計數據結構,
/// 還可以隨意組合出不同的協議數據包,不用為每個協議設計一個數據包結構
/// </summary>
public class WxPayData
{
public WxPayData()
{
}
//採用排序的Dictionary的好處是方便對數據包進行簽名,不用再簽名之前再做一次排序
private SortedDictionary<string, object>
m_values = new SortedDictionary<string, object>();
/**
* 設置某個欄位的值
* @param key 欄位名
* @param value 欄位值
*/
public void SetValue(string key, object value)
{
m_values[key] = value;
}
/**
* 根據欄位名獲取某個欄位的值
* @param key 欄位名
* @return key對應的欄位值
*/
public object GetValue(string key)
{
object o = null;
m_values.TryGetValue(key, out o);
return o;
}
/**
* 判斷某個欄位是否已設置
* @param key 欄位名
* @return 若欄位key已被設置,則返回true,否則返回false
*/
public bool IsSet(string key)
{
object o = null;
m_values.TryGetValue(key, out o);
if (null != o)
return true;
else
return false;
}
/**
* @將Dictionary轉成xml
* @return 經轉換得到的xml串
* @throws WxPayException
**/
public string ToXml()
{
//數據為空時不能轉化為xml格式
if (0 == m_values.Count)
{
Log.Error(this.GetType().ToString(), "WxPayData數據為空!");
throw new WxPayException("WxPayData數據為空!");
}
string xml = "<xml>";
foreach (KeyValuePair<string,
object> pair in m_values)
{
//欄位值不能為null,會影響後續流程
if (pair.Value == null)
{
Log.Error(this.GetType().ToString(), "WxPayData內部含有值為null的欄位!");
throw new
WxPayException("WxPayData內部含有值為null的欄位!");
}
if
(pair.Value.GetType() == typeof(int))
{
xml +=
"<" + pair.Key + ">" + pair.Value + "</"
+ pair.Key + ">";
}
else if
(pair.Value.GetType() == typeof(string))
{
xml +=
"<" + pair.Key + ">" + "<![CDATA[" +
pair.Value + "]]></" + pair.Key + ">";
}
else//除了string和int類型不能含有其他數據類型
{
Log.Error(this.GetType().ToString(), "WxPayData欄位數據類型錯誤!");
throw new
WxPayException("WxPayData欄位數據類型錯誤!");
}
}
xml += "</xml>";
return xml;
}
/**
* @將xml轉為WxPayData對象並返回對象內部的數據
* @param string 待轉換的xml串
* @return 經轉換得到的Dictionary
* @throws WxPayException
*/
public SortedDictionary<string, object>
FromXml(string xml, WxPayConfig wxConfig)
{
if (string.IsNullOrEmpty(xml))
{
Log.Error(this.GetType().ToString(), "將空的xml串轉換為WxPayData不合法!");
throw new
WxPayException("將空的xml串轉換為WxPayData不合法!");
}
XmlDocument xmlDoc = new
XmlDocument();
xmlDoc.LoadXml(xml);
XmlNode xmlNode =
xmlDoc.FirstChild;//獲取到根節點<xml>
XmlNodeList nodes =
xmlNode.ChildNodes;
foreach (XmlNode xn in nodes)
{
XmlElement xe =
(XmlElement)xn;
m_values[xe.Name] =
xe.InnerText;//獲取xml的鍵值對到WxPayData內部的數據中
}
try
{
//2015-06-29 錯誤是沒有簽名
if(m_values["return_code"]
!= "SUCCESS")
{
return
m_values;
}
CheckSign(wxConfig);//驗證簽名,不通過會拋異常
}
catch(WxPayException ex)
{
throw new
WxPayException(ex.Message);
}
return m_values;
}
/**
* @Dictionary格式轉化成url參數格式
* @ return url格式串, 該串不包含sign欄位值
*/
public string ToUrl()
{
string buff = "";
foreach (KeyValuePair<string,
object> pair in m_values)
{
if (pair.Value == null)
{
Log.Error(this.GetType().ToString(), "WxPayData內部含有值為null的欄位!");
throw new
WxPayException("WxPayData內部含有值為null的欄位!");
}
if (pair.Key !=
"sign" && pair.Value.ToString() != "")
{
buff +=
pair.Key + "=" + pair.Value + "&";
}
}
buff = buff.Trim('&');
return buff;
}
/**
* @Dictionary格式化成Json
* @return json串數據
*/
public string ToJson()
{
string jsonStr =
JsonMapper.ToJson(m_values);
return jsonStr;
}
/**
* @values格式化成能在Web頁面上顯示的結果(因為web頁面上不能直接輸出xml格式的字元串)
*/
public string ToPrintStr()
{
string str = "";
foreach (KeyValuePair<string,
object> pair in m_values)
{
if (pair.Value == null)
{
Log.Error(this.GetType().ToString(), "WxPayData內部含有值為null的欄位!");
throw new
WxPayException("WxPayData內部含有值為null的欄位!");
}
str += string.Format("{0}={1}<br>",
pair.Key, pair.Value.ToString());
}
Log.Debug(this.GetType().ToString(),
"Print in Web Page : " + str);
return str;
}
/**
* @生成簽名,詳見簽名生成演算法
* @return 簽名, sign欄位不參加簽名
*/
public string MakeSign(WxPayConfig wxConfig)
{
//轉url格式
string str = ToUrl();
//在string後加入API KEY
str += "&key=" +
wxConfig.KEY;
//MD5加密
var md5 = MD5.Create();
var bs =
md5.ComputeHash(Encoding.UTF8.GetBytes(str));
var sb = new StringBuilder();
foreach (byte b in bs)
{
sb.Append(b.ToString("x2"));
}
//所有字元轉為大寫
return sb.ToString().ToUpper();
}
/**
*
* 檢測簽名是否正確
* 正確返回true,錯誤拋異常
*/
public bool CheckSign(WxPayConfig wxConfig)
{
//如果沒有設置簽名,則跳過檢測
if (!IsSet("sign"))
{
Log.Error(this.GetType().ToString(), "WxPayData簽名存在但不合法!");
throw new
WxPayException("WxPayData簽名存在但不合法!");
}
//如果設置了簽名但是簽名為空,則拋異常
else if(GetValue("sign") ==
null || GetValue("sign").ToString() == "")
{
Log.Error(this.GetType().ToString(), "WxPayData簽名存在但不合法!");
throw new
WxPayException("WxPayData簽名存在但不合法!");
}
//獲取接收到的簽名
string return_sign =
GetValue("sign").ToString();
//在本地計算新的簽名
string cal_sign = MakeSign(wxConfig);
if (cal_sign == return_sign)
{
return true;
}
Log.Error(this.GetType().ToString(),
"WxPayData簽名驗證錯誤!");
throw new
WxPayException("WxPayData簽名驗證錯誤!");
}
/**
* @獲取Dictionary
*/
public SortedDictionary<string, object>
GetValues()
{
return m_values;
}
}
/***
* 退款查詢完整業務流程邏輯
* @param refund_id 微信退款單號(優先使用)
* @param out_refund_no 商戶退款單號
* @param transaction_id 微信訂單號
* @param out_trade_no 商戶訂單號
* @return 退款查詢結果(xml格式)
*/
public static string Run(string refund_id, string
out_refund_no, string transaction_id, string out_trade_no, WxPayConfig
wxConfig)
{
Log.Info("RefundQuery",
"RefundQuery is processing...");
WxPayData data = new WxPayData();
if(!string.IsNullOrEmpty(refund_id))
{
data.SetValue("refund_id", refund_id);//微信退款單號,優先順序最高
}
else
if(!string.IsNullOrEmpty(out_refund_no))
{
data.SetValue("out_refund_no", out_refund_no);//商戶退款單號,優先順序第二
}
else
if(!string.IsNullOrEmpty(transaction_id))
{
data.SetValue("transaction_id", transaction_id);//微信訂單號,優先順序第三
}
else
{
data.SetValue("out_trade_no", out_trade_no);//商戶訂單號,優先順序最低
}
WxPayData result = RefundQuery(data,
wxConfig);//提交退款查詢給API,接收返回數據
Log.Info("RefundQuery",
"RefundQuery process complete, result : " + result.ToXml());
return result.ToPrintStr();
}
/**
*
* 查詢退款
* 提交退款申請後,通過該介面查詢退款狀態。退款有一定延時,
* 用零錢支付的退款20分鐘內到賬,銀行卡支付的退款3個工作日後重新查詢退款狀態。
* out_refund_no、out_trade_no、transaction_id、refund_id四個參數必填一個
* @param WxPayData
inputObj 提交給查詢退款API的參數
* @param int timeOut 介面超時時間
* @throws
WxPayException
* @return 成功時返回,其他拋異常
*/
public static
WxPayData RefundQuery(WxPayData inputObj,WxPayConfig wxConfig, int timeOut = 6)
{
string url =
"https://api.mch.weixin.qq.com/pay/refundquery";
//檢測必填參數
if(!inputObj.IsSet("out_refund_no") &&
!inputObj.IsSet("out_trade_no") &&
!inputObj.IsSet("transaction_id") &&
!inputObj.IsSet("refund_id"))
{
throw new
WxPayException("退款查詢介面中,out_refund_no、out_trade_no、transaction_id、refund_id四個參數必填一個!");
}
inputObj.SetValue("appid",
wxConfig.APPID);//公眾賬號ID
inputObj.SetValue("mch_id",
wxConfig.MCHID);//商戶號
inputObj.SetValue("nonce_str",GenerateNonceStr());//隨機字元串
inputObj.SetValue("sign",
inputObj.MakeSign(wxConfig));//簽名
string xml =
inputObj.ToXml();
var start =
DateTime.Now;//請求開始時間
Log.Debug("WxPayApi",
"RefundQuery request : " + xml);
string response = HttpService.Post(xml,
url, false, timeOut, wxConfig);//調用HTTP通信介面以提交數據到API
Log.Debug("WxPayApi",
"RefundQuery response : " + response);
var end = DateTime.Now;
int timeCost =
(int)((end-start).TotalMilliseconds);//獲得介面耗時
//將xml格式的結果轉換為對象以返回
WxPayData result
= new WxPayData();
result.FromXml(response, wxConfig);
ReportCostTime(url, timeCost, result,
wxConfig);//測速上報
return result;
}