小程序微信支付java服务端

小程序微信支付java服务端_第1张图片

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 BaseResp> getOpenId(String code, HttpServletRequest request) throws Exception{
        if (StringUtils.isEmpty(code)) {
            return new BaseResp>(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);
        Map result = new HashMap();
        JSONObject jsonObject = JSONObject.parseObject(httpRet);
        if (jsonObject != null) {
            Integer errcode = jsonObject.getInteger("errcode");
            if (errcode != null) {
                //返回异常信息
                return new BaseResp>(errcode, jsonObject.getString("errmsg"), null);
            }
            result.put("openId", jsonObject.getString("openid"));
            result.put("sessionKey", jsonObject.getString("session_key"));
        }
        return new BaseResp>(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 BaseResp> doUnifiedOrder(@Valid WeixinInVo inVo, HttpServletRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            List list = bindingResult.getAllErrors();
            return new BaseResp>(ResultStatus.FAIL.getErrorCode(), list.get(0).getDefaultMessage(), null);
        }

        //根据id获取订单信息
        SoOut soOut = soService.get(inVo.getSoId());
        if (soOut == null) {
            return new BaseResp>(ResultStatus.error_so_not_exist);
        }
        //订单已支付
        if (soOut.getSoStatus() != So.SO_STATUS_WAIT_PAID) {
            return new BaseResp>(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();
        //统一下单接口
        HashMap data = new HashMap();
        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());

        //返回给小程序端需要的参数
        Map response = new HashMap();
        response.put("appId", config.getAppID());
        try {
            Map 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 BaseResp>(ResultStatus.SUCCESS, response);
            }else{
                return new BaseResp>(ResultStatus.error_unified_order_fail.getErrorCode(), rMap.get("err_code_des"), null);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new BaseResp>(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 = "" + "" + "" + " ";
                } else {
                    // 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功
                    if (So.SO_STATUS_WAIT_PAID == soOut.getSoStatus()) {//支付的状态判断
                        //订单状态的修改。根据实际业务逻辑执行
                        int ret = soService.paid(inVo);
                        resXml = "" + "" + "" + " ";
                    } else {
                        resXml = "" + "" + "" + " ";
                        logger.info("订单已处理");
                    }
                }
            } else {
                logger.info("支付失败,错误信息:" + notifyMap.get("err_code"));
                resXml = "" + "" + "" + " ";
            }
        } else {
            // 签名错误,如果数据里没有sign字段,也认为是签名错误
            resXml = "" + "" + "" + " ";
            logger.info("通知签名验证失败");
        }
        //------------------------------
        //处理业务完毕
        //------------------------------
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();
    }

一定要注意参数的大小写,字符编码为UTF-8。

pom.xml增加微信sdk:

        
            com.github.wxpay
            wxpay-sdk
            0.0.3
        

另外sdk的github地址为:https://github.com/wxpay/WXPay-SDK-Java。可以自己进行修改和编译打包。

如果有其他问题,请联系我。


你可能感兴趣的:(微信小程序)