微信支付JSAPI下单和微信小程序调起支付(V2版本)

一、前文

参考 基于WxJava实现微信支付SpringBoot后端接口的使用 微信支付—开发者文档—小程序—小程序调起支付API(V3版本) 微信支付—开发文档—小程序支付—小程序调起支付API(V2) 微信支付接口签名校验工具 微信官方文档—小程序—支付 /wx.requestPayment(V3)

  • V2版本的API,通过MD5加密(本博文主要简述)
  • V3版本的API,使用RAS加密

二、流程图

sequenceDiagram
微信小程序 ->> 微信小程序: 用户点击付款按钮
微信小程序 ->> SpringBoot后端: 调用后端下单接口
SpringBoot后端->>SpringBoot后端: JSAPI统一下单
SpringBoot后端->>SpringBoot后端: 微信订单数据存入数据库
SpringBoot后端->>SpringBoot后端: 生成小程序调起支付所需的数据
SpringBoot后端->>微信小程序: 返回小程序调起支付所需的数据
微信小程序->>微信小程序: wx.requestPayment发起微信支付
微信小程序->>SpringBoot后端: 支付成功/失败回调
SpringBoot后端->>SpringBoot后端: JSAPI查询订单
SpringBoot后端->>SpringBoot后端: 确认支付成功,更新数据库

三、SpringBoot接口实现

2.1 微信调起支付所需数据

Md5Utils

/**
 * Md5加密方法
 *
 * @author weijian
 */
