美文网首页小程序
微信扫码支付、网站接入微信支付-java

微信扫码支付、网站接入微信支付-java

作者: 码农笔录 | 来源:发表于2018-05-03 00:03 被阅读516次

    如果你的网站想接入微信支付,那么你的有个公众号(微信公众平台),然后开通支付功能,在微信商户平台操作。仔细看哦,这是两个平台,商家平台有详细的接入流程,这里只介绍程序方面。

    1、准备

    • 准备商家帐户
    • 下载证书,重置密钥(密钥重置后请妥善保管)
    • 内网穿透软件(微信支付成功后会有回调)

    2、代码

    • 加入依赖

    微信支付比较麻烦,所以我们采用第三方封装的jar包

    compile group: 'com.github.binarywang', name: 'weixin-java-pay', version: '3.0.0'
    
    • 新建 pay.properties
    #wxpay
    WX.APPID=wxdd088f0c1d70cbf9
    WX.MCHID=你的商户id
    WX.MCHKEY=你的密钥
    WX.SUBAPPID=
    WX.SUBMCHID=
    WX.KEYPATH=
    #下面这个是微信回调地址,要保证外网可以访问,内网测试就用内网穿透软件
    WX.NOTIFYURL=http://tdcloud.trmap.cn/reward/getOrderNotifyResult
    WX.TRADETYPE=NATIVE
    
    • 配置类

    WxPayConfig 将配置文件里的值读取出来,微信支付需要的相关配置

    
    @Configuration
    @PropertySource(value="classpath:pay.properties", ignoreResourceNotFound=true)
    public class WxPayConfig {
    
      /**
       * http请求连接超时时间
       */
      private int httpConnectionTimeout = 5000;
    
      /**
       * http请求数据读取等待时间
       */
      private int httpTimeout = 10000;
    
      private String appId;
      private String subAppId;
      private String mchId;
      private String mchKey;
      private String subMchId;
      private String notifyUrl;
      private String tradeType;
      private String signType;
      private SSLContext sslContext;
      private String keyPath;
      private boolean useSandboxEnv = false;
      private String httpProxyHost;
      private Integer httpProxyPort;
      private String httpProxyUsername;
      private String httpProxyPassword;
    
      public String getKeyPath() {
        return keyPath;
      }
    
      /**
       * 设置证书
       *
       * @param keyPath apiclient_cert.p12的文件的绝对路径
       */
      public void setKeyPath(String keyPath) {
        this.keyPath = keyPath;
      }
    
      /**
       * 商户号
       */
      public String getMchId() {
        return this.mchId;
      }
    
      public void setMchId(String mchId) {
        this.mchId = mchId;
      }
    
      /**
       * 商户密钥
       */
      public String getMchKey() {
        return this.mchKey;
      }
    
      public void setMchKey(String mchKey) {
        this.mchKey = mchKey;
      }
    
      /**
       * 公众号appid
       */
      public String getAppId() {
        return this.appId;
      }
    
      public void setAppId(String appId) {
        this.appId = appId;
      }
    
      /**
       * 服务商模式下的子商户公众账号ID
       */
      public String getSubAppId() {
        return this.subAppId;
      }
    
      public void setSubAppId(String subAppId) {
        this.subAppId = subAppId;
      }
    
      /**
       * 服务商模式下的子商户号
       */
      public String getSubMchId() {
        return this.subMchId;
      }
    
      public void setSubMchId(String subMchId) {
        this.subMchId = subMchId;
      }
    
      /**
       * 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。
       */
      public String getNotifyUrl() {
        return this.notifyUrl;
      }
    
      public void setNotifyUrl(String notifyUrl) {
        this.notifyUrl = notifyUrl;
      }
    
      /**
       * 交易类型
       * <pre>
       * JSAPI--公众号支付
       * NATIVE--原生扫码支付
       * APP--app支付
       * </pre>
       */
      public String getTradeType() {
        return this.tradeType;
      }
    
      public void setTradeType(String tradeType) {
        this.tradeType = tradeType;
      }
    
      /**
       * 签名方式
       * 有两种HMAC_SHA256 和MD5
       * @see com.github.binarywang.wxpay.constant.WxPayConstants.SignType
       */
      public String getSignType() {
        return this.signType;
      }
    
      public void setSignType(String signType) {
        this.signType = signType;
      }
    
      public SSLContext getSslContext() {
        return this.sslContext;
      }
    
      public void setSslContext(SSLContext sslContext) {
        this.sslContext = sslContext;
      }
    
      /**
       * 微信支付是否使用仿真测试环境
       * 默认不使用
       */
      public boolean useSandbox() {
        return this.useSandboxEnv;
      }
    
      /**
       * 设置是否使用沙箱仿真测试环境
       */
      public void setUseSandboxEnv(boolean useSandboxEnv) {
        this.useSandboxEnv = useSandboxEnv;
      }
    
      public SSLContext initSSLContext() throws WxPayException {
        if (StringUtils.isBlank(this.getMchId())) {
          throw new WxPayException("请确保商户号mchId已设置");
        }
    
        if (StringUtils.isBlank(this.getKeyPath())) {
          throw new WxPayException("请确保证书文件地址keyPath已配置");
        }
    
        InputStream inputStream;
        final String prefix = "classpath:";
        String fileHasProblemMsg = "证书文件【" + this.getKeyPath() + "】有问题,请核实!";
        String fileNotFoundMsg = "证书文件【" + this.getKeyPath() + "】不存在,请核实!";
        if (this.getKeyPath().startsWith(prefix)) {
          String path = StringUtils.removeFirst(this.getKeyPath(), prefix);
          if (!path.startsWith("/")) {
            path = "/" + path;
          }
          inputStream = WxPayConfig.class.getResourceAsStream(path);
          if (inputStream == null) {
            throw new WxPayException(fileNotFoundMsg);
          }
        } else {
          try {
            File file = new File(this.getKeyPath());
            if (!file.exists()) {
              throw new WxPayException(fileNotFoundMsg);
            }
    
            inputStream = new FileInputStream(file);
          } catch (IOException e) {
            throw new WxPayException(fileHasProblemMsg, e);
          }
        }
    
        try {
          KeyStore keystore = KeyStore.getInstance("PKCS12");
          char[] partnerId2charArray = this.getMchId().toCharArray();
          keystore.load(inputStream, partnerId2charArray);
          this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();
          return this.sslContext;
        } catch (Exception e) {
          throw new WxPayException(fileHasProblemMsg, e);
        } finally {
          IOUtils.closeQuietly(inputStream);
        }
      }
    
      /**
       * http请求连接超时时间
       */
      public int getHttpConnectionTimeout() {
        return this.httpConnectionTimeout;
      }
    
      public void setHttpConnectionTimeout(int httpConnectionTimeout) {
        this.httpConnectionTimeout = httpConnectionTimeout;
      }
    
      /**
       * http请求数据读取等待时间
       */
      public int getHttpTimeout() {
        return this.httpTimeout;
      }
    
      public void setHttpTimeout(int httpTimeout) {
        this.httpTimeout = httpTimeout;
      }
    
      public String getHttpProxyHost() {
        return httpProxyHost;
      }
    
      public void setHttpProxyHost(String httpProxyHost) {
        this.httpProxyHost = httpProxyHost;
      }
    
      public Integer getHttpProxyPort() {
        return httpProxyPort;
      }
    
      public void setHttpProxyPort(Integer httpProxyPort) {
        this.httpProxyPort = httpProxyPort;
      }
    
      public String getHttpProxyUsername() {
        return httpProxyUsername;
      }
    
      public void setHttpProxyUsername(String httpProxyUsername) {
        this.httpProxyUsername = httpProxyUsername;
      }
    
      public String getHttpProxyPassword() {
        return httpProxyPassword;
      }
    
      public void setHttpProxyPassword(String httpProxyPassword) {
        this.httpProxyPassword = httpProxyPassword;
      }
    }
    
    
    • 发起支付,获取生成二维码的地址

    微信支付的单位是分,例如你支付金额是9.9元(保留两位小数),那你微信支付的时候支付金额是9.9元*100 = 990分,最后支付金额是整数。

    封装请求参数,请求微信接口后会返回生成二维码的地址

        @Autowired
        private WxPayConfig payConfig;
        @Autowired
        private WxPayService wxService; //上边依赖第三方库提供的
    
        
        @RequestMapping("wxpay")
        private ResponseEntity<Result> rewardWxpay(@RequestParam Double money, HttpServletRequest request,WxPayUnifiedOrderRequest wxreq){
            try {
                    
        @RequestMapping("wxpay")
        private ResponseEntity<Result> rewardWxpay(String userid,@RequestParam String produceid,@RequestParam Double money,String remark, HttpServletRequest request,WxPayUnifiedOrderRequest wxreq){
            try {
                // TODO 生成你的订单信息,然后返回订单id,这里代码忽略,根据自己的业务调整
                String ip = req.getHeader("x-forwarded-for");
                if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                   ip = req.getHeader("Proxy-Client-IP");
                }
                if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                       ip = req.getHeader("WL-Proxy-Client-IP");
                }
                 if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                       ip = req.getRemoteAddr();
                }
                if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {  
                    // 多次反向代理后会有多个ip值,第一个ip才是真实ip
                    if( ip.indexOf(",")!=-1 ){
                        ip = ip.split(",")[0];
                    }
                }
                request.setBody("实景在线打赏-"); // 支付标题
                request.setOutTradeNo(你自己的订单编号);
                // 金额一定要以分为单位,所以最后支付金额为整数
                String string = String.valueOf((reward.getMoney()*100));
                request.setTotalFee(Integer.parseInt(string.substring(0,string.indexOf("."))));
                request.setAppid(payConfig.getAppId());
                request.setMchId(payConfig.getMchId());
                request.setNotifyUrl(payConfig.getNotifyUrl());
                request.setSpbillCreateIp(ip);
                
                request.setTradeType(payConfig.getTradeType());
                //this.wxService.unifiedOrder(request) 发起请求,获取地址,然后根据地址生成二维码
                return SUCCESS(this.wxService.unifiedOrder(request););
            } catch (WxPayException e) {
                e.printStackTrace();
            }
            return ERROR("发起支付失败");
        }
            } catch (WxPayException e) {
                e.printStackTrace();
            }
            return ERROR("发起支付失败");
        }
    
    • 生成二维码

    根据上一步请求返回的地址生成二维码,生成的二维码是base64格式的字节码,前台用img标签直接显示即可,这时候用户就可以扫描生成的二维码进行支付了

    /**
         * <pre>
         * 扫码支付模式二生成二维码的方法
         * 对应链接格式:weixin://wxpay/bizpayurl?sr=XXXXX。请商户调用第三方库将code_url生成二维码图片。
         * 该模式链接较短,生成的二维码打印到结账小票上的识别率较高。
         * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5
         * </pre>
         *
         * @param codeUrl
         *            微信返回的交易会话的二维码链接
         * @param logoFile
         *            商户logo图片的文件对象,可以为空
         * @param sideLength
         *            要生成的二维码的边长,如果为空,则取默认值400
         * @return 生成的二维码的字节数组
         */
    
        @PostMapping(value = "/createScanPayQrcode")
        public ResponseEntity<Result> createQrcode(String codeUrl,File logoFile, Integer sideLength) {
            byte[] by = this.wxService.createScanPayQrcodeMode2(codeUrl, logoFile, sideLength);
            return SUCCESS(by);
        }
    
    • 回调接口

    用户扫描支付后,微信会异步通知,请求地址为配置文件中的接口地址,所以要保证公网可以访问。微信共会请求8次回调接口,如果处理成功后,将不在请求回调接口

    
        /**
         * 读取支付结果通知
         *
         * @param xmlData
         * @throws WxPayException 
         */
        @RequestMapping(value = "/getOrderNotifyResult", method = RequestMethod.POST)
        public String getOrderNotifyResult(@RequestBody String xmlData,HttpServletRequest request,
                HttpServletResponse response) throws WxErrorException, WxPayException {
            WxPayOrderNotifyResult orderNotifyResult = this.wxService.parseOrderNotifyResult(xmlData);
            String noticeStr = null;
            try {
                BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
    
                // 支付成功,商户处理后同步返回给微信参数
                if (!orderNotifyResult.getResultCode().equals("SUCCESS")) {
                    // 支付失败, 记录流水失败
                    System.out.println("===============支付失败==============");
                    noticeStr = setXML("FAIL", "支付失败");
                    outputStream.write(noticeStr.getBytes());
                    outputStream.flush();
                    outputStream.close();
                } else {
                    // TODO 处理你的业务,修改订单状态等
                    // 通知微信已经收到消息,不要再给我发消息了,否则微信会8连击调用本接口
                    noticeStr = setXML("SUCCESS", "支付成功");
                    outputStream.write(noticeStr.getBytes());
                    outputStream.flush();
                    outputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return noticeStr;
        }
        
        public String setXML(String return_code, String return_msg) {
                return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
        }
    

    到这里,网站接入微信扫码支付,代码部分就全部完成了,最后效果图请看下方图片。


    这里写图片描述
    这里写图片描述

    关注

    如果有问题,请在下方评论,或者加群讨论 200909980

    关注下方微信公众号,可以及时获取到各种技术的干货哦,如果你有想推荐的帖子,也可以联系我们的。

    码农笔录二维码

    相关文章

      网友评论

        本文标题:微信扫码支付、网站接入微信支付-java

        本文链接:https://www.haomeiwen.com/subject/ihihrftx.html