看了一些文章,觉得比较复杂,所以自己写了一个小程序端调用逻辑,java后端获取普通微信支付参数接口,微信支付成功回调接口。

因为我java项目是用springboot,所以要用maven引入一个微信支付的依赖,微信支付官方的请求类,方法都在这里面

    <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-pay</artifactId>
            <version>4.4.0</version></dependency>
        <dependency>

微信小程序端用的是官方提供的原生语言wxml页面组合

下面从简说明

先说小程序端的,他是如何调用接口,并拉起支付的

整体流程:调用获取支付参数接口获取参数→利用返回的支付参数使用 wx.requestPayment拉起微信支付,通过回调success或fail来判断是否支付成功。

如果涉及业务,一般不通过wx.requestPayment来更新业务表,一般在生成支付参数的时候,可以提供支付结果回调地址(公网可访问的HTTPS地址)用于生成支付参数,如果支付成功,会在自己服务端那边自动处理,这个下面将服务端接口代码的时候会提到

页面支付代码pay.js

orderId可以用自定义的订单号,在微信支付那也可以查到自己定义的订单号,exp和type是自己业务字段,可以忽略

  doWxPay: function () {
    var that = this
    var money = parseFloat(that.data.wxPayMoney)
    if (!money || money <= 0) { wx.showToast({ title: '请输入正确的充值金额', icon: 'none' }); return }

    wx.showLoading({ title: '正在下单...', mask: true })

    petUserApi.waitOpenid(app).then(function (openid) {
      if (!openid) { wx.hideLoading(); wx.showToast({ title: '获取openid失败,请重试', icon: 'none' }); return }

      var orderId  = 'WX' + Date.now() + '' + Math.ceil(Math.random() * 1000)
      var moneyFen = Math.round(money * 100)

      wx.request({
        url: app.globalData.host + '/api/pet/exp/wx/pay',
        method: 'POST',
        header: { 'content-type': 'application/json', Cookie: wx.getStorageSync('cookieKey') || '' },
        data: {
          orderId: orderId,
          userId:  String(userInfo.id || userInfo._id),
          openid:  openid,
          money:   String(moneyFen),
          exp:     String(moneyFen),
          type:    '余额充值'
        },
        success: function (res) {
          wx.hideLoading()
          var body = res.data || {}
          if (!petUserApi.isApiSuccess(body.code)) {
            wx.showToast({ title: petUserApi.getApiMsg(body, '下单失败'), icon: 'none' }); return
          }
          var p = body.obj
          wx.requestPayment({
            timeStamp: p.timeStamp,
            nonceStr:  p.nonceStr,
            package:   p.packageStr,
            signType:  p.signType,
            paySign:   p.paySign,
            success: function () {
              that.setData({ wxPayMoney: '' })
              //下面一行是业务方法
              that._queryUserBalance()
              wx.showToast({ title: '支付成功,已到账', icon: 'success', duration: 2500 })
            },
            fail: function (err) {
              if (err && err.errMsg && err.errMsg.indexOf('cancel') !== -1) {
                wx.showToast({ title: '已取消支付', icon: 'none' })
              } else {
                wx.showToast({ title: '支付失败', icon: 'error' })
              }
            }
          })
        },
        fail: function () { wx.hideLoading(); wx.showToast({ title: '网络异常,请稍后重试', icon: 'none' }) }
      })
    })
  },

java后端代码

java后端是利用的微信支付的统一下单接口

    //微信支付依赖里的方法
    @Autowired
    private WxPayService wxPayService;

// 小程序端调用统一下单的请求参数
    @Data
    @NoArgsConstructor
     public static class PetPayOrderReq
     {
    private String orderId;    // 订单号
    private String userId;     // 用户ID
    private String openid;     // 用户openid
    private String money;      // 金额,单位分(落库 pet_token_order.money)
    private String type;       // 订单类型(如"金额充值")

   }

    @PostMapping("/wx/pay")
    public String wxpaypost(@RequestBody PetPayOrderReq req) {
        IsInterface<WxPayRes> res = new IsInterface<>();
        try {
            // 1. 校验参数
            if (req.getOrderId().isEmpty() || req.getUserId().isEmpty() || req.getOpenid().isEmpty()
                    || req.getMoney().isEmpty() || req.getType().isEmpty()) {
                res.setCode("fail");
                res.setMsg("必填项为空!");
                return  String.valueOf(JSONObject.fromObject(res));
            }

            // 3. 调用微信支付统一下单API
            int totalFee = Integer.parseInt(req.getMoney()); // 金额(分,对应原云函数的totalFee)

            //这个类来源微信支付依赖
            WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
            request.setBody(req.getType());
            request.setOutTradeNo(req.getOrderId());
            request.setTotalFee(totalFee);
            request.setOpenid(req.getOpenid());
            request.setSpbillCreateIp("127.0.0.1");
            request.setTradeType("JSAPI");
            request.setNotifyUrl(wxPayService.getConfig().getNotifyUrl());


            WxPayUnifiedOrderResult orderResult = wxPayService.unifiedOrder(request);


// 4. 生成【小程序支付参数】(必须用统一下单返回的 prepay_id)
            WxPayRes payRes = new WxPayRes();

// 1. 生成基础参数
            String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); // 10位时间戳
            String nonceStr = getNonceStr(); // 随机字符串
            String packageStr = "prepay_id=" + orderResult.getPrepayId(); // ✅ 必须用这个!
            String signType = "MD5";
// ==============================================
// 4.4.0 专用:原生 MD5 签名(无任何依赖,不爆红)
// ==============================================
            String appId = wxPayService.getConfig().getAppId();
            String mchKey = wxPayService.getConfig().getMchKey();

// 1. 组装签名参数
            SortedMap<String, String> signParams = new TreeMap<>();
            signParams.put("appId", appId);
            signParams.put("nonceStr", nonceStr);
            signParams.put("package", packageStr);
            signParams.put("signType", "MD5");
            signParams.put("timeStamp", timeStamp);

// 2. 拼接字符串 a
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> entry : signParams.entrySet()) {
                String k = entry.getKey();
                String v = entry.getValue();
                if (v != null && !v.isEmpty()) {
                    sb.append(k).append("=").append(v).append("&");
                }
            }
