首先不得不吐槽下腾讯,升级微信支付为毛不兼容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;
}
}