因工作需要对接微信的扫码支付,这里用到的统一下单支付接口。开始一切正常。可是当传递参数body为中文的时候,返回错误提示:body不是utf8编码。看字面意思是编码不是utf-8,经常做java的朋友都对这类问题见怪不怪了(心中早有了解决方法),可是这次没有想像的那么简单,试过了N种方法,结果要么是:签名不对,要么是:body不是utf8编码。看来这次没有那么简单,毕竟要对接人家的接口,所幸到网上搜索一下,遇到此问题的还真不少,照着别人的解决方法也不好使,所有方法都不行的情况下,只好回头来耐心研究了。还好费了点工夫总算解决,下面说一下解决的方法吧。保证切实可用!
首先有说对中文进行unicode编码,结果试了是不可以的,见下图:
再就是有说进行urlencode编码,试了结果也是不行的:
下面分析一下提单环节用到中文编码的几个地方:
1、xml的内容中中文要使用utf-8
2、对参数签名的加密方法无论用MD5还是SHA256,均需要指定utf-8
3、数据post到签名下单接口时要指定utf-8编码
4、读取微信返回参数时使用utf-8编码(虽然这个不影响业务逻辑,不过指定好可以正确获取微信的中文信息)
然后针对上面的每个环节看一下我的代码:
一、构造map
HashMap map=new HashMap
String body=RequestUtil.getSafeStr(request, "body");
map.put("appid",wxBean.getAppId());//公众账号id
map.put("mch_id",wxBean.getMchId());//商户号
map.put("sub_mch_id", sub_mch_id);//子商户号
map.put("nonce_str",timestamp);//随机字符串
map.put("sign_type","HMAC-SHA256");//签名方式
map.put("body",body);//商品描述
map.put("out_trade_no", tradeno);//商户订单号
map.put("total_fee",pay_money+"");//总金额 分
map.put("spbill_create_ip",uip);//终端ip
map.put("notify_url", SiteUrl+"/mch/notify.jsp");//通知地址
map.put("trade_type", "NATIVE");//交易类型
map.put("product_id", "1");//商品id 否 native必传
String xml=WxUtil.covertToXml(map,wxBean.getMchKey(),"HMACSHA256");
二、生成加签名的xml字符串
public static String covertToXml(Map
StringBuffer sb=new StringBuffer();
String sign = "";
try{
sign=generateSignature(m, key,signtype);
}catch(Exception e){
e.printStackTrace();
}
sb.append("
Iterator it = m.keySet().iterator();
String kname;
while(it.hasNext()) {
kname= (String)it.next();
sb.append("<"+kname+">").append(m.get(kname)).append(""+kname+">");
}
sb.append("
sb.append("");
return sb.toString();
}
三、对应的加签方法(支持MD5或SHA256)
public static String generateSignature(final Map
Set
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals("sign")) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if ("MD5".equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if ("HMACSHA256".equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
//MD5加密
public static String MD5(String str) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(str.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException caught!");
System.exit(-1);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
byte[] byteArray = messageDigest.digest();
StringBuffer md5StrBuff = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) {
md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i]));
} else {
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
}
}
return md5StrBuff.toString();
}
/**
* 生成 HMACSHA256
* @param data 待处理数据
* @param key 密钥
* @return 加密结果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
四、将以上三步生成好的XML字符串提交到微信统一下单接口即可
String ret=WxUtil.requestXml("https://api.mch.weixin.qq.com/pay/unifiedorder", xml);
然后贴一下requestXml方法的详细代码:
public static String requestXml(String url,String data){
BasicHttpClientConnectionManager connManager;
connManager = new BasicHttpClientConnectionManager(
RegistryBuilder.
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build(),
null,
null,
null
);
String rets="";
HttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connManager).build();
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();
httpPost.setConfig(requestConfig);
StringEntity postEntity = new StringEntity(data, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);
HttpResponse httpResponse = null;
try{
httpResponse=httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
rets=EntityUtils.toString(httpEntity, "UTF-8");
}catch(Exception e){
e.printStackTrace();
}
return rets;
}
经过以上步骤即可成功解决BODY中文UTF-8编码的问题,最后效果图如下:
最后,在找解决方法的时候,偶然发现个人也可以申请微信支付接口,原来一直以为只有企业才可以,服务商叫"易快得",经过在官网注册(域名是中文名的全拼)后发现从申请到微信审核通过只用了10分钟 就成功了,需要的朋友可以了解下。