public class Md5Utils
{
    private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);

    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) {
        try {
            java.security.MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(data.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString().toUpperCase();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
复制代码

WxPayment


/**
 * 微信调起支付数据 WxPayment
 *
 * @author weijian
 */
@ApiModel(value="WxPayment",description="微信调起支付数据")
public class WxPayment
{
    @ApiModelProperty(value="wxTradeId", required = true)
    @Excel(name = "wxTradeId")
    private Long wxTradeId;

    @ApiModelProperty(value="小程序id", required = true)
    @Excel(name = "小程序id")
    private String appId;

    @ApiModelProperty(value="时间戳", required = true)
    @Excel(name = "时间戳")
    private String timeStamp;

    @ApiModelProperty(value="随机字符串", required = true)
    @Excel(name = "随机字符串")
    private String nonceStr;

    @ApiModelProperty(value="订单详情扩展字符串", required = true)
    @Excel(name = "订单详情扩展字符串")
    private String _package;

    @ApiModelProperty(value="签名方式", required = true)
    @Excel(name = "签名方式")
    private String signType;

    @ApiModelProperty(value="签名", required = true)
    @Excel(name = "签名")
    private String paySign;

    public void sign(String key){
        String sign = ("appId="+appId+"&");
        sign += ("nonceStr="+nonceStr+"&");
        sign += ("package="+_package+"&");
        sign += ("signType="+signType+"&");
        sign += ("timeStamp="+timeStamp+"&");
        sign += ("key="+key);
        System.out.println("sign="+sign);
        setPaySign(Md5Utils.MD5(sign).toUpperCase());
    }

    public Long getWxTradeId() {
        return wxTradeId;
    }

    public void setWxTradeId(Long wxTradeId) {
        this.wxTradeId = wxTradeId;
    }

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(String timeStamp) {
        this.timeStamp = timeStamp;
    }

    public String getNonceStr() {
        return nonceStr;
    }

    public void setNonceStr(String nonceStr) {
        this.nonceStr = nonceStr;
    }

    public String get_package() {
        return _package;
    }

    public void set_package(String _package) {
        this._package = _package;
    }

    public String getSignType() {
        return signType;
    }

    public void setSignType(String signType) {
        this.signType = signType;
    }

    public String getPaySign() {
        return paySign;
    }

    public void setPaySign(String paySign) {
        this.paySign = paySign;
    }

    @Override
    public String toString() {
        return "WxPayment{" +
                "wxTradeId=" + wxTradeId +
                ", appId='" + appId + '\'' +
                ", timeStamp='" + timeStamp + '\'' +
                ", nonceStr='" + nonceStr + '\'' +
                ", _package='" + _package + '\'' +
                ", signType='" + signType + '\'' +
                ", paySign='" + paySign + '\'' +
                '}';
    }
}

复制代码

2.2 下单接口

WxPayController


    @ApiOperation("微信统一下单")
//    @PreAuthorize("@ss.hasPermi('wallet:update')")
    @Log(title = "微信统一下单", businessType = BusinessType.UPDATE)
    @PostMapping("/wx/unifiedOrder")
    public AjaxResult wxUnifiedOrder(@ApiParam() Long billId) throws Exception
    {
        Bill bill = billService.selectBillById(billId);
        if(ObjectUtil.isNull(bill)){
            return AjaxResult.error("bill异常");
        }

		//1. JSAPI微信统一下单
        WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
        wxPayUnifiedOrderRequest.setOutTradeNo(IdUtils.fastSimpleUUID(16));
        wxPayUnifiedOrderRequest.setTotalFee(1);
        wxPayUnifiedOrderRequest.setSpbillCreateIp(IpUtils.getHostIp());
        wxPayUnifiedOrderRequest.setNotifyUrl("https://www.nb.cn:8080/v1/wx/notify");
        wxPayUnifiedOrderRequest.setTradeType("JSAPI");
        wxPayUnifiedOrderRequest.setBody("账单支付");
        wxPayUnifiedOrderRequest.setOpenid(SecurityUtils.getLoginUser().getUser().getTenantOpenId());
        wxPayUnifiedOrderRequest.setAppid("wx********");
        WxPayUnifiedOrderResult wxPayUnifiedOrderResult = wxService.unifiedOrder(wxPayUnifiedOrderRequest);

		//2. 微信订单数据存入数据库
        WxTrade wxTrade = new WxTrade();
        wxTrade.setBillId(billId);
        wxTrade.setUserId(SecurityUtils.getUserId());
        wxTrade.setUserName(SecurityUtils.getUsername());
        wxTrade.setAppId(wxPayUnifiedOrderResult.getAppid());
        wxTrade.setMchId(wxPayUnifiedOrderResult.getMchId());
        wxTrade.setSubMchId(wxPayUnifiedOrderResult.getSubMchId());
        wxTrade.setOutTradeNo(wxPayUnifiedOrderRequest.getOutTradeNo());
        wxTrade.setTotalFee(wxPayUnifiedOrderRequest.getTotalFee());
        wxTrade.setBody(wxPayUnifiedOrderRequest.getBody());
        wxTrade.setStatus(Constants.TRADE_UNKNOWN);
        wxTradeService.insertWxTrade(wxTrade);

		//3. 生成小程序调起支付所需的数据
        WxPayment wxPayment = new WxPayment();
        wxPayment.setWxTradeId(wxTrade.getId());
        wxPayment.setAppId(wxPayUnifiedOrderResult.getAppid());
        wxPayment.setTimeStamp((System.currentTimeMillis()/1000)+"");
        wxPayment.set_package("prepay_id="+wxPayUnifiedOrderResult.getPrepayId());
        wxPayment.setNonceStr(IdUtils.fastSimpleUUID(32));
        wxPayment.setSignType("MD5");
        wxPayment.sign(wxPayProperties.getMchKey());
        logger.info(wxPayment.toString());

		//4. 返回wxPayment
        return AjaxResult.success(wxPayment);
    }
复制代码

2.3 支付回调接口

支付回调接口

  1. 查询该微信订单是否完成
  2. 确认支付完成,则执行具体的业务逻辑
    /**
     * 订单状态, -1:未知;0:支付失败;1:支付成功
     */
    public static final int TRADE_UNKNOWN     = -1;
    public static final int TRADE_FAIL        = 0;
    public static final int TRADE_SUCCESS     = 1;
复制代码
    public int queryWxOrder(String outTradeNo){
        try {
            WxPayOrderQueryResult wxPayOrderQueryResult = wxService.queryOrder(null, outTradeNo);
            logger.info(wxPayOrderQueryResult.toString());
            if(wxPayOrderQueryResult.getTradeState().equals("NOTPAY")){
                return Constants.TRADE_UNKNOWN;
            }
            return Constants.TRADE_SUCCESS;
        }catch (Exception e){
//            e.printStackTrace();
            //异常,订单不存在
        }
        return Constants.TRADE_FAIL;
    }
复制代码

三、微信小程序实现

大部分的加密校验,以及与微信支付接口的交互都在Java后台操作,所以微信小程序的代码就相对简洁简单。

3.1 JSAP统一下单

  //统一下单
  unifiedOrder(){
    var _this = this
    app.showLoading(true)
    keyueliSdk.wxUnifiedOrder(
      {
        billId: _this.data.id,
      },
      (res) => {
        console.log("wxUnifiedOrder", res)
        _this.requestPayment(res.data.data)
      },
      (res) => {})
  },
复制代码

3.2 小程序调起支付

  //官方标准的支付方法,调起支付界面
  requestPayment(payData) {
    var _this = this
    wx.requestPayment({
      timeStamp: payData.timeStamp,
      nonceStr: payData.nonceStr,
      package: payData._package,
      signType: payData.signType,
      paySign: payData.paySign,
      success(res) {
        console.log("支付成功", res)
        _this.paySuccess(payData)
      },
      fail(res) {
        console.log("支付失败", res)
        app.showLoading(false)
        app.showToast("支付失败")
      }
    })
  },
复制代码

3.3 支付回调

  //支付成功记录
  paySuccess(payData){
    keyueliSdk.wxPay(
      payData.wxTradeId,
      (res) => {
        wx.hideLoading()
        console.log("wxPay", res)
        if(res.data.code==200){
          app.showToast("支付成功")
          wx.navigateBack({
            delta: 1,
          })
          return
        }
        app.showToast(res.data.msg)
      },
      (res) => {})
  },
复制代码

觉得好,就一键三连呗(点赞+收藏+关注)

分类:
后端