这两天做了下微信支付,其中也遇到不少问题,记录下,以免下次重复踩坑。
结合我们项目使用场景,我选择了扫码支付 模式二 (具体开发步骤见 官方文档
扫码支付模式二
)
微信下单方法:
SortedMap<Object,Object> param = new TreeMap<Object,Object>();
//公众号ID
param.put("appid", appid);
//订单主题
param.put("body", body);
//商户号
param.put("mch_id", mch_id);
//随机字符串
param.put("nonce_str", randomStr);
//回调地址
param.put("notify_url", NOTIFY_URL);
//商户订单号
param.put("out_trade_no",out_trade_no);
//客户端IP
param.put("spbill_create_ip",ip);
//金额
param.put("total_fee",changeY2F(ppd.getPartyFee().toString()));
//订单类型
param.put("trade_type", "NATIVE");
String sign = createSign(param, apiKey);
//签名
param.put("sign", sign);
System.out.println("========== sign: " + sign);
String xml = getRequestXML(param);
System.out.println("========== xml: " + xml);
String content = HttpClinetUtil.sendPost(UNIFORMORDER, new String(xml.getBytes(),"ISO8859-1"));
System.out.println("==========content: " + content);
JSONObject result_xml = JSONObject.parseObject(xml2JSON(content)) ;
System.out.println("================return_msg: " +result_xml.get("return_msg"));
String return_code = result_xml.getString("return_code");
String result_code = result_xml.getString("result_code");
由于微信total_fee 参数要求是具体到多少分 (整数),这里我们需要将金额进行转换 changeY2F 具体实现如下 (直接引用了网上的 不需要重复造轮子)
public String changeY2F(String amount) {
String currency = amount.replaceAll("\\$|\\¥|\\,", ""); // 处理包含, ¥
// 或者$的金额
int index = currency.indexOf(".");
int length = currency.length();
Long amLong = 0l;
if (index == -1) {
amLong = Long.valueOf(currency + "00");
} else if (length - index >= 3) {
amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
} else if (length - index == 2) {
amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
} else {
amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
}
return amLong.toString();
}
随机字符串的生成方法:
// 随机字符串生成
public static String getRandomString(int length) { // length表示生成字符串的长度
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
生成签名方法:
//创建md5 数字签证
public String createSign(SortedMap<Object, Object> parame,String apiKey) throws Exception{
StringBuffer buffer = new StringBuffer();
Set set = parame.entrySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String)entry.getKey();
Object value = (String)entry.getValue();
if(null != value && !"".equals(value) && !"sign".equals(key) && !"key".equals(key)){
buffer.append(key+"="+value+"&");
}
}
buffer.append("key="+apiKey);
System.out.println("============ " + buffer.toString());
String sign = MD5(buffer.toString()).toUpperCase();
System.out.println("签名参数:"+sign);
return sign;
}
MD5 我直接引用了官方sdk的方法:
public String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(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请求参数方法:
public static String getRequestXML(SortedMap<Object, Object> parame){
StringBuffer buffer = new StringBuffer();
buffer.append("<xml>");
Set set = parame.entrySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String)entry.getKey();
String value = (String)entry.getValue();
//过滤相关字段sign
if("sign".equalsIgnoreCase(key)){
buffer.append("<"+key+">"+"<![CDATA["+value+"]]>"+"</"+key+">");
}else{
buffer.append("<"+key+">"+value+"</"+key+">");
}
}
buffer.append("</xml>");
return buffer.toString();
}
这里需要注意的是 参数排序需要安装ascii码升序排列 即 a b c 这个顺序
这里一个遇到一个大坑,一直签名错误,同样的参数在官网签名验证可以通过,但是调用接口就返回乱码,经过各种尝试 还是不行 (参数包含中文时候) 后来发现这里需要
转码为
ISO8859-1
最后就是对返回xml数据的解析了
public static String xml2JSON(String xml) {
JSONObject obj = new JSONObject();
try {
InputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(is);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
for (Element e : elementList)
obj.put(e.getName(), e.getText());
return obj.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
核心代码就这么多了,具体过程官方文档给的很详细,这里就不多做累述了
关键点就是签名算法 以及最后对请求参数的转码和金额的单位(分)
版权声明:本文为jin5203344原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。