SpringBoot微信小程序支付

本地模拟微信支付回调

  • 因微信支付回调是由拉起支付时的notify_url属性设置,notify_url 是由微信服务器请求自己项目暴露出的指定的接口地址(公网IP);在生产环境中项目接口地址微信服务器请求必须可达;
  • 则在本地服务支付回调接口需要测试时,可以通过从线上服务器中获取微信回调XML数据,在本地使用Postman模拟支付回调进行接口测试。

示例代码:

1.微信支付SDK

<!--微信支付SDK-->
<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>0.0.3</version>
</dependency>

2.微信支付小程序配置

public class OurWxPayConfig implements WXPayConfig {
    private final static String APP_KEY = "APPId";
    private final static String MCH_ID = "商户ID";
    private final static String KEY = "小程序Key";
    private final static String SECRET = "小程序密钥";

    /**
     * 加载证书  这里证书需要到微信商户平台进行下载
     */
    private byte[] certData;

    public OurWxPayConfig() throws Exception {
        InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("apiclient_cert.p12");
        this.certData = IOUtils.toByteArray(certStream);
        certStream.close();
    }

    /**
     * 设置我们自己的appid
     * 商户号
     * 秘钥
     */

    @Override
    public String getAppID() {
        return APP_KEY;
    }

    @Override
    public String getMchID() {
        return MCH_ID;
    }


    @Override
    public String getKey() {
        return KEY;
    }

    @Override
    public InputStream getCertStream() {
        return new ByteArrayInputStream(this.certData);
    }

    @Override
    public int getHttpConnectTimeoutMs() {
        return 0;
    }

    @Override
    public int getHttpReadTimeoutMs() {
        return 0;
    }

    public String getSecret() {
        return SECRET;
    }
}

2.1.支付POJO

@ToString
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class PayOrderInfo implements Serializable {

    private static final long serialVersionUID = -3133004352432358931L;

    /**
     * 商户订单Id = 微信回调 out_trade_no
     */
    @TableId(type = IdType.INPUT)
    private String id;

    /**
     * 金额
     */
    private Float money;

    /**
     * 订单创建时间
     */
    private Date createTime;

    /**
     * 订单结束时间
     */
    private Date endTime;

    /**
     * 实际付款金额
     */
    private Float payMoney;

    /**
     * 支付商户订单号
     */
    private String payOrder;

    /**
     * 订单付款时间
     */
    private Date orderPayTime;

}

2.2.支付后台关键业务方法

	/**
	* 执行支付关键方法拉起
	*/
	@Override
    public JSONObject execPay(JSONObject requestParam, HttpServletRequest request) throws Exception {
        JSONObject attach = new JSONObject();
        String spbill_create_ip = request.getRemoteAddr();
        String out_trade_no = requestParam.getString("order_id");
        String openid = requestParam.getString("openid");

        PayOrderInfo orderById = payOrderInfoService.getById(out_trade_no);
        Float total_fee = orderById.getPayMoney();
        String body = "订单实付金额:" + orderById.getPayMoney();
        OurWxPayConfig ourWxPayConfig = new OurWxPayConfig();
        WXPay wxPay = new WXPay(ourWxPayConfig);
        //根据微信支付api来设置
        Map<String, String> data = new HashMap<>();
        data.put("appid", ourWxPayConfig.getAppID());
        data.put("mch_id", ourWxPayConfig.getMchID());
        data.put("spbill_create_ip", spbill_create_ip);//终端ip
        data.put("trade_type", "JSAPI");//支付场景 APP 微信app支付 JSAPI 公众号支付  NATIVE 扫码支付
        data.put("notify_url", notifyUrl);//回调地址
        //total_fee提交内容:金额*100
        data.put("total_fee", String.valueOf(Math.round(total_fee * 100)));//订单总金额
        data.put("fee_type", "CNY");//默认人民币
        data.put("out_trade_no", out_trade_no);//商户id
        data.put("body", body);//订单信息描述
        data.put("openid", openid);   // openId
        data.put("sign_type", "MD5");   // 签名类型

        //生成首次签名
        String sign = WXPayUtil.generateSignature(data, ourWxPayConfig.getKey());
        data.put("sign", sign); // sign

        //调用微信统一下单接口
        Map<String, String> wxRespData = wxPay.unifiedOrder(data);
        if (wxRespData.get("return_code").equals("SUCCESS")) {
            try {
                //根据统一下单数据包装获取二次签名
                String timeStamp =
                			String.valueOf(System.currentTimeMillis() / 1000);//注意该单位一定得是秒
                String nonceStr = WXPayUtil.generateNonceStr();
                Map<String, String> sign2Map = new HashMap<>();
                sign2Map.put("appId", ourWxPayConfig.getAppID());
                sign2Map.put("nonceStr", nonceStr);
                sign2Map.put("package", "prepay_id=" + wxRespData.get("prepay_id"));
                sign2Map.put("signType", "MD5");
                sign2Map.put("timeStamp", timeStamp);
                //获取二次签名并封装前端拉起的时需要的数据
                String paySign = WXPayUtil.generateSignature(sign2Map, ourWxPayConfig.getKey());
                attach.put("paySign", paySign);
                attach.put("prepay_id", wxRespData.get("prepay_id"));
                attach.put("appId", ourWxPayConfig.getAppID());
                attach.put("key", ourWxPayConfig.getKey());
                attach.put("signType", "MD5");
                attach.put("nonceStr", nonceStr);
                attach.put("timeStamp", timeStamp);
                return attach;//返回前端拉去数据
            } catch (RuntimeException e) {
                log.error(" --- 订单拉起支付失败");
            }
        }
        throw new JeecgBootException("微信支付订单出现错误");
    }