// 3. 拼接商户密钥
            sb.append("key=").append(mchKey);
            String stringA = sb.toString();

// 4. MD5 加密 → 转大写
            String paySign = DigestUtils.md5Hex(stringA).toUpperCase();

// 3. 封装返回给小程序的参数
            payRes.setTimeStamp(timeStamp);
            payRes.setNonceStr(nonceStr);
            payRes.setPackageStr(packageStr);
            payRes.setSignType(signType);
            payRes.setPaySign(paySign);

            // 5. 返回结果给小程序
            res.setCode("success");
            res.setMsg("统一下单成功");
            res.setObj(payRes);
            return String.valueOf(JSONObject.fromObject(res));
        } catch (WxPayException e) {
            res.setCode("fail");
            res.setMsg("微信支付统一下单失败:" + e.getErrCodeDes());
            return String.valueOf(JSONObject.fromObject(res));
        } catch (Exception e) {
            res.setCode("fail");
            res.setMsg("系统异常:" + e.getMessage());
            return String.valueOf(JSONObject.fromObject(res));
        }

    }


    // 返回给小程序的支付参数
    @Data
    @NoArgsConstructor
    public static class WxPayRes {
    private String timeStamp;  // 时间戳(10位)
    private String nonceStr;   // 随机字符串
    private String packageStr; // prepay_id参数(格式:prepay_id=xxx)
    private String signType;   // 签名类型(MD5)
    private String paySign;    // 支付签名

    }

  // 生成随机字符串
    private String getNonceStr() {
        return UUID.randomUUID().toString().replace("-", "");
    }

wxPayService.java商户号配置类

@Configuration
public class WxPayConfig {
    // 小程序APPID
    private static final String APP_ID = "";
    // 商户号(和原云函数的subMchId一致:**)
    private static final String MCH_ID = "";
    // 商户密钥(微信支付商户平台设置的API密钥)
    private static final String MCH_KEY = "、";
    // 支付结果回调地址(公网可访问的HTTPS地址)
    private static final String NOTIFY_URL = "**";

    @Bean
    public  WxPayService wxPayService() {
        com.github.binarywang.wxpay.config.WxPayConfig payConfig = new com.github.binarywang.wxpay.config.WxPayConfig();
        payConfig.setAppId(APP_ID);
        payConfig.setMchId(MCH_ID);
        payConfig.setMchKey(MCH_KEY);
        payConfig.setNotifyUrl(NOTIFY_URL);
        // 沙箱环境(测试用,切正式时改为 false)
        payConfig.setUseSandboxEnv(false);

        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }
}

支付成功后走这个接口,微信端自动检测调用这个接口


    //支付成功才会走这个回调,且是在服务端,小程序端那边不用判断,只需要给弹窗就行
    @PostMapping("/wx/pay/notify")
    public String payNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 1. 读取微信回调的XML数据
            String xmlData = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);

            // 2. 解析回调结果
            WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);

            // 3. 校验成功,处理业务(加经验、改订单状态)
            String orderId = notifyResult.getOutTradeNo(); // 订单号
            String transactionId = notifyResult.getTransactionId(); // 微信订单号

            System.out.println("订单" + orderId + "支付成功!");

            //下面是业务代码
            PetTokenOrder tokenOrder = petTokenOrderRepository.findFirstByOrderId(orderId);
            if (tokenOrder == null) {
                return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[订单不存在]]></return_msg></xml>";
            }
            if (!"WX_RECHARGE".equals(tokenOrder.getType())) {
                return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[订单类型异常]]></return_msg></xml>";
            }

            if ("1".equals(tokenOrder.getStatus())) {
                return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
            }

            PetUsers petUsers = petUserRepository.findById(tokenOrder.getUserId()).orElse(null);
            String userOpenid = petUsers != null && petUsers.get_openid() != null ? petUsers.get_openid() : "";

            tokenOrder.setStatus("1");
            tokenOrder.setWxOrderNo(transactionId != null ? transactionId : "");
            if (tokenOrder.getOpenid() == null || tokenOrder.getOpenid().isEmpty()) {
                tokenOrder.setOpenid(userOpenid);
            }
            tokenOrder.setUpdatedate(new Timestamp(System.currentTimeMillis()));
            petTokenOrderRepository.save(tokenOrder);

            if (petUsers != null) {
                petUsers.setJyz(petUsers.getJyz() + tokenOrder.getTokenAmount());
                petUserRepository.save(petUsers);
            }


            // 4. 返回微信成功标识
            return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        } catch (Exception e) {
            return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";
        }
    }

后面再讲一下微信的虚拟支付,后面的小程序用到虚拟商品的都需要从普通微信支付转到微信支付了,有问题可以评论区一起讨论,码字不易,麻烦留下个小心心~,谢谢

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