首先不得不吐槽下腾讯,升级微信支付为毛不兼容V2版本呢?V2算是白研究了。
V3预支付文档几个坑,不知道你们有没有中招
- 商户号 mch_id 是 String(32) 微信支付分配的商户号 其实是邮件的“微信支付商户号(接口文档中的商户号MCHID)” ,我一直都以为是商户号。
- 预支付提交的xml节点值都必须带有<![CDATA[]]>,如果根据文档开发只带部分。那么你就开心了,一直都是sign签名错误。
回到正文,以下为实现代码
public class PaymentHandler : RequestHandler { public PaymentHandler(HttpContext context) : base(context, WX_V3_CONFIG.config.PartnerKey) { } /// <summary> /// 获取预支付订单号 /// </summary> /// <returns></returns> public string GetUnifiedOrder(string body, string openid, string trade_type) { base.setParameter("appid", WX_V3_CONFIG.config.AppID); base.setParameter("mch_id", WX_V3_CONFIG.config.MCHID); base.setParameter("nonce_str", TenpayUtil.getNoncestr()); base.setParameter("body", body); base.setParameter("out_trade_no", TenpayUtil.getOrderNo()); base.setParameter("total_fee", "1"); base.setParameter("spbill_create_ip", "192.168.1.1");//base.httpContext.Request.UserHostAddress); base.setParameter("notify_url", "www.111.com"); base.setParameter("trade_type", "JSAPI"); base.setParameter("openid", openid); base.createSign(); string s = HttpHelper.OpenReadWithHttps("https://api.mch.weixin.qq.com/pay/unifiedorder", base.parseXML());//post请求 return ""; } }
RequestHandler帮助类
/// <summary> /// RequestHandler 的摘要说明。 /// </summary> public class RequestHandler { public RequestHandler(HttpContext httpContext, string key) { parameters = new Hashtable(); this.key = key; this.httpContext = httpContext; } /** 密钥 */ private string key; protected HttpContext httpContext; /** 请求的参数 */ protected Hashtable parameters; /** debug信息 */ private string debugInfo; /** 初始化函数 */ public virtual void init() { } /** 获取debug信息 */ public String getDebugInfo() { return debugInfo; } /** 获取密钥 */ public String getKey() { return key; } /** 设置密钥 */ public void setKey(string key) { this.key = key; } /** 设置参数值 */ public void setParameter(string parameter, string parameterValue) { if (parameter != null && parameter != "") { if (parameters.Contains(parameter)) { parameters.Remove(parameter); } parameters.Add(parameter, parameterValue); } } /** 获取参数值 */ public string getParameter(string parameter) { string s = (string)parameters[parameter]; return (null == s) ? "" : s; } //获取package带参数的签名包 public string getRequestURL() { this.createSign(); StringBuilder sb = new StringBuilder(); ArrayList akeys = new ArrayList(parameters.Keys); akeys.Sort(); foreach (string k in akeys) { string v = (string)parameters[k]; if (null != v && "key".CompareTo(k) != 0) { sb.Append(k + "=" + TenpayUtil.UrlEncode(v, getCharset()) + "&"); } } //去掉最后一个& if (sb.Length > 0) { sb.Remove(sb.Length - 1, 1); } return sb.ToString(); } //创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 protected virtual void createSign() { StringBuilder sb = new StringBuilder(); ArrayList akeys = new ArrayList(parameters.Keys); akeys.Sort(); foreach (string k in akeys) { string v = (string)parameters[k]; if (null != v && "".CompareTo(v) != 0 && "sign".CompareTo(k) != 0 && "key".CompareTo(k) != 0) { sb.Append(k + "=" + v + "&"); } } sb.Append("key=" + this.getKey()); string sign = MD5Util.GetMD5(sb.ToString(), getCharset()).ToUpper(); this.setParameter("sign", sign); //debug信息 this.setDebugInfo(sb.ToString() + " => sign:" + sign); } //创建package签名 public virtual string createMd5Sign() { StringBuilder sb = new StringBuilder(); ArrayList akeys = new ArrayList(parameters.Keys); akeys.Sort(); foreach (string k in akeys) { string v = (string)parameters[k]; if (null != v && "".CompareTo(v) != 0 && "sign".CompareTo(k) != 0 && "".CompareTo(v) != 0) { sb.Append(k + "=" + v + "&"); } } string sign = MD5Util.GetMD5(sb.ToString(), getCharset()).ToLower(); this.setParameter("sign", sign); return sign; } //创建sha1签名 public string createSHA1Sign() { StringBuilder sb = new StringBuilder(); ArrayList akeys = new ArrayList(parameters.Keys); akeys.Sort(); foreach (string k in akeys) { string v = (string)parameters[k]; if (null != v && "".CompareTo(v) != 0 && "sign".CompareTo(k) != 0 && "key".CompareTo(k) != 0) { if (sb.Length == 0) { sb.Append(k + "=" + v); } else { sb.Append("&" + k + "=" + v); } } } string paySign = SHA1Util.getSha1(sb.ToString()).ToString().ToLower(); //debug信息 this.setDebugInfo(sb.ToString() + " => sign:" + paySign); return paySign; } //输出XML public string parseXML() { StringBuilder sb = new StringBuilder(); sb.Append("<xml>"); foreach (string k in parameters.Keys) { string v = (string)parameters[k]; if (Regex.IsMatch(v, @"^[0-9.]$")) { sb.Append("<" + k + ">" + v + "</" + k + ">"); } else { sb.Append("<" + k + "><![CDATA[" + v + "]]></" + k + ">"); } } sb.Append("</xml>"); return sb.ToString(); } /** 设置debug信息 */ public void setDebugInfo(String debugInfo) { this.debugInfo = debugInfo; } public Hashtable getAllParameters() { return this.parameters; } protected virtual string getCharset() { return this.httpContext.Request.ContentEncoding.BodyName; } }