1、首先可以通过服务端来获取openid,openid可以作为自己平台微信用户身份的唯一标识。
/** * @Description: 获取openId * @param: code 小程序授权后获得的code * @Author: zhangpeng32 * @Date: 2018/3/11 17:39 * @Version: 1.0.0 */ @RequestMapping(value = "/getOpenId", method = RequestMethod.POST) public BaseRespMapString, String getOpenId(String code, HttpServletRequest request) throws Exception{ if (StringUtils.isEmpty(code)) { return new BaseRespMapString, String(ResultStatus.error_weixin_user_code_empty); } //获取openId String param = "?grant_type=" + PaymentConfig.GRANT_TYPE + "&appid=" + PaymentConfig.APPID + "&secret=" + PaymentConfig.API_KEY + "&js_code=" + code; System.out.println(PaymentConfig.GET_OPEN_ID_URL + param); //创建请求对象 String httpRet = PayUtils.httpRequest(PaymentConfig.GET_OPEN_ID_URL, "GET", param); System.out.println(httpRet); MapString, String result = new HashMapString, String(); JSONObject jsonObject = JSONObject.parseObject(httpRet); if (jsonObject != null) { Integer errcode = jsonObject.getInteger("errcode"); if (errcode != null) { //返回异常信息 return new BaseRespMapString, String(errcode, jsonObject.getString("errmsg"), null); } result.put("openId", jsonObject.getString("openid")); result.put("sessionKey", jsonObject.getString("session_key")); } return new BaseRespMapString, String(ResultStatus.SUCCESS, result); }2、调用支付统一下单API来获取prepay_id,并将小程序调起支付数据需要签名的字段appId,timeStamp,nonceStr,package再次签名。
特别要注意,调用官方的SDK默认的加密方式为HMACSHA256,SKD里面wxpay的初始化源码如下:
public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception { this.config = config; this.notifyUrl = notifyUrl; this.autoReport = autoReport; this.useSandbox = useSandbox; this.signType = SignType.MD5; if (useSandbox) { this.signType = SignType.MD5; // 沙箱环境 } else { this.signType = SignType.HMACSHA256; } this.wxPayRequest = new WXPayRequest(config); }我们在调用统一下单签名的时候,可以直接把这里的sign_type默认改为MD5
public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception { this.config = config; this.notifyUrl = notifyUrl; this.autoReport = autoReport; this.useSandbox = useSandbox; this.signType = SignType.MD5;/* if (useSandbox) { this.signType = SignType.MD5; // 沙箱环境 } else { this.signType = SignType.HMACSHA256; }*/ this.wxPayRequest = new WXPayRequest(config); }使用微信支付的SDK调用微信支付比较简单,最新的SDK也不需要自己处理xml和map之间的转化,接口里面包含了签名的方法和签名验证的方法,在我调用统一下单接口的时候,开始报签名错误,自己要注意一下。可以通过微信签名验证工具来进行验证,工具的地址为:https://pay.weixin.qq.com/wiki/tools/signverify/。此处WXPayUtil为SDK自带的工具类,WXPayUtil.generateSignature生成签名,WXPayUtil.isSignatureValid直接可以在代码中组签名验证。
@RequestMapping(value = "/doUnifiedOrder", method = RequestMethod.POST) public BaseRespMapString, String doUnifiedOrder(@Valid WeixinInVo inVo, HttpServletRequest request, BindingResult bindingResult) { if (bindingResult.hasErrors()) { ListObjectError list = bindingResult.getAllErrors(); return new BaseRespMapString, String(ResultStatus.FAIL.getErrorCode(), list.get(0).getDefaultMessage(), null); } //根据id获取订单信息 SoOut soOut = soService.get(inVo.getSoId()); if (soOut == null) { return new BaseRespMapString, String(ResultStatus.error_so_not_exist); } //订单已支付 if (soOut.getSoStatus() != So.SO_STATUS_WAIT_PAID) { return new BaseRespMapString, String(ResultStatus.error_so_paid); } //生成的随机字符串 String nonce_str = WXPayUtil.generateNonceStr(); //获取客户端的ip地址 String spbill_create_ip = IpUtils.getIpAddr(request); int price100 = soOut.getSoAmount().multiply(new BigDecimal(100)).intValue(); //统一下单接口 HashMapString, String data = new HashMapString, String(); data.put("appid", config.getAppID()); data.put("mch_id", config.getMchID()); data.put("nonce_str", nonce_str); data.put("body", soOut.getSkuName()); //商品描述 data.put("out_trade_no", soOut.getId().toString());//商户订单号 data.put("total_fee", String.valueOf(price100));//支付金额,这边需要转成字符串类型,否则后面的签名会失败 data.put("spbill_create_ip", spbill_create_ip); data.put("notify_url", PaymentConfig.WX_NOTIFY_URL);//支付成功后的回调地址 data.put("trade_type", PaymentConfig.TRADE_TYPE);//支付方式 data.put("openid", inVo.getOpenId()); //返回给小程序端需要的参数 MapString, String response = new HashMapString, String(); response.put("appId", config.getAppID()); try { MapString, String rMap = wxpay.unifiedOrder(data); System.out.println("统一下单接口返回: " + rMap); String return_code = (String) rMap.get("return_code");//返回状态码 String result_code = (String) rMap.get("result_code");// String nonceStr = WXPayUtil.generateNonceStr(); response.put("nonceStr", nonceStr); Long timeStamp = System.currentTimeMillis() / 1000; if ("SUCCESS".equals(return_code) && return_code.equals(result_code)) { String prepayid = rMap.get("prepay_id");// response.put("prepayid", rMap.get("prepay_id")); response.put("package", "prepay_id="+prepayid); response.put("signType", "MD5"); response.put("timeStamp", timeStamp + "");//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误 System.out.println("二次签名参数response : "+response); //再次签名,这个签名用于小程序端调用wx.requesetPayment方法 String sign = WXPayUtil.generateSignature(response, PaymentConfig.API_KEY); response.put("paySign", sign); System.out.println("生成的签名paySign : "+ sign); return new BaseRespMapString, String(ResultStatus.SUCCESS, response); }else{ return new BaseRespMapString, String(ResultStatus.error_unified_order_fail.getErrorCode(), rMap.get("err_code_des"), null); } } catch (Exception e) { e.printStackTrace(); return new BaseRespMapString, String(ResultStatus.FAIL, response); } }注意:所有的签名加密方式一定要全部是MD5,这个问题卡了我两天,最后跟进代码到sdk源码中才找到问题。
3、微信支付结果通知,这里就是统一支付接口定义的回调url,只能在公网上进行测试验证。
比如我定义的是:
//支付成功后的服务器回调url public static final String WX_NOTIFY_URL = "https://xxxxxx.com/wxpay/wxNotify"; @RequestMapping(value = "/wxNotify") public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception { //读取参数 BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream())); String line = null; StringBuilder sb = new StringBuilder(); while ((line = br.readLine()) != null) { sb.append(line); } //sb为微信返回的xml String notifyData = sb.toString(); //支付结果通知的xml格式数据 System.out.println("支付结果通知的xml格式数据:" + notifyData); Map notifyMap = WXPayUtil.xmlToMap(notifyData); // 转换成map String resXml = ""; if (wxpay.isPayResultNotifySignatureValid(notifyMap)) { // 签名正确 if ("SUCCESS".equals(notifyMap.get("result_code"))) { //这里是支付成功 //////////执行自己的业务逻辑//////////////// String mch_id = (String) notifyMap.get("mch_id"); //商户号 String openid = (String) notifyMap.get("openid"); //用户标识 String out_trade_no = (String) notifyMap.get("out_trade_no"); //商户订单号 String total_fee = (String) notifyMap.get("total_fee"); String transaction_id = (String) notifyMap.get("transaction_id"); //微信支付订单号 //查询订单 根据订单号查询订单 SoOut -订单实体类 Long soId = Long.valueOf(out_trade_no); SoOut soOut = soService.get(soId); if (!PaymentConfig.MCH_ID.equals(mch_id) || soOut == null || new BigDecimal(total_fee).compareTo(soOut.getSoAmount().multiply(new BigDecimal(100))) != 0) { logger.info("支付失败,错误信息:" + "参数错误"); resXml = "xml" + "return_code![CDATA[FAIL]]/return_code" + "return_msg![CDATA[参数错误]]/return_msg" + "/xml "; } else { // 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功 if (So.SO_STATUS_WAIT_PAID == soOut.getSoStatus()) {//支付的状态判断 //订单状态的修改。根据实际业务逻辑执行 int ret = soService.paid(inVo); resXml = "xml" + "return_code![CDATA[SUCCESS]]/return_code" + "return_msg![CDATA[OK]]/return_msg" + "/xml "; } else { resXml = "xml" + "return_code![CDATA[SUCCESS]]/return_code" + "return_msg![CDATA[OK]]/return_msg" + "/xml "; logger.info("订单已处理"); } } } else { logger.info("支付失败,错误信息:" + notifyMap.get("err_code")); resXml = "xml" + "return_code![CDATA[FAIL]]/return_code" + "return_msg![CDATA[报文为空]]/return_msg" + "/xml "; } } else { // 签名错误,如果数据里没有sign字段,也认为是签名错误 resXml = "xml" + "return_code![CDATA[FAIL]]/return_code" + "return_msg![CDATA[通知签名验证失败]]/return_msg" + "/xml "; logger.info("通知签名验证失败"); } //------------------------------ //处理业务完毕 //------------------------------ BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); }一定要注意参数的大小写,字符编码为UTF-8。
pom.xml增加微信sdk:
dependency groupIdcom.github.wxpay/groupId artifactIdwxpay-sdk/artifactId version0.0.3/version /dependency另外sdk的github地址为:https://github.com/wxpay/WXPay-SDK-Java。可以自己进行修改和编译打包。
如果有其他问题,请联系我。













