• 微信小程序开发-微信支付功能【WxMaService 获取openid,WxPayService建微信订单,附有完整前后端代码】


    前提:对小程序开发有一定的基础;小程序已发布使用,已开通微信支付,关联商户号。

    微信小程序平台:小程序平台
    微信开发者文档:开发者文档
    微信小程序支付API 地址:微信支付文档地址
    微信支付平台:微信支付平台地址

    一、微信公众平台设置

    1、微信支付设置

    开通微信支付和商户号,关联商户号【需公司信息、银行账户等,按提示逐步完成即可】:
    在这里插入图片描述

    2、商户平台设置

    登录微信支付商户平台- 账户中心 - 账户设置 -API安全-申请证书、设置秘钥【按提示逐步完成即可,秘钥必须32位】,使用超级管理员账号进行设置;下载API证书apiclient_cert.p12的文件

    在这里插入图片描述

    二、代码实现

    1、java后端引入依赖、设置微信小程序配置

    Java后端使用的是jeecgboot框架

    1)pom.xml 引入依赖

    	    <!--微信小程序-->
            <dependency>
                <groupId>com.github.binarywang</groupId>
                <artifactId>weixin-java-miniapp</artifactId>
                <version>4.2.0</version>
            </dependency>
    
            <!--微信小程序 支付-->
            <dependency>
                <groupId>com.github.binarywang</groupId>
                <artifactId>weixin-java-pay</artifactId>
                <version>4.2.0</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2)application.yml配置

    #小程序
    wechat:
      appid: 
      secret: 
      mchId: #微信支付商户号
      mchKey: #微信支付商户密钥
      keyPath: classpath:apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
      tradeType: JSAPI  #JSAPI--公众号支付    NATIVE--原生扫码支付   APP--app支付
      notifyUrl:  https://www.XXXX.com/wx/wxOrder/notify: #微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    keyPath对应的apiclient_cert.p12文件就是在微信商户平台下载的API证书文件

    3)微信小程序配置文件

    (1)微信小程序配置文件:

    @AllArgsConstructor
    @Configuration
    @ConditionalOnClass({WxMaService.class, WxPayService.class})
    @EnableConfigurationProperties(WxProperties.class)
    public class WxConfig {
        private final WxProperties wxProperties;
    
        /**
         * 小程序配置
         * @return
         */
        @Bean
        @ConditionalOnMissingBean
        public WxMaConfig wxMaConfig() {
            WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
            config.setAppid(this.wxProperties.getAppId());
            config.setSecret(this.wxProperties.getSecret());
            return config;
        }
    
        @Bean
        public WxMaService wxMaService(WxMaConfig maConfig) {
            WxMaService service = new WxMaServiceImpl();
            service.setWxMaConfig(maConfig);
            return service;
        }
    
        /**
         * 支付配置
         *
         * @return
         */
        @Bean
        @ConditionalOnMissingBean
        public WxPayService wxService() {
            WxPayConfig payConfig = new WxPayConfig();
            payConfig.setAppId(StringUtils.trimToNull(this.wxProperties.getAppId()));
            payConfig.setMchId(StringUtils.trimToNull(this.wxProperties.getMchId()));
            payConfig.setMchKey(StringUtils.trimToNull(this.wxProperties.getMchKey()));
            payConfig.setKeyPath(StringUtils.trimToNull(this.wxProperties.getKeyPath()));
            payConfig.setTradeType(StringUtils.trimToNull(this.wxProperties.getTradeType()));
            payConfig.setNotifyUrl(StringUtils.trimToNull(this.wxProperties.getNotifyUrl()));
    
            // 可以指定是否使用沙箱环境
            payConfig.setUseSandboxEnv(false);
    
            WxPayService wxPayService = new WxPayServiceImpl();
            wxPayService.setConfig(payConfig);
            return wxPayService;
        }
    }
    
    • 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

    (2)微信小程序属性

    @Data
    @Component
    @ConfigurationProperties(prefix = "wechat")
    public class WxProperties {
        private String appId;
        private String secret;
        //微信支付商户号
        private String mchId;
        //微信支付商户密钥
        private String mchKey;
        // p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath;开头)
        private String keyPath;
        private String tradeType;
        private String notifyUrl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2、获取openid

    文档:微信小程序登录API接口地址

    1)小程序wx.login代码参考:

    参数、结果接收按照自己的代码实际情况设置:

      // 登录动作
      doLogin: function () {
        let that = this;
        wx.login({
          success: function (loginRes) {
            if (loginRes.code) {
              wx.getUserInfo({
                withCredentials: true, // 非必填, 默认为true
                success: function (infoRes) {
                  //获取openid
                  wx.request({
                    url: loginUrl,
                    data: {
                      code: loginRes.code, // 临时登录凭证
                      rawData: infoRes.rawData, // 用户非敏感信息
                      signature: infoRes.signature, // 签名
                      encryptedData: infoRes.encryptedData, // 用户敏感信息
                      ivStr: infoRes.iv // 解密算法的向量
                    },
                    success: function (res) {
                      res = res.data;
                      if (res.code == 1) {
                        that.globalData.userInfo = JSON.parse(infoRes.rawData);
                        wx.setStorageSync('openid', res.data.openId);
                        wx.setStorageSync('loginFlag', res.data.sessionKey);
                      } else {
                        that.showInfo(res.info);
                      }
                    },
                    fail: function (error) {
                      //调用接口失败
                    }
                  });
                },
                fail: function (error) {
                  // 获取 userInfo 失败,去检查是否未开启权限
                }
              });
            } else {
              // 获取 code 失败
            }
          },
          fail: function (error) {
            // 调用 wx.login 接口失败
           
          }
        });
      },
    
    • 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

    2)Java后端代码参考

    访问后端的url: loginUrl对应的登录获取openid方法:

    controller层:

      /**
         * 执行微信端登录
         *
         * @param code          : 微信登录code
         * @param encryptedData : 包括敏感数据在内的完整用户信息的加密数据
         * @param ivStr         : 加密算法的初始向量
         * @return : org.dpkj.bean.Result
         */
     @RequestMapping("login")
        public Result login(String code, String encryptedData, String ivStr) {
            Result result = new Result();
            try {
                WxUserBean result1 = userService.doLogin(code, encryptedData, ivStr);
                result.setCode(ResultState.OK);
                result.setInfo("oponId获取成功");
                result.setData(result1);
            } catch (Exception ex) {
                ex.printStackTrace();
                logger.info(ex.getMessage());
                result.setInfo("接口异常,请联系管理员!");
                result.setCode(ResultState.NO);
            }
            return result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    serviceImpl层:

      @Autowired
        private WxMaService wxMaService;
    
      @Override
        public WxUserBean doLogin(String code, String encryptedData, String ivStr) throws Exception {
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
            WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionInfo.getSessionKey(), encryptedData, ivStr);
            
            //数据库操作
            
            bean.setSessionKey(sessionInfo.getSessionKey());
            return bean;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3、实现微信支付

    1)小程序wx.requestPayment代码参考:

    微信支付API接口:支付API接口
    参数、结果接收按照自己的代码实际情况设置:

     startTestPay: function (e) {
        let data = {
          pickPhone: wx.getStorageSync("phoneNumber"),
          openId: wx.getStorageSync("openid"),
          testid: id,
          title: title,
        }
        if (data.pickTest && data.pickPhone && data.openId) {
          //正则判断手机号
          if (/^[1][3,4,5,7,8,9][0-9]{9}$/.test(data.pickPhone)) {
            data.amount = price * 100; //把价格换成分
            if (app.globalData.userInfo) {
              //插入订单
              if(price > 0){
                wx.request({
                  url: _createOrder,
                  data: data,
                  method: 'post',
                  success: function (res) {
                    if (res.data.success) {
                      let resData = res.data.result;
                       //吊起微信支付
                       wx.requestPayment({
                        nonceStr: resData.nonceStr,
                        package: resData.packageValue,
                        paySign: resData.paySign,
                        timeStamp: resData.timeStamp,
                        signType: resData.signType,
                        success() {
                           console.log("支付成功");
                           wx.navigateTo({
                            url: url
                          })
                        },
                        fail() {
                          //微信支付出现问题,请稍后再试
                        }
                    })
                    
                    } else {
                    }
                  },
                  fail: function (res) {
                    //检查用户出现问题,请稍后再试
                  },
                });
              }else{
                  //价格为0,直接进入目标页面
                  wx.navigateTo({
                    url: url
                  })
              }
            } else {
              wx.showToast({
                title: '微信环境异常,请重新进入小程序后尝试',
                icon: 'none'
              })
            }
          } else {
            wx.showToast({
              title: '请填写正确的手机号码',
              icon: 'none'
            })
          }
        } else {
          wx.showToast({
            title: '请完善用户信息',
            icon: 'none'
          })
        }
      },
    
    • 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

    2)Java后端创建订单

    controller层

     /**
         * @param data 所有请求参数
         * @Description: 统一下单接口
         * @Return: org.jeecg.common.api.vo.Result
         */
        @RequestMapping("createOrder")
        public Result createOrder(@RequestBody Map<String, Object> data) {
            try {
                return Result.ok(wxOrderService.createOrder(data));
            } catch (Exception ex) {
                ex.printStackTrace();
                return Result.error(ex.getMessage());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    serviceImpl层

    
        @Autowired
        private WxPayService wxPayService;
     
     @Override
        @Transactional(rollbackFor = Exception.class)
        public Map<String, Object> createOrder(Map<String, Object> map) throws Exception {
            String type = "paytest";//类型
            String eventId = map.get("testid").toString();//测评id
            String remark = map.get("title").toString();//测评标题
            String customerId = map.get("openId") == null ? "" : map.get("openId").toString();//测评人id
            String openId = map.get("openId").toString(); //用户微信标识
            String description = map.get("title").toString();//商品描述
            String amount = map.get("amount").toString();//付款金额
            
            //保存微信订单
            PayOrder wxOrder = new PayOrder();
            wxOrder.setOpenId(openId);
            wxOrder.setDescription(description);
            wxOrder.setRemark(remark);
            wxOrder.setEventId(eventId);
            wxOrder.setEventModule("paytest");
            wxOrder.setPayerTotal(Double.parseDouble(amount) / 100);
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
            StringBuffer sb = new StringBuffer();
            sb.append(dateTimeFormatter.format(LocalDateTime.now()));
            sb.append(new Random().nextInt(9999));
            wxOrder.setOutTradeNo(sb.toString()); //时间戳+四位随机数
            wxOrder.setCreateTime(new Date());
            wxOrder.setTradeState(PayStatusConst.APPLY);
            payOrderService.save(wxOrder);
    
            //向微信服务器提交申请
            return this.createWxOrderToWxService(openId, wxOrder.getOutTradeNo(), Integer.valueOf(amount), wxOrder.getDescription(), type);
        }
    
       /**
         * @param openId      微信用户openId
         * @param outTradeNo  我们的系统订单编号
         * @param amount      付款金额
         * @param description 商品描述
         * @param type        付款类型 详见PayTypeEnumConst
         * @Description: 向微信服务器发起预付款订单
         * @Return: java.util.Map<java.lang.String, java.lang.Object>
         */
        public Map<String, Object> createWxOrderToWxService(String openId, String outTradeNo, Integer amount, String description, String type) throws Exception {
            final WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                    //调起支付的人的 openId
                    .openid(openId)
                    //订单编号
                    .outTradeNo(outTradeNo)
                    //订单金额
                    .totalFee(amount)
                    //商品描述
                    .body(description)
                    //获取本地IP
                    .spbillCreateIp(InetAddress.getLoopbackAddress().getHostAddress())
                    .attach(type)
                    //回调的 URL 地址  已经在WxConfig文件中进行设置
                    .build();
            WxPayMpOrderResult result = this.wxPayService.createOrder(wxPayUnifiedOrderRequest);
            Map<String, Object> returnMap = new HashMap<>();
            returnMap.put("appId", result.getAppId());
            returnMap.put("nonceStr", result.getNonceStr());
            returnMap.put("packageValue", result.getPackageValue());
            returnMap.put("paySign", result.getPaySign());
            returnMap.put("signType", result.getSignType());
            returnMap.put("timeStamp", result.getTimeStamp());
            returnMap.put("orderId", outTradeNo);
            return returnMap;
        }
    
    • 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

    调用方法时的打印数据参考:
    在这里插入图片描述

    3、微信支付异步回调方法

    我这样写是因为在yml文件中设置了notifyUrl,然后在WxConfig文件中的wxService方法中进行支付配置了,如果不用配置也可以直接在使用wxPayService.createOrder()方法中对WxPayUnifiedOrderRequest参数设置.notifyUrl(回调地址),多种方式,实现即可。

    异步回调方法为:
    controller层:

     @ApiOperation(value = "支付回调通知处理")
        @RequestMapping("/notify")
        public String parseOrderNotifyResult(@RequestBody String xmlData) throws WxPayException {
            try {
                this.wxOrderService.parseOrderNotifyResult(xmlData);
                return WxPayNotifyResponse.success("成功");
            } catch (Exception ex) {
                ex.printStackTrace();
                return WxPayNotifyResponse.failResp("失败");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    serviceImpl层:

      @Override
        public void parseOrderNotifyResult(String xmlData) throws Exception {
            final WxPayOrderNotifyResult notifyResult = this.wxPayService.parseOrderNotifyResult(xmlData);
            if (null != notifyResult && StringUtils.isNotBlank(notifyResult.getTransactionId())) {
                //更新用户订单状态
                PayOrder wxOrder = payOrderService.getOne(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getOutTradeNo, notifyResult.getOutTradeNo()));
                wxOrder.setBankType(notifyResult.getBankType());
                wxOrder.setTradeState(PayStatusConst.SUCCESS);
                wxOrder.setSuccessTime(new Date());
                wxOrder.setTransactionId(notifyResult.getTransactionId());
                payOrderService.updateById(wxOrder);
                System.out.println("==============>atach" + notifyResult.getAttach());
                if (StringUtils.isNotEmpty(notifyResult.getAttach())) {
    
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注意:
    在这里插入图片描述

    除了这个方法,还有其他方式可用,实现即可,若是记录中有错误或遗漏的地方,欢迎评论指出。

  • 相关阅读:
    线程优先级
    Maven依赖详解
    基于javaweb+mysql的宿舍管理系统
    linux下profile、bashrc区别和PATH、LIBRARY_PATH、LD_LIBRARY_PATH区别
    最全解决docker配置kibana报错 Kibana server is not ready yet
    编程要搞明白的东西(二)
    nacos配置中心及服务注册中心使用
    Springboot智能物流拼单组合系统设计与实现
    如何拒绝期末复习无用功?猿辅导:找准适合自己的复习方法很重要
    【python3】4.文件管理
  • 原文地址:https://blog.csdn.net/weixin_44934104/article/details/126465854