微信支付服务端的一些坑及最终解决
来源:互联网 发布:免费蝙蝠侠埋雷软件 编辑:程序博客网 时间:2024/05/21 18:43
有1年多没搞微信支付了,最近跳槽,要重做APP,又来接触微信这个坑比。OK,不多说,上代码。以下是我的一个controller类,重点在下面
// 微信交易类型
private static final String TRADETYPE = "APP";
// 微信统一下单接口路径
private static final String UNIFORMORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// 微信回调地址
private static final String NOTIFYURL = "*****";
// 微信商户号:*****
private static final String MCHID = "******";
//微信APIKEY
private static final String APIKEY ="*******";
//微信APPID
private static final String APPID ="*******";
/**
* @Description: Ajax生成微信预支付ID
* @param entity
* @return JsonResult
* @throws JSONException
* @throws UnsupportedEncodingException
*/
@RequestMapping(value = "/getPrepayId",method = RequestMethod.POST)
@ResponseBody
public String getPrepayId(HttpServletRequest request) throws UnsupportedEncodingException {
ResultObject result = new ResultObject(false);// 返回数据结果集合
request.setCharacterEncoding("UTF-8");
try {
// 订单编号
String out_trade_no = request.getParameter("out_trade_no") == null ? null : request.getParameter("out_trade_no").trim();// 订单编号
// 消费金额
String money = request.getParameter("money") == null ? null : request.getParameter("money").trim();// 消费金额
// 消费主题
String subject = request.getParameter("subject") == null ? null : request.getParameter("subject").trim();// 消费主体
if(StringUtils.isEmpty(out_trade_no)){
result.setMsg("参数:out_trade_no 为空");
result.setResultCode("-1");
return JSON.toJSONString(result);
}
if(StringUtils.isEmpty(money)){
result.setMsg("参数:money 为空");
result.setResultCode("-1");
return JSON.toJSONString(result);
}
if(StringUtils.isEmpty(subject)){
result.setMsg("参数:subject 为空");
result.setResultCode("-1");
return JSON.toJSONString(result);
}
String total_fee = "";
total_fee = String.valueOf((new BigDecimal(money).multiply(new BigDecimal(100))).longValue());
SortedMap<Object,Object> parame = new TreeMap<Object,Object>();
parame.put("appid", APPID);
parame.put("mch_id", MCHID);// 商家账号。
String randomStr = getRandomString(18).toUpperCase();
parame.put("nonce_str", randomStr);// 随机字符串
parame.put("body", subject);// 商品描述
parame.put("out_trade_no", out_trade_no);// 商户订单编号
parame.put("fee_type", "CNY");//币种
parame.put("total_fee", total_fee);// 消费金额
String ip = getIpAddr(request);
if (StringUtils.isEmpty(ip)) {
parame.put("spbill_create_ip", "127.0.0.1");// 消费IP地址
} else {
parame.put("spbill_create_ip", ip);// 消费IP地址
}
parame.put("notify_url", NOTIFYURL);// 回调地址
parame.put("trade_type", TRADETYPE);// 交易类型APP
String sign =createSign(parame);
parame.put("sign", sign);// 数字签证
String xml = getRequestXML(parame);
String content = HttpUtil.sendPost(UNIFORMORDER, xml);
System.out.println(content);
JSONObject jsonObject = JSONObject.parseObject(XmltoJsonUtil.xml2JSON(content));
JSONObject result_xml = jsonObject.getJSONObject("xml");
JSONArray result_code = result_xml.getJSONArray("result_code");
String code = (String)result_code.get(0);
if(code.equalsIgnoreCase("FAIL")){
result.setMsg("微信统一订单下单失败");
result.setResultCode("-1");
}else if(code.equalsIgnoreCase("SUCCESS")){
result.setFlag(true);
JSONArray prepay_id = result_xml.getJSONArray("prepay_id");
String prepayId = (String)prepay_id.get(0);
String timestamp = String.valueOf(System.currentTimeMillis()/1000);
SortedMap<Object,Object> signParams = new TreeMap<Object,Object>();
signParams.put("appid", APPID);
signParams.put("noncestr", randomStr);
signParams.put("prepayid", prepayId);
signParams.put("package", "Sign=WXPay");
signParams.put("partnerid", MCHID);
signParams.put("timestamp", timestamp);
String sign2 = createSign(signParams).toUpperCase();
SortedMap<Object, Object> parameterMap2 = new TreeMap<Object, Object>();
parameterMap2.put("appid", APPID);
parameterMap2.put("partnerid", MCHID);
parameterMap2.put("prepayid", prepayId);
parameterMap2.put("package", "Sign=WXPay");
parameterMap2.put("noncestr", randomStr);
//本来生成的时间戳是13位,但是ios必须是10位,所以截取了一下
// parameterMap2.put("timestamp", String.valueOf(System.currentTimeMillis()).toString().substring(0,10));
parameterMap2.put("timestamp", timestamp);
parameterMap2.put("sign", sign2);
result.setMsg("微信统一订单下单成功");
result.setResultCode("1");
result.setData(parameterMap2);
}
return JSON.toJSONString(result);
} catch (Exception e) {
result.setMsg(e.getMessage());
result.setResultCode("-1");
return JSON.toJSONString(result);
}
}
// 返回用IP地址
public String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader(" x-forwarded-for ");
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getHeader(" Proxy-Client-IP ");
}
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getHeader(" WL-Proxy-Client-IP ");
}
if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
// 随机字符串生成
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();
}
//拼接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();
}
//创建md5 数字签证
public static String createSign(SortedMap<Object, Object> parame){
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.getMessageDigest(buffer.toString().getBytes()).toUpperCase();
System.out.println("签名参数:"+sign);
return sign;
}
第一步,生成prepayid,这一步,只要你的appid,mch_id,key没写错,那么99%以上都能获取到prepayid,如果失败,那肯定是几个ID和key有问题,仔细检查,包括编码等,仔细仔细仔细检查。
问题来了,第二步,对获取到的prepayid进行二次签名,官方文档的坑来了,官方并没有详细说明这一步骤,一切的一切只能靠自己摸索,爬坑。
首先第一坑:参数顺序,我这里用了SortedMap,自动对参数进行asc编码顺序,一劳永逸,当然,也可以用其他map,但一定要注意参数顺序,必须是asc编码顺序。
第二坑:参数package的Sign=WXPay中=的编码问题,转码即可,小坑。
第三坑:苹果系统的timestamp位数,统一成10位即可,小坑。
第四坑:次级大坑,注意,官方文档说到的参与二次签名的参数,prepayId,appId,timeStamp等,如果你用他们的驼峰进行大写,那么你就完了。一定要小写,小写,小写。
第五坑:最大坑,一样,官方文档并没有对于二次签名有过多赘述,如果你上面几个坑完美出坑,那么,你获取到的签名sign跟官方验证的sign绝对是一样的,然而,将这些玩意丢回给APP,APP调起支付,大大的几个字出现了,验证签名失败!WTF!不要急,我已折腾了好几天,终于发现坑在哪里,那就是noncestr随机字符串,参与二次签名的随机字符串不能再次生成,注意,不能再次生成,一定要用第一步中获取prepayid时的那串字符串,一定要用第一步中获取prepayid时的那串字符串,一定要用第一步中获取prepayid时的那串字符串。
第六坑:经历了上述5坑,相信你已经有想干死人的冲动,那么你以为这就结束了吗,还有最后一坑,那就是APP签名已经包名,一定要与开放平台中的一致,然而,即使一致了你以为又结束了吗,NO,如果你更改过开放平台中的签名,并且,在更改前调用过APP微信支付,那么一定一定一定一定记得清除微信缓存。
至此,所有坑都成功出坑,终于出现了支付页面,举国欢腾,微信去年买了个表。最后附上MD5签名类
import java.security.MessageDigest;
public class MD5 {
private MD5() {}
public final static String getMessageDigest(byte[] buffer) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(buffer);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
}
- 微信支付服务端的一些坑及最终解决
- 微信支付的一些坑
- 微信支付开发的一些坑
- 微信支付上的一些坑
- 微信支付的一些坑
- 微信支付APP客户端及服务端集成
- c# 微信支付,问题及解决
- 微信支付,服务端代码
- 微信支付服务端总结
- APP服务端微信支付(PHP服务端)
- 解决微信App支付服务端,App上提示“商户支付下单id非法”
- 如何使用golang实现微信支付的服务端
- java服务端,微信支付功能的实现
- 【手机app微信支付】app微信支付服务端(java)的实现
- 自己封装的微信支付与支付宝支付(服务端)
- 微信支付具体步骤和一些常见的坑
- 微信支付web开发遇到的一些坑
- 微信jsapi支付遇到的一些坑
- Android获取指定路径的图片(通过Drawable方式)
- Codeforces 330E Graph Reconstruction【随机化算法】Get it!
- Spring 顾问 代理 增强
- Git与Angular入门
- VUE中v-if和v-for介绍
- 微信支付服务端的一些坑及最终解决
- 再谈UI设计的入门与进阶
- 分析了六百万字的歌词数据,告诉你中国的Rapper都在唱些啥?
- NYOJ备用2344 盖伦的告白(线段树||双端队列)
- 利用easyUI的combobox打造自动搜索提示功能
- JSF使用百度地图,ak的错误问题
- UI设计师这样面试更容易成功
- MYSQL用户权限
- libsvm安装问题解决!!