最近在做微信支付项目,记录一下:
1、需要现在微信公众平台上设置,具体在 菜单:设置-公众号设置-功能设置,把开发程序所在的域名增加,具体在业务域名、JS接口安全域名
、网页授权域名(这个主要是用于授权用的) 都要增加相应的域名目录。
2、在微信支付平台,产品中心-开发配置-支付设置,公众号支付-JSAPI授权目录增加域名目录,否则不能成功调用。 以上两步必须同时设置好。
3、然后阅读开发微信支付开发文档,根据文档开发即可。虽然大家都说有很多坑,但是慢慢解决,能够很好的实现调用。自己感觉微信支付比支付宝支付要简单。如果还是无法成功,或者提示签名错误,有的时候需要重置API密钥试试(调试了几天 找了好多办法都没有解决,结果最后真的重置了API密钥就好了)。
4、还有一个问题,就是微信调起支付后,没有支付,然后再支付的时候提示重复下单或这缺少total_fee参数(这个实际上有的时候并不是真少这个参数),这个时候应该是body内容不一样但是订单号一样。
我的解决办法是这样的:每次调起支付的时候,将生成的支付信息保存到数据库,如果当时调起支付未支付,那么再次调起的时候会先搜索一下数据库,如果有这条记录,那么将当时的timestamp、NonceStr、paysign、Prepay_id等信息提取出来,生成支付数据。如果数据库没有数据,则启用新的支付参数。这样就能保证统一支付下单的是一致的,不至于重复下单。在这里插入代码片
几个关键代码参考:
public class UnifiedOrder
{
///
///
/// 微信支付分配的终端设备号
///
public string _device_info = ""; ///
/// 公共号ID(微信分配的公众账号 ID)
///
public string appid = "";
///
/// 商户号(微信支付分配的商户号)
///
public string mch_id = "";
///
/// 微信支付分配的终端设备号
///
public string device_info = "";
///
/// 随机字符串,不长于 32 位
///
public string nonce_str = "";
///
/// 签名
///
public string sign = "";
///
/// 商品描述
///
public string body = "";
///
/// 附加数据,原样返回
///
public string attach = "";
///
/// 商户系统内部的订单号,32个字符内、可包含字母,确保在商户系统唯一,详细说明
///
public string out_trade_no = "";
///
/// 订单总金额,单位为分,不能带小数点
///
public int total_fee = 0;
///
/// 终端IP
///
public string spbill_create_ip = "";
///
/// 订 单 生 成 时 间 , 格 式 为yyyyMMddHHmmss,如 2009 年12 月 25 日 9 点 10 分 10 秒表示为 20091225091010。时区为 GMT+8 beijing。该时间取自商户服务器
///
public string time_start = "";
///
/// 交易结束时间
///
public string time_expire = "";
///
/// 商品标记 商品标记,该字段不能随便填,不使用请填空,使用说明详见第 5 节
///
public string goods_tag = "";
///
/// 接收微信支付成功通知
///
public string notify_url = "";
///
/// JSAPI、NATIVE、APP
///
public string trade_type = "";
///
/// 用户标识 trade_type 为 JSAPI时,此参数必传
///
public string openid = "";
///
/// 只在 trade_type 为 NATIVE时需要填写。
///
public string product_id = "";
}
这个是主要的代码
//
///TenpayUtil 的摘要说明
///
public class TenpayUtil
{
public TenpayUtil()
{
//
}
//TODO: 在此处添加构造函数逻辑
//
///
/// 统一支付接口
///
const string UnifiedPayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
///
/// 网页授权接口
///
const string access_tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
///
/// 微信订单查询接口
///
const string OrderQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
///
/// 随机串
///
public static string getNoncestr()
{
Random random = new Random();
return MD5Util.GetMD5(random.Next(1000).ToString(), "GBK").ToLower().Replace("s", "S");
}
///
/// 时间截,自1970年以来的秒数
///
public static string getTimestamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
///
/// 网页授权接口
///
public static string getAccess_tokenUrl()
{
return access_tokenUrl;
}
///
/// 获取微信签名
///
///
///
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;
}
///
/// post数据到指定接口并返回数据
///
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;
}
///
/// 获取prepay_id
///
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;
}
///
/// 获取微信订单明细
///
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;
}
///
/// 把XML数据转换为SortedDictionary集合
///
///
///
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;
}
///
/// 微信统一下单接口xml参数整理
///
/// 微信支付参数实例
/// 密钥
///
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 + ">" + k.Key + ">");
//}
//else
//{
sbPay.Append("<" + k.Key + ">" + k.Value + "" + k.Key + ">");
// }
}
return_string = string.Format("{0} ", sbPay.ToString());
byte[] byteArray = Encoding.UTF8.GetBytes(return_string);
return_string = Encoding.GetEncoding("UTF-8").GetString(byteArray);
return return_string;
}
///
/// 微信订单查询接口XML参数整理
///
/// 微信订单查询参数实例
/// 密钥
///
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 + "> + k.Value + "]]>" + k.Key + ">");
}
else
{
sbPay.Append("<" + k.Key + ">" + k.Value + "" + k.Key + ">");
}
}
return_string = string.Format("{0} ", sbPay.ToString().TrimEnd(','));
return return_string;
}
}
MD5加密:
public class MD5Util
{
public MD5Util()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
/** 获取大写的MD5签名结果 */
public static string GetMD5(string encypStr, string charset)
{
string retStr;
MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider();
//创建md5对象
byte[] inputBye;
byte[] outputBye;
//使用GB2312编码方式把字符串转化为字节数组.
try
{
inputBye = Encoding.GetEncoding(charset).GetBytes(encypStr);
}
catch (Exception ex)
{
inputBye = Encoding.GetEncoding("GB2312").GetBytes(encypStr);
}
outputBye = m5.ComputeHash(inputBye);
retStr = System.BitConverter.ToString(outputBye);
retStr = retStr.Replace("-", "").ToUpper();
return retStr;
}
}
Pay.aspx主要内容
//支付信息
function Pay() {
var appId = "<%=appId %>";
var timeStamp = "<%=timeStamp %>";
var nonceStr = "<%=nonceStr %>";
var prepay_id = "<%=prepay_id %>";
var paySign = "<%=paySign %>";
var OrderID = "<%=OrderID %>";
//alert("appId:" + appId + ",timeStamp:" + timeStamp + ",nonceStr:" + nonceStr + ",prepay_id:" + prepay_id + ",paySign:" + paySign);
//return;
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": appId, //公众号名称,由商户传入
"timeStamp": timeStamp, //时间戳,自1970年以来的秒数
"nonceStr": nonceStr, //随机串
"package": "prepay_id=" + prepay_id,
"signType": "MD5", //微信签名方式:
"paySign": paySign //微信签名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
$.showLoading("正在校验支付");
//检查是否支付成功
$.get("Tools/CheckPay.aspx?OrderId=" + OrderID, function (data) {
$.hideLoading();
if (data == "success") {
alert("支付成功");
window.location.href = "Order.aspx?OrderID=" + OrderID + "&OpenId=" + '<%=OpenID %>' + "&M=" + Math.random();
}
else {
alert("支付待验证,返回结果:" + data);
window.location.href = "Order.aspx?OrderID=" + OrderID + "&OpenId=" + '<%=OpenID %>' + "&M=" + Math.random();
}
}); //get结束
} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
else {
alert("交易取消");
window.location.href = "Order.aspx?OrderID=" + OrderID + "&OpenId=" + '<%=OpenID %>' + "&M=" + Math.random();
}
}
);
}
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
}
Pay.aspx.cs 主要内容
private void PayInfo(string OpenId, string OrderId, string OrderAmount, string paySignKey)
{
try
{
TenpayUtil tenpay = new TenpayUtil();
UnifiedOrder order = new UnifiedOrder();
order.appid = "这里是APPID";
order.attach = "随便写";
order.body = "这里是订单内容";
order.device_info = "";
order.mch_id = "这里是微信支付的商户号";//商户号
order.nonce_str = TenpayUtil.getNoncestr();
order.notify_url = "回调通知地址";
order.openid = OpenId;
order.out_trade_no = OrderId;
order.trade_type = "JSAPI";
order.spbill_create_ip = Page.Request.UserHostAddress;
order.total_fee = int.Parse(OrderAmount);
//order.total_fee = int.Parse("12") * 100;
prepay_id = tenpay.getPrepay_id(order, paySignKey);
timeStamp = TenpayUtil.getTimestamp();
nonceStr = TenpayUtil.getNoncestr();
SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
sParams.Add("appId", appId);
sParams.Add("timeStamp", timeStamp);
sParams.Add("nonceStr", nonceStr);
sParams.Add("package", "prepay_id=" + prepay_id);
sParams.Add("signType", "MD5");
paySign = tenpay.getsign(sParams, paySignKey);
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
}