2.3.前端获取签名进行支付窗口拉起

pay(res){
    const packages='wxpay'
    //前端拉起支付当前时间(单位必须得是秒)
    let timeStamp = String(Date.now()/1000).substr(0,10)
    let nonceStr = res.data.result.nonceStr
    let packageStr = 'prepay_id='+res.data.result.prepay_id
    let signTypeStr = 'MD5'
    let key=res.data.result.key
    let sign = res.data.result.paySign
    wx.requestPayment({
        timeStamp: res.data.result.timeStamp,
        nonceStr: nonceStr ,
        package: packageStr,
        signType: signTypeStr,
        paySign: sign,
        success:  (res) =>{  
      uni.redirectTo({
       url:'***'
      })
        },
        fail:  (errs)=> {
         this.$u.toast('支付失败')
        }
    });
   },

3.微信支付回调接口

 @ApiOperation("blogDemo")
    @RequestMapping(value = "/blogDemo")
    public void blogDemo(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Lock lock = this.lock;
        lock.lock();
        try {
            OurWxPayConfig ourWxPayConfig = new OurWxPayConfig();
            WXPay wxpay = new WXPay(ourWxPayConfig);
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
            response.setHeader("Access-Control-Allow-Origin", "*");

            DataInputStream in;
            String wxNotifyXml = "";
            try {
                in = new DataInputStream(request.getInputStream());
                byte[] dataOrigin = new byte[request.getContentLength()];
                in.readFully(dataOrigin); // 根据长度,将消息实体的内容读入字节数组dataOrigin中

                in.close(); // 关闭数据流
                wxNotifyXml = new String(dataOrigin); // 从字节数组中得到表示实体的字符串
                //可通过这行代码获取微信回调xml数据
               	log.error(" --- wxNotifyXml->[{}]", wxNotifyXml);
            } catch (IOException e) {
                e.printStackTrace();
            }

            Map<String, String> wxResponse = wxpay.processResponseXml(wxNotifyXml);
            // 解析各种数据
            if (wxResponse.get("result_code").equalsIgnoreCase("SUCCESS") && wxResponse.get("return_code").equalsIgnoreCase("SUCCESS")) {
                String out_trade_no = wxResponse.get("out_trade_no");//商户保存订单号(自身业务保存订单好)
                String transaction_id = wxResponse.get("transaction_id");//微信支付订单号(微信生成的订单号)
            }
        } finally {
            lock.unlock();
        }
    }

3.1.从公网服务器中获取接口微信回调的xml数据

在这里插入图片描述

3.2Postman接口请求配置

Postman接口请求配置
在这里插入图片描述
本地请求成功!注:服务器行获取的xml数据内容不可改变,如果订单内容有变动,得从服务器上获取新的xml数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值