当前位置:   article > 正文

微信小程序支付踩坑_微信支付noncestr每次都不一样吗

微信支付noncestr每次都不一样吗

微信小程序支付踩坑

一套最基本的微信小程序支付流程,是后台向微信发起预下单(统一下单)接口,返回预下单数据给前端——前端根据数据向微信后台发起支付api——用户支付或者取消——微信发送回调信息到开发者后台。
首先是预下单接口名称:
在这里插入多少描述
然后是参数:
在这里插入图片描述
正常并不需要把所有参数都传入,根据业务需求提交参数即可。
下面就聊聊我在开发过程中遇到的坑。
1:参数大小写问题
微信预下单接口数据传到微信后台后,微信会把数据做一次加密处理(也就是文档中的签名算法),这就代表开发者在返回数据给前端的时候,也需要做一次同样的加密处理,这样才能和微信后台的数据对应上。如下图
在这里插入图片描述
而微信官方文档中的签名算法上的参数名称,比如上图中的appid为全小写,但是事实上微信官方加密的时候是以appId作为参数名传入的,而当你用了全小写去加密返回数据给前端的时候,前端去调支付api接口失败,得到的失败原因也并不会告诉你是参数名错误,这样就造成了第一次接触的开发者完全不知道该如何下手。这就是第一个坑。

2:nonceStr参数问题
nonceStr这个参数,在后台发起预下单接口的时候是必须要传入的,前端去调用api的时候也需要这个参数,因为有签名算法的存在,正常开发过程中会觉得两个参数必须是同一个才行,微信文档也没有说明,真正的调用过程中这两个参数必须是不一样的,需要生成两次,再加上前面所说的参数出错微信不会返回具体是哪里出错,这就造成了这第二个坑。

3:timeStamp时间戳问题
这是一个小问题,只要细心读文档基本上不会踩,但是如果习惯性觉得时间戳是以毫秒为单位的就会踩进这第三个坑,因为微信需要的是以秒为单位,而且你出错了,微信也不会告诉是时间戳的问题,再加上你一旦自以为是得以为毫秒是对的时候,基本上出错的时候不会去看,因为毕竟是这么简单的一个参数,所以读微信的开发文档一定要细心。

