微信app支付 签名错误 以及 访问https 的解决办法
微信支付的接口不同于支付宝等其他第三方接口足够友好,所以这里有必要对 微信签名错误和访问https接口做一些小结以供参考。
签名错误:
首先总结原因,可能发生签名错误的原因如下:
1 密钥错误,密钥是32位长的一串字符,类似于 f0fnf5872825aien55end044e092le39 ,如果需要重新设置 ,位置在 设置位置:账户设置-安全设置-API安全
2 本地签名程序
基本遵照微信官方文档说明即可 https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=4_3
传递哪些参数 需要根据文档说明 https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1
参考(内部的一些关键参数只提供参考,位数不对):
sb 字符串 appid=wx5382e5300d7f6e555f&body=Corsair Neutron Series GTX 480GB 固态硬盘&mch_id=1237315833041&nonce_str=1B72746255EF01F9D75400995C62EA12
¬ify_url=http://m.dahongwa.com/pay/notify&out_trade_no
=151023113401857821&spbill_create_ip=10.244.1.171&total_fee=84462&trade_type=APP&key=f0fn3f5872825ai4en55end044e092le39
再对此字符串进行md5加密之后调成大写,即为sign签名
生成的xml文档:
<xml>
<appid>wx5382e5300d7f6e555f</appid>
<body><![CDATA[Corsair Neutron Series GTX 480GB 固态硬盘]]></body>
<mch_id>1237315833041</mch_id>
<nonce_str>1B72746255EF01F9D75400995C62EA12</nonce_str>
<notify_url>http://m.dahongwa.com/pay/notify</notify_url>
<out_trade_no>151023113401857821</out_trade_no>
<sign><![CDATA[CD0DCCA447A6C0B0482C4D43A545A0BA]]></sign>
<spbill_create_ip>10.244.1.171</spbill_create_ip>
<total_fee>84462</total_fee>
<trade_type>APP</trade_type>
</xml>
生成的xml是提交给 微信的参数,必须和签名的字符串里的值相同
如果返回的微信参数提示 签名错误,可以到微信官方测试测试地址测试
https://pay.weixin.qq.com/wiki/tools/signverify/
微信返回信息中,如果出现签名错误,可以把自己的资料填写在上面的测试用例中获取微信生成的签名,
如果测试sign与自己生成的不同 返回上步检查,如果微信生成的和自己生成的签名相同,但是微信还是返回签名错误,则一定是商品中文(body)编码。
有几个地方的编码必须注意.
设置 sb 字符串 的时候 body一定为utf-8,sb字符串md5加密的时候 必须为utf-8,
传递给 微信的时候 ,此参数也必须为 utf-8 :entity = new StringEntity(xml,"utf-8");
httpclient 头部和参数 也设置成utf-8:
httpPost.setEntity(entity);
httpPost.addHeader("Content-Type", "text/html;charset=UTF-8");
httpClient.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
httpPost.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
这几个编码优先级逐步提高,也就是说后面的会覆盖前面的。
由于微信的接口是https,在生产环境又只能通过代理访问,所以通常的http接口访问会返回错误信息:
WARN,http-apr-9002-exec-4,10-22 20:21:30.393,httpclient.HttpMethodDirector.authenticateHost:288 - Required credentials not available for BASIC <any realm>@api.mch.weixin.qq.com:443
WARN,http-apr-9002-exec-4,10-22 20:21:30.393,httpclient.HttpMethodDirector.authenticateHost:290 - Preemptive authentication requested but no default credentials available
INFO,http-apr-9002-exec-4,10-22 20:21:30.583,httpclient.HttpMethodDirector.executeWithRetry:439 - I/O exception (java.net.ConnectException) caught when processing request: Connection refused
INFO,http-apr-9002-exec-4,10-22 20:21:30.583,httpclient.HttpMethodDirector.executeWithRetry:445 - Retrying request
INFO,http-apr-9002-exec-4,10-22 20:21:30.591,httpclient.HttpMethodDirector.executeWithRetry:439 - I/O exception (java.net.ConnectException) caught when processing request: Connection refused
INFO,http-apr-9002-exec-4,10-22 20:21:30.592,httpclient.HttpMethodDirector.executeWithRetry:445 - Retrying request
INFO,http-apr-9002-exec-4,10-22 20:21:30.594,httpclient.HttpMethodDirector.executeWithRetry:439 - I/O exception (java.net.ConnectException) caught when processing request: Connection refused
INFO,http-apr-9002-exec-4,10-22 20:21:30.595,httpclient.HttpMethodDirector.executeWithRetry:445 - Retrying request
java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:579)
at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:618)
at com.legendshop.model.app.wxpay.MySSLProtocolSocketFactory.createSocket(MySSLProtocolSocketFactory.java:92)
at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
这是因为代理访问被微信拒绝,所以需要重新设置访问程序:
StringEntity entity;
HttpResponse httpResponse;
int proxyPortCon = 0;
DefaultHttpClient httpClient = new DefaultHttpClient(); //创建默认的httpClient实例
try {
entity = new StringEntity(xml,"utf-8");
if("true".equalsIgnoreCase(useProxy) ){
X509TrustManager xtm = new X509TrustManager(){ //创建TrustManager
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() { return null; }
};
X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() {
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
public void verify(String arg0, SSLSocket arg1) throws IOException {}
public void verify(String arg0, String[] arg1, String[] arg2) throws SSLException {}
public void verify(String arg0, X509Certificate arg1) throws SSLException {}
};
//TLS1.0与SSL3.0基本上没有太大的差别,可粗略理解为TLS是SSL的继承者,但它们使用的是相同的SSLContext
SSLContext ctx = SSLContext.getInstance("TLS");
//使用TrustManager来初始化该上下文,TrustManager只是被SSL的Socket所使用
ctx.init(null, new TrustManager[] { xtm }, null);
//创建SSLSocketFactory
SSLSocketFactory socketFactory = new SSLSocketFactory(ctx);
socketFactory.setHostnameVerifier(hostnameVerifier);
//通过SchemeRegistry将SSLSocketFactory注册到我们的HttpClient上
httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", socketFactory, 443));
proxyPortCon = Integer.parseInt(proxyPort);
HttpHost proxy = new HttpHost(proxyHost, proxyPortCon);
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
}
HttpPost httpPost = new HttpPost(unifiedorder); //创建HttpPost
httpPost.setEntity(entity);
httpPost.addHeader("Content-Type", "text/html;charset=UTF-8");
httpClient.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
//httpPost.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
httpResponse = httpClient.execute(httpPost); //执行POST请求
if (AppUtils.isNotBlank(httpResponse)) {
HttpEntity httpEntity = httpResponse.getEntity();
String result = EntityUtils.toString(httpEntity, "UTF-8");
}
下面贴一些代码 供参考:
app前段调用微信统一下单接口
/**
* 微信支付统一下单接口
* @param orderInfoJson 请求参数json
* @return
* @throws UnsupportedEncodingException
*/
@RequestMapping(value = "/pay/unifiedorder", method =RequestMethod.POST)
@ResponseBody
public Result getOrderInfo(HttpServletRequest request, HttpServletResponse response,@RequestParam("code") String code, @RequestParam("orderInfoJson") String orderInfoJson) throws UnsupportedEncodingException {
Result result = null;
//日志记录
AppServiceLog.info("orderInfoJson is {}", orderInfoJson);
AppServiceLog.visit(AppInterfaceEnum.WECHAT_PAY, "/pay/unifiedorder", getUserName(request));
Map<?, ?> paramMap=JsonToMap.getBody(orderInfoJson);
final Gson gson =new Gson();
WXPrepayVO orderInfo = gson.fromJson(gson.toJson(paramMap),WXPrepayVO.class);
WXPrepay prePay = new WXPrepay();
//获取订单信息
result = orderService.payUnifiedorder(request,orderInfo,prePay);
return result;
}
服务器构造对象 向微信服务器发起https访问,以获取预支付id
public Result payUnifiedorder(HttpServletRequest request,WXPrepayVO orderInfo,WXPrepay prePay) throws UnsupportedEncodingException {
Result result = new Result();
Double totalAmount = new Double(0);
OrderTemp orderTemp = orderTempDao.getOrderByOrderNum(orderInfo.getOut_trade_no());
Order order = null;
if(AppUtils.isBlank(orderTemp)){
order = orderDao.getOrderByOrderNumLimitOne(orderInfo.getOut_trade_no());
if(AppUtils.isNotBlank(order)){
totalAmount = order.getTotalAmount();
}
}else{
totalAmount = orderTemp.getTotalAmount();
}
if(AppUtils.isBlank(orderTemp) && AppUtils.isBlank(order)){
result.setCode(ReturnCode.orderIsNotExist.value());
result.setMessage(ReturnCode.orderIsNotExist.desc());
return result;
}
//商品描述
String bodyStr = Base64Decoder.Decoder(orderInfo.getBody());
String spbill_create_ip = request.getRemoteAddr();
prePay.setAppid(appId);
prePay.setBody(bodyStr);
prePay.setPartnerKey(appKey);
prePay.setMch_id(mchId);
prePay.setNotify_url(notifyUrl);
prePay.setOut_trade_no(orderInfo.getOut_trade_no());
prePay.setSpbill_create_ip(spbill_create_ip);
DecimalFormat df = new DecimalFormat("#");
prePay.setTotal_fee(df.format(totalAmount * 10 * 10));
//prePay.setTotal_fee("1");
prePay.setTrade_type(PayTypeEnum.WX_APP_PAY.value());
//代理
String useProxyConfig = PropertiesUtil.getUseProxy();
String proxyHostConfig = PropertiesUtil.getProxyHost();
String proxyPortConfig = PropertiesUtil.getProxyPort();
//此处添加获取openid的方法,获取预支付订单需要此参数!!!!!!!!!!!
// 获取预支付订单号
WePayResponse wePayResponse = prePay.submitXmlGetPrepayId(useProxyConfig,proxyHostConfig,proxyPortConfig);
wePayResponse.setPartnerId(mchId);
wePayResponse.setPackageS("Sign=WXPay");
wePayResponse.setNonceStr(OrderUtil.CreateNoncestr());
wePayResponse.setTimeStamp(new Date().getTime());
if(wePayResponse.getResultCode().equals("SUCCESS")){
result.setCode(ReturnCode.OK.value());
result.setMessage(wePayResponse.getReturnMsg());
result.setSuccess(true);
}else{
result.setMessage(wePayResponse.getErrCodeDes());
}
result.setObject(wePayResponse);
return result;
}
/**
* 生成预支付订单
*
* @return
* @throws UnsupportedEncodingException
*/
@SuppressWarnings("deprecation")
public WePayResponse submitXmlGetPrepayId(String useProxy,String proxyHost,String proxyPort) throws UnsupportedEncodingException {
System.out.println(useProxy +"--"+ proxyHost +"---"+ proxyPort);
String xml = getPackage();
WePayResponse response = null;
StringEntity entity;
HttpResponse httpResponse;
int proxyPortCon = 0;
DefaultHttpClient httpClient = new DefaultHttpClient(); //创建默认的httpClient实例
try {
entity = new StringEntity(xml,"utf-8");
if("true".equalsIgnoreCase(useProxy) ){
/**
下面这段代码 用来处理访问https的请求
**/
X509TrustManager xtm = new X509TrustManager(){ //创建TrustManager
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() { return null; }
};
X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() {
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
public void verify(String arg0, SSLSocket arg1) throws IOException {}
public void verify(String arg0, String[] arg1, String[] arg2) throws SSLException {}
public void verify(String arg0, X509Certificate arg1) throws SSLException {}
};
//TLS1.0与SSL3.0基本上没有太大的差别,可粗略理解为TLS是SSL的继承者,但它们使用的是相同的SSLContext
SSLContext ctx = SSLContext.getInstance("TLS");
//使用TrustManager来初始化该上下文,TrustManager只是被SSL的Socket所使用
ctx.init(null, new TrustManager[] { xtm }, null);
//创建SSLSocketFactory
SSLSocketFactory socketFactory = new SSLSocketFactory(ctx);
socketFactory.setHostnameVerifier(hostnameVerifier);
//通过SchemeRegistry将SSLSocketFactory注册到我们的HttpClient上
httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", socketFactory, 443));
/****
下面这段代码 使用代理访问https外部接口
****/
proxyPortCon = Integer.parseInt(proxyPort);
HttpHost proxy = new HttpHost(proxyHost, proxyPortCon);
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
}
HttpPost httpPost = new HttpPost(unifiedorder); //创建HttpPost
httpPost.setEntity(entity);
httpPost.addHeader("Content-Type", "text/html;charset=UTF-8");
httpClient.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
//httpPost.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
httpResponse = httpClient.execute(httpPost); //执行POST请求
if (AppUtils.isNotBlank(httpResponse)) {
HttpEntity httpEntity = httpResponse.getEntity();
String result = EntityUtils.toString(httpEntity, "UTF-8");
// 过滤
result = result.replaceAll("<![CDATA[|]]>", "");
String prepay_id = Jsoup.parse(result).select("prepay_id").html();
String trade_type = Jsoup.parse(result).select("trade_type").html();
String returnCode = Jsoup.parse(result).select("return_code").html();
String returnMsg = Jsoup.parse(result).select("return_msg").html();
String resultCode = Jsoup.parse(result).select("result_code").html();
String errCode = Jsoup.parse(result).select("err_code").html();
String errCodeDes = Jsoup.parse(result).select("err_code_des").html();
response = WePayResponse.newInstance();
response.setPrepayId(prepay_id);
response.setTradeType(trade_type);
response.setReturnCode(returnCode);
response.setReturnMsg(returnMsg);
response.setResultCode(resultCode);
response.setErrCode(errCode);
response.setErrCodeDes(errCodeDes);
if (response != null)
return response;
}
// 释放资源
} catch (Exception e) {
e.printStackTrace();
}
return response;
}