按照官方的文档,微信退款需要证书,这里证书部署不再赘述。C#开发随意放至一个目录即可。
**重点在参数和验证,在测试过程中遇到问题,每次提交都是返回缺少参数,后来仔细检查,原来appid写成了appId,这样也不行。因此,一定要细心,检查参数大小写。
证书验证提交: posturl为:https://api.mch.weixin.qq.com/secapi/pay/refund
public string PostWebRequest(string postUrl, string paramData)
{
string ret = string.Empty;
try
{
X509Certificate2 certificate = new X509Certificate2(HttpContext.Current.Server.MapPath("Cert")+"\\apiclient_cert.p12", Function.Mch_id, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet); //初始化证书
byte[] byteArray = Encoding.Default.GetBytes(paramData); //转化
HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(new Uri(postUrl));
webReq.Method = "POST";
webReq.ContentType = "application/x-www-form-urlencoded";
webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
webReq.ClientCertificates.Add(certificate); //添加证书
webReq.ContentLength = byteArray.Length;
Stream newStream = webReq.GetRequestStream(); //
newStream.Write(byteArray, 0, byteArray.Length);//写入参数
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;
HttpWebResponse response = (HttpWebResponse)webReq.GetResponse(); //这里报错
StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
ret = sr.ReadToEnd();
newStream.Close();
sr.Close();
response.Close();
newStream.Close();
}
catch (Exception ex)
{
return ex.Message;
}
return ret;
}
这里提交参数`
TenpayUtil tenpay = new TenpayUtil();
string appid = AppID; //商户appid
string mch_id = Mch_id;//商户号
string nonce_str = TenpayUtil.getNoncestr();
string out_trade_no = "2019100214***0836067";
string out_refund_no = System.DateTime.Now.ToString("yyyyMMddHHmmssfff");
int total_fee = int.Parse("1");
//order.total_fee = int.Parse("12") * 100;
int refund_fee = int.Parse("1");
string transaction_id = "420000040420191002*****62284";
SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
sParams.Add("appid", appid);
sParams.Add("mch_id",mch_id);
sParams.Add("nonce_str", nonce_str);
sParams.Add("out_trade_no", out_trade_no);
sParams.Add("out_refund_no", out_refund_no);
sParams.Add("refund_fee", refund_fee.ToString());
sParams.Add("total_fee", total_fee.ToString());
sParams.Add("transaction_id", transaction_id.ToString());
string sign = tenpay.getsign(sParams, Function.PayAPIKey);
sParams.Add("sign", sign.ToString());
//拼接成XML请求数据
StringBuilder sbPay = new StringBuilder();
foreach (KeyValuePair<string, string> k in sParams)
{
//if (k.Key == "attach" || k.Key == "body" || k.Key == "sign")
//{
// sbPay.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">");
//}
//else
//{
sbPay.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">");
// }
}
string return_string = string.Format("<xml>{0}</xml>", sbPay.ToString());
byte[] byteArray = Encoding.UTF8.GetBytes(return_string);
return_string = Encoding.GetEncoding("UTF-8").GetString(byteArray);
TenpayUtil 类
using System;
using System.Collections.Generic;
using System.Web;
using System.Text;
using System.Xml;
using System.Net;
using System.IO;
/// <summary>
///TenpayUtil 的摘要说明
/// </summary>
public class TenpayUtil
{
public TenpayUtil()
{
//
}
//TODO: 在此处添加构造函数逻辑
//
/// <summary>
/// 统一支付接口
/// </summary>
const string UnifiedPayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/// <summary>
/// 网页授权接口
/// </summary>
const string access_tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
/// <summary>
/// 微信订单查询接口
/// </summary>
const string OrderQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
/// <summary>
/// 随机串
/// </summary>
public static string getNoncestr()
{
Random random = new Random();
return MD5Util.GetMD5(random.Next(1000).ToString(), "GBK").ToLower().Replace("s", "S");
}
/// <summary>
/// 时间截,自1970年以来的秒数
/// </summary>
public static string getTimestamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
/// <summary>
/// 网页授权接口
/// </summary>
public static string getAccess_tokenUrl()
{
return access_tokenUrl;
}
/// <summary>
/// 获取微信签名
/// </summary>
/// <param name="sParams"></param>
/// <returns></returns>
public string getsign(SortedDictionary<string, string> sParams, string key)
{
int i = 0;
string sign = string.Empty;
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<string, string> temp in sParams)
{
if (temp.Value == "" || temp.Value == null || temp.Key.ToLower() == "sign" )
{
continue;
}
i++;
sb.Append(temp.Key.Trim() + "=" + temp.Value.Trim() + "&");
}
sb.Append("key=" + key.Trim() + "");
string signkey = sb.ToString();
sign = MD5Util.GetMD5(signkey, "utf-8");
return sign;
}
/// <summary>
/// post数据到指定接口并返回数据
/// </summary>
public string PostXmlToUrl(string url, string postData)
{
//string returnmsg = "";
//using (System.Net.WebClient wc = new System.Net.WebClient())
//{
// returnmsg = wc.UploadString(url, "POST", postData);
//}
//return returnmsg;
HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create(url);
hwr.Method = "POST";
Stream stream = hwr.GetRequestStream();
StreamWriter sw = new StreamWriter(stream, System.Text.Encoding.UTF8);
sw.Write(postData);
sw.Close();
stream = hwr.GetResponse().GetResponseStream();
StreamReader sr = new StreamReader(stream, System.Text.Encoding.UTF8);
string ret = sr.ReadToEnd();
sr.Close();
return ret;
}
/// <summary>
/// 获取prepay_id
/// </summary>
public string getPrepay_id(UnifiedOrder order, string key)
{
string prepay_id = "";
string post_data = getUnifiedOrderXml(order, key);
string request_data = PostXmlToUrl(UnifiedPayUrl, post_data);
SortedDictionary<string, string> requestXML = GetInfoFromXml(request_data);
foreach (KeyValuePair<string, string> k in requestXML)
{
if (k.Key == "prepay_id")
{
prepay_id = k.Value;
break;
}
}
return prepay_id;
}
/// <summary>
/// 获取微信订单明细
/// </summary>
public OrderDetail getOrderDetail(QueryOrder queryorder, string key)
{
string post_data = getQueryOrderXml(queryorder, key);
string request_data = PostXmlToUrl(OrderQueryUrl, post_data);
OrderDetail orderdetail = new OrderDetail();
SortedDictionary<string, string> requestXML = GetInfoFromXml(request_data);
foreach (KeyValuePair<string, string> k in requestXML)
{
switch (k.Key)
{
case "retuen_code":
orderdetail.result_code = k.Value;
break;
case "return_msg":
orderdetail.return_msg = k.Value;
break;
case "appid":
orderdetail.appid = k.Value;
break;
case "mch_id":
orderdetail.mch_id = k.Value;
break;
case "nonce_str":
orderdetail.nonce_str = k.Value;
break;
case "sign":
orderdetail.sign = k.Value;
break;
case "result_code":
orderdetail.result_code = k.Value;
break;
case "err_code":
orderdetail.err_code = k.Value;
break;
case "err_code_des":
orderdetail.err_code_des = k.Value;
break;
case "trade_state":
orderdetail.trade_state = k.Value;
break;
case "device_info":
orderdetail.device_info = k.Value;
break;
case "openid":
orderdetail.openid = k.Value;
break;
case "is_subscribe":
orderdetail.is_subscribe = k.Value;
break;
case "trade_type":
orderdetail.trade_type = k.Value;
break;
case "bank_type":
orderdetail.bank_type = k.Value;
break;
case "total_fee":
orderdetail.total_fee = k.Value;
break;
case "coupon_fee":
orderdetail.coupon_fee = k.Value;
break;
case "fee_type":
orderdetail.fee_type = k.Value;
break;
case "transaction_id":
orderdetail.transaction_id = k.Value;
break;
case "out_trade_no":
orderdetail.out_trade_no = k.Value;
break;
case "attach":
orderdetail.attach = k.Value;
break;
case "time_end":
orderdetail.time_end = k.Value;
break;
default:
break;
}
}
return orderdetail;
}
/// <summary>
/// 把XML数据转换为SortedDictionary<string, string>集合
/// </summary>
/// <param name="strxml"></param>
/// <returns></returns>
protected SortedDictionary<string, string> GetInfoFromXml(string xmlstring)
{
SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlstring);
XmlElement root = doc.DocumentElement;
int len = root.ChildNodes.Count;
for (int i = 0; i < len; i++)
{
string name = root.ChildNodes[i].Name;
if (!sParams.ContainsKey(name))
{
sParams.Add(name.Trim(), root.ChildNodes[i].InnerText.Trim());
}
}
}
catch { }
return sParams;
}
/// <summary>
/// 微信统一下单接口xml参数整理
/// </summary>
/// <param name="order">微信支付参数实例</param>
/// <param name="key">密钥</param>
/// <returns></returns>
protected string getUnifiedOrderXml(UnifiedOrder order, string key)
{
string return_string = string.Empty;
SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
sParams.Add("appid", order.appid);
sParams.Add("attach", order.attach);
sParams.Add("body", order.body);
sParams.Add("device_info", order.device_info);
sParams.Add("mch_id", order.mch_id);
sParams.Add("nonce_str", order.nonce_str);
sParams.Add("notify_url", order.notify_url);
sParams.Add("openid", order.openid);
sParams.Add("out_trade_no", order.out_trade_no);
sParams.Add("spbill_create_ip", order.spbill_create_ip);
sParams.Add("total_fee", order.total_fee.ToString());
sParams.Add("trade_type", order.trade_type);
order.sign = getsign(sParams, key);
sParams.Add("sign", order.sign);
//拼接成XML请求数据
StringBuilder sbPay = new StringBuilder();
foreach (KeyValuePair<string, string> k in sParams)
{
//if (k.Key == "attach" || k.Key == "body" || k.Key == "sign")
//{
// sbPay.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">");
//}
//else
//{
sbPay.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">");
// }
}
return_string = string.Format("<xml>{0}</xml>", sbPay.ToString());
byte[] byteArray = Encoding.UTF8.GetBytes(return_string);
return_string = Encoding.GetEncoding("UTF-8").GetString(byteArray);
return return_string;
}
/// <summary>
/// 微信订单查询接口XML参数整理
/// </summary>
/// <param name="queryorder">微信订单查询参数实例</param>
/// <param name="key">密钥</param>
/// <returns></returns>
protected string getQueryOrderXml(QueryOrder queryorder, string key)
{
string return_string = string.Empty;
SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
sParams.Add("appid", queryorder.appid);
sParams.Add("mch_id", queryorder.mch_id);
sParams.Add("transaction_id", queryorder.transaction_id);
sParams.Add("out_trade_no", queryorder.out_trade_no);
sParams.Add("nonce_str", queryorder.nonce_str);
queryorder.sign = getsign(sParams, key);
sParams.Add("sign", queryorder.sign);
//拼接成XML请求数据
StringBuilder sbPay = new StringBuilder();
foreach (KeyValuePair<string, string> k in sParams)
{
if (k.Key == "attach" || k.Key == "body" || k.Key == "sign")
{
sbPay.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">");
}
else
{
sbPay.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">");
}
}
return_string = string.Format("<xml>{0}</xml>", sbPay.ToString().TrimEnd(','));
return return_string;
}
}