下面是我的一个案例,包含预下单接口和会调接口:

	public JSONObject createUnifiedOrder(HttpServletRequest request, String openId, long childId, String price,
			String orderDetails, String project) {
//		Integer source = UsrExtUser.SOURCE_WX;
		JSONObject resultJson = new JSONObject();
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("YYYYMMddHHmmssSSS");
		SortedMap<String, String> paraMap = new TreeMap<String, String>();
		String body = "VIP会员充值";
		String nonceStr = UUIDUtil.genUUID().replaceAll("-", "");
		String outTradeNo = sdf.format(date);
		System.err.println("outTradeNo:" + outTradeNo);

		// 设置请求参数(小程序ID)
		paraMap.put("appid", wxEnviroment.getWx().get(project).get("appid"));
		// 设置请求参数(商户号)
		paraMap.put("mch_id", MCHID);
		// 设置请求参数(随机字符串)
		paraMap.put("nonce_str", nonceStr);
		// 设置请求参数(商品描述)
		paraMap.put("body", body);
		// 设置请求参数(商户订单号)
		paraMap.put("out_trade_no", outTradeNo);
		// 设置请求参数(总金额)
		paraMap.put("total_fee", price);
		// 设置请求参数(终端IP)
		paraMap.put("spbill_create_ip", WxUtil.getIpAddress(request));
		// 设置请求参数(通知地址)
		paraMap.put("notify_url", wxEnviroment.getWx().get(project).get("callback-url"));
		// 设置请求参数(交易类型)
		paraMap.put("trade_type", "JSAPI");
		// 设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid)
		paraMap.put("openid", openId);
		// 调用逻辑传入参数按照字段名的 ASCII 码从小到大排序(字典序)
		String stringA = WxUtil.formatUrlMap(paraMap, false, false);
		// 第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。(签名)
		String signTemp = stringA + "&key=" + MCHKEY;
		String sign = WxUtil.MD5(signTemp).toUpperCase();
		// 将参数 编写XML格式
		StringBuffer sb = new StringBuffer();
		sb.append("<xml>");
		sb.append("<appid>" + wxEnviroment.getWx().get(project).get("appid") + "</appid>");
		sb.append("<body>" + body + "</body>");
		sb.append("<mch_id>" + MCHID + "</mch_id>");
		sb.append("<nonce_str>" + paraMap.get("nonce_str") + "</nonce_str>");
		sb.append("<notify_url>" + paraMap.get("notify_url") + "</notify_url>");
		sb.append("<openid>" + paraMap.get("openid") + "</openid>");
		sb.append("<out_trade_no>" + paraMap.get("out_trade_no") + "</out_trade_no>");
		sb.append("<spbill_create_ip>" + paraMap.get("spbill_create_ip") + "</spbill_create_ip>");
		sb.append("<total_fee>" + paraMap.get("total_fee") + "</total_fee>");
		sb.append("<trade_type>" + paraMap.get("trade_type") + "</trade_type>");
		sb.append("<sign>" + sign + "</sign>");
		sb.append("</xml>");

		System.err.println("xml:" + sb.toString());
		// 发送请求(POST)(获得数据包ID)
		try {
			Map<String, String> map = WxUtil.doXMLParse(HttpConnectionUtil.post(WXPAY_URL, new String(sb.toString().getBytes(), "utf-8")));
			if (map.get("prepay_id") != null) {
				logger.info("微信小程序 统一下单 接口调用成功");
				/*
				 * 添加wx_user_oders表数据
				 */
				WxUserOrders wuo = new WxUserOrders();
				wuo.setUserId(childId);
				wuo.setAppOpenId(openId);
				wuo.setCommodityInfo(orderDetails);
				wuo.setOrderNumber(outTradeNo);
				wuo.setOrderStatus(0);
				wuo.setPayTime(date);
				wuo.setPrice(price);
				wuo.setProject(project);
				wxUserOrdersMapper.save(wuo);
				logger.info("数据库usr_user_orders添加数据成功");
			}
			SortedMap<String, String> para2Map = new TreeMap<String, String>();
			String nonceStr2 = UUIDUtil.genUUID().replaceAll("-", "");
			String timeStamp = Long.valueOf(System.currentTimeMillis() / 1000).toString();

			para2Map.put("appId", wxEnviroment.getWx().get(project).get("appid"));
			para2Map.put("timeStamp", timeStamp);
			para2Map.put("nonceStr", nonceStr2);
			para2Map.put("package", "prepay_id=" + map.get("prepay_id"));
			para2Map.put("signType", "MD5");

			String stringB = WxUtil.formatUrlMap(para2Map, false, false);
			String signTemp2 = stringB + "&key=" + MCHKEY;
			String sign2 = WxUtil.MD5(signTemp2).toUpperCase();

			resultJson.put("paySign", sign2);
			resultJson.put("signType", "MD5");
			resultJson.put("nonceStr", nonceStr2);
			resultJson.put("timeStamp", timeStamp);
			resultJson.put("package", "prepay_id=" + map.get("prepay_id"));

		} catch (UnsupportedEncodingException e) {
			logger.info("微信 统一下单 异常:" + e.getMessage());
			e.printStackTrace();
		} catch (Exception e) {
			logger.info("微信 统一下单 异常:" + e.getMessage());
			e.printStackTrace();
		}

		return resultJson;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
	public String acceptRsult(HttpServletRequest request, HttpServletResponse response,String pj) {
		String resXml = "";
		Map<String, String> map = new HashMap<String, String>();

		try {
			BufferedReader br = new BufferedReader(
					new InputStreamReader((ServletInputStream) request.getInputStream()));
			String line = null;
			StringBuilder sb = new StringBuilder();
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
			br.close();
			// sb为微信返回的xml
			String notityXml = sb.toString();
			map = WxUtil.doXMLParse(notityXml);
			logger.info("微信回调xml:" + notityXml);
		} catch (Exception e) {
			logger.error("解析微信支付回调错误:" + e.getMessage());
		}
		String returnCode = map.get("return_code");
		if ("SUCCESS".equals(returnCode)) {
			// 验证微信签名
			// 调用逻辑传入参数按照字段名的 ASCII 码从小到大排序(字典序)
			Map<String, String> paraMap = WxUtil.paraFilter(map);
			String stringA = WxUtil.formatUrlMap(paraMap, false, false);
			// 第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。(签名)
			String signTemp = stringA + "&key=" + MCHKEY;
			String sign = WxUtil.MD5(signTemp).toUpperCase();
			if (sign.equals(map.get("sign"))) {
				String orderNumber = map.get("out_trade_no");
				logger.info("订单号为:" + orderNumber);
				wxUserOrdersMapper.updateOrderStatus(orderNumber);
				WxUserOrders wuo = wxUserOrdersMapper.findByOrderType(orderNumber);
				if (wuo == null || !Objects.equals(wuo.getProject(),pj)){
					logger.error("order err {}",orderNumber);
				}
				String orderType = wuo.getCommodityInfo();
				long userId = wuo.getUserId();
				long buyTimeStamp = getBuyTimeStamp(orderType);
				String endTime = usrUserInfoMapper.queryEndTimeByUserId(userId);
				long nowTimeStamp = System.currentTimeMillis();
				if (endTime != null) {
					String str[] = endTime.split("-");
					int year = Integer.parseInt(str[0]);
					int month = Integer.parseInt(str[1]);
					int day = Integer.parseInt(str[2]);

					Calendar cal = Calendar.getInstance();
					cal.set(year, month - 1, day, 0, 0, 0);
					long endTimeStamp = cal.getTimeInMillis();

					// 会员权限未过期
					if (endTimeStamp > nowTimeStamp) {
						usrUserInfoMapper.updateVipInfo(new Date(), new Date(endTimeStamp + buyTimeStamp), UsrUserInfo.VIP_TYPE_BUY, userId);
					}
					// 会员权限已过期
					else if (endTimeStamp < nowTimeStamp) {
						usrUserInfoMapper.updateVipInfo(new Date(), new Date(nowTimeStamp + buyTimeStamp), UsrUserInfo.VIP_TYPE_BUY, userId);
					}
				} else if (endTime == null) {
					usrUserInfoMapper.updateVipInfo(new Date(), new Date(nowTimeStamp + buyTimeStamp), UsrUserInfo.VIP_TYPE_BUY, userId);
				}
				resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
						+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
			} else {
				resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
						+ "<return_msg><![CDATA[签名验证失败]]></return_msg>" + "</xml> ";
			}
		}
		return resXml;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

附上签名算法的方法:

	/**
	 * 
	 * 方法用途: 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序),并且生成url参数串
	 * 
	 * @param paraMap    要排序的Map对象
	 * @param urlEncode  是否需要URLENCODE
	 * @param keyToLower 是否需要将Key转换为全小写 true:key转化成小写,false:不转化
	 * @return
	 */
	public static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode, boolean keyToLower) {
		String buff = "";
		Map<String, String> tmpMap = paraMap;
		try {
			List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());
			// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
			Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
				@Override
				public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
					return (o1.getKey()).toString().compareTo(o2.getKey());
				}
			});
			// 构造URL 键值对的格式
			StringBuilder buf = new StringBuilder();
			for (Map.Entry<String, String> item : infoIds) {
				if (StringUtils.isNotBlank(item.getKey())) {
					String key = item.getKey();
					String val = item.getValue();
					if (urlEncode) {
						val = URLEncoder.encode(val, "utf-8");
					}
					if (keyToLower) {
						buf.append(key.toLowerCase() + "=" + val);
					} else {
						buf.append(key + "=" + val);
					}
					buf.append("&");
				}

			}
			buff = buf.toString();
			if (buff.isEmpty() == false) {
				buff = buff.substring(0, buff.length() - 1);
			}
		} catch (Exception e) {
			return null;
		}
		return buff;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/450146
推荐阅读
相关标签
  

闽ICP备14008679号