手把手教你如何基于Springboot对接微信支付接口_Native支付(奶爸级别)

Posted 胡金水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手教你如何基于Springboot对接微信支付接口_Native支付(奶爸级别)相关的知识,希望对你有一定的参考价值。

微信开发文档地址:https://pay.weixin.qq.com/wiki/doc/api/index.html

申请商户号地址:https://pay.weixin.qq.com/static/help_guide/bind_guide_admin.shtml

demo下载地址:https://download.csdn.net/download/qq_17555933/18507085

微信对接时序图:

微信对接时序图

准备资料

# 微信支付二维码key
wxpay.qrcodeKey=wxpay_qrcode
# 微信支付二维码过期时间为 < 2小时(微信二维码code_url有效期为2小时)
wxpay.qrcodeExpire=7000

# 注意!!!如果异步通知接口没有返回success,微信异步通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒

# 公众账号ID
wxpay.appId=xxx
# 商户号
wxpay.merchantId=xxx
# 商户秘钥
wxpay.secrectKey=xxxx

# APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
wxpay.spbillCreateIp=127.0.0.1
# 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数
wxpay.notifyUrl=http://5ktbak.natappfree.cc/payment/notice/wxpay

# 支付方式,取值如下:JSAPI,NATIVE,APP
wxpay.tradeType=NATIVE

# 微信支付 - 统一下单地址
wxpay.placeOrderUrl=https://api.mch.weixin.qq.com/pay/unifiedorder

代码实现

WXPayController.java
package com.wxpay.controller;

import com.wxpay.pojo.Orders;
import com.wxpay.pojo.PayResult;
import com.wxpay.pojo.PaymentInfoVO;
import com.wxpay.pojo.PreOrderResult;
import com.wxpay.resource.WXPayResource;
import com.wxpay.service.WxOrderService;
import com.wxpay.utils.DateUtil;
import com.wxpay.utils.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: HuGoldWater
 * @description:
 */
@Slf4j
@RestController
@RequestMapping("payment")
public class WXPayController {
    @Autowired
    private WXPayResource wxPayResource;
    @Autowired
    private WxOrderService wxOrderService;
    /**
     * 支付成功后的微信支付异步通知
     */
    @RequestMapping(value = "/notify/wxpay")
    public void wxpay(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("支付成功后的微信支付异步通知");
        // 获取微信支付结果
        PayResult payResult = wxOrderService.getWxPayResult(request.getInputStream());

        boolean isPaid = payResult.getReturn_code().equals("SUCCESS") ? true : false;
        // 查询该笔订单在微信那边是否成功支付
        // 支付成功,商户处理后同步返回给微信参数
        PrintWriter writer = response.getWriter();
        if (isPaid) {
            String merchantOrderId = payResult.getOut_trade_no();            // 商户订单号
            String wxFlowId = payResult.getTransaction_id();
            Integer paidAmount = payResult.getTotal_fee();

            System.out.println("================================= 支付成功 =================================");

            log.info("************* 支付成功(微信支付异步通知) - 时间: {} *************", DateUtil.getCurrentDateString(DateUtil.DATETIME_PATTERN));
            log.info("* 商户订单号: {}", merchantOrderId);
            log.info("* 微信订单号: {}", wxFlowId);
            log.info("* 实际支付金额: {}", paidAmount);
            log.info("*****************************************************************************");
            // 通知订单服务,该订单已支付
            System.out.println("================================= 通知订单服务,该订单已支付 =================================");

            // 通知微信已经收到消息,不要再给我发消息了,否则微信会10连击调用本接口
            String noticeStr = setXML("SUCCESS", "");
            writer.write(noticeStr);
            writer.flush();
        } else {
            System.out.println("================================= 支付失败 =================================");

            // 支付失败
            String noticeStr = setXML("FAIL", "");
            writer.write(noticeStr);
            writer.flush();
        }
    }

    public static 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>";
    }

    /**
     * 微信扫码支付页面
     */
    @PostMapping(value = "/getWXPayQRCode")
    public JsonResult getWXPayQRCode(String merchantUserId, String merchantOrderId) throws Exception {
        // 根据订单ID和用户ID查询订单详情
//        Orders waitPayOrder = paymentOrderService.queryOrderByStatus(merchantOrderId,merchantUserId,PaymentStatus.WAIT_PAY.type);
        Orders waitPayOrder = new Orders();
        // 商品描述
        String body = "胡金水-付款用户[" + merchantUserId + "]";
        // 商户订单号
        String out_trade_no = merchantOrderId;
        // 使用map代替redis
        Map<String, String> redisCacheMap = new HashMap<>();
        // 从redis中去获得这笔订单的微信支付二维码,如果订单状态没有支付没有就放入,这样的做法防止用户频繁刷新而调用微信接口
        if (waitPayOrder != null) {
            String qrCodeUrl = redisCacheMap.get(wxPayResource.getQrcodeKey() + ":" + merchantOrderId);

            if (StringUtils.isEmpty(qrCodeUrl)) {
                // 订单总金额,单位为分
                String total_fee = "1"; // 测试金额
                // 统一下单
                PreOrderResult preOrderResult = wxOrderService.placeOrder(body, out_trade_no, total_fee);
                qrCodeUrl = preOrderResult.getCode_url();
            }

            PaymentInfoVO paymentInfoVO = new PaymentInfoVO();
            paymentInfoVO.setAmount(1);
            paymentInfoVO.setMerchantOrderId(merchantOrderId);
            paymentInfoVO.setMerchantUserId(merchantUserId);
            paymentInfoVO.setQrCodeUrl(qrCodeUrl);

            redisCacheMap.put(wxPayResource.getQrcodeKey() + ":" + merchantOrderId, qrCodeUrl);

            return JsonResult.ok(paymentInfoVO);
        } else {
            return JsonResult.errorMsg("该订单不存在,或已经支付");
        }
    }
}
WXPayResource.java
package com.wxpay.resource;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "wxpay")
@PropertySource("classpath:wxpay.properties")
public class WXPayResource {

    private String qrcodeKey;
    private long qrcodeExpire;

    private String appId;
    private String merchantId;
    private String secrectKey;

    private String spbillCreateIp;
    private String notifyUrl;

    private String tradeType;
    private String placeOrderUrl;
}
WxOrderServiceImpl.java
package com.wxpay.service.impl;

import com.wxpay.pojo.PayResult;
import com.wxpay.pojo.PreOrder;
import com.wxpay.pojo.PreOrderResult;
import com.wxpay.resource.WXPayResource;
import com.wxpay.service.WxOrderService;
import com.wxpay.utils.HttpUtil;
import com.wxpay.utils.Sign;
import com.wxpay.utils.XmlUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;

@Service
public class WxOrderServiceImpl implements WxOrderService {

    @Autowired
    private WXPayResource wxPayResource;

    /**
     * ==========================================
     * 微信预付单:指的是在自己的平台需要和微信进行支付交易生成的一个微信订单,称之为“预付单”
     * 订单:指的是自己的网站平台与用户之间交易生成的订单
     * <p>
     * 1. 用户购买产品 --> 生成网站订单
     * 2. 用户支付 --> 网站在微信平台生成预付单
     * 3. 最终实际根据预付单的信息进行支付
     * ==========================================
     */

    @Override
    public PreOrderResult placeOrder(String body, String out_trade_no, String total_fee) throws Exception {
        // 生成预付订单对象
        PreOrder o = new PreOrder();
        // 生成随机字符串
        String nonce_str = UUID.randomUUID().toString().trim().replaceAll("-", "");
        o.setAppid(wxPayResource.getAppId());
        o.setBody(body);
        o.setMch_id(wxPayResource.getMerchantId());
        o.setNotify_url(wxPayResource.getNotifyUrl());
        o.setOut_trade_no(out_trade_no);
        if (total_fee != null && !total_fee.equals("")) {
            o.setTotal_fee(Integer.parseInt(total_fee));
        } else {
            o.setTotal_fee(1);
        }
        o.setNonce_str(nonce_str);
        o.setTrade_type(wxPayResource.getTradeType());
        o.setSpbill_create_ip(wxPayResource.getSpbillCreateIp());
        SortedMap<Object, Object> p = new TreeMap<Object, Object>();
        p.put("appid", wxPayResource.getAppId());
        p.put("mch_id", wxPayResource.getMerchantId());
        p.put("body", body);
        p.put("nonce_str", nonce_str);
        p.put("out_trade_no", out_trade_no);
        p.put("total_fee", total_fee);
        p.put("spbill_create_ip", wxPayResource.getSpbillCreateIp());
        p.put("notify_url", wxPayResource.getNotifyUrl());
        p.put("trade_type", wxPayResource.getTradeType());
        // 获得签名
        String sign = Sign.createSign("utf-8", p, wxPayResource.getSecrectKey());
        o.setSign(sign);
        // Object转换为XML
        String xml = XmlUtil.object2Xml(o, PreOrder.class);
        // 统一下单地址
        String url = wxPayResource.getPlaceOrderUrl();
        // 调用微信统一下单地址
        String returnXml = HttpUtil.sendPost(url, xml);

        // XML转换为Object
        PreOrderResult preOrderResult = (PreOrderResult) XmlUtil.xml2Object(returnXml, PreOrderResult.class);

        return preOrderResult;
    }

    @Override
    public PayResult getWxPayResult(InputStream inStream) throws Exception {
        BufferedReader in = null;
        String result = "";
        in = new BufferedReader(new InputStreamReader(inStream));
        String line;
        while ((line = in.readLine()) != null) {
            result += line;
        }
        PayResult pr = (PayResult) XmlUtil.xml2Object(result, PayResult.class);
        return pr;
    }
}
MerchantOrdersBO.java
package com.wxpay.pojo;

import lombok.Data;

@Data
public class MerchantOrdersBO {
    private String merchantOrderId;         // 商户订单号
    private String merchantUserId;          // 商户方的发起用户的用户主键id
    private Integer amount;                 // 实际支付总金额(包含商户所支付的订单费邮费总额)
    private Integer payMethod;              // 支付方式 1:微信   2:支付宝
    private String returnUrl;               // 支付成功后的回调地址(学生自定义)

}
Orders.java
package com.wxpay.pojo;


import lombok.Data;

import java.util.Date;

@Data
public class Orders {
    /**
     * 订单主键
     */
    private String id;

    /**
     * 商户订单号
     */
    private String merchantOrderId;

    /**
     * 商户方的发起用户的用户主键id
     */
    private String merchantUserId;

    /**
     * 实际支付总金额(包含商户所支付的订单费邮费总额)
     */
    private Integer amount;

    /**
     * 支付方式
     */
    private Integer payMethod;

    /**
     * 支付状态 10:未支付 20:已支付 30:支付失败 40:已退款
     */
    private Integer payStatus;

    /**
     * 从哪一端来的,比如从XXX过来的
     */
    private String comeFrom;

    /**
     * 支付成功后的通知地址,这个是开发者那一段的,不是第三方支付通知的地址
     */
    private String returnUrl;

    /**
     * 逻辑删除状态;1: 删除 0:未删除
     */
    private Integer isDelete;

    /**
     * 创建时间(成交时间)
     */
    private Date createdTime;


}
PaymentInfoVO.java
package com.wxpay.pojo;

import lombok.Data;

@Data
public class PaymentInfoVO {

    private String merchantOrderId;         // 商户订单号
    private String merchantUserId;          // 商户方的发起用户的用户主键id
    private Integer amount;                 // 实际支付总金额(包含商户所支付的订单费邮费总额)
    private String qrCodeUrl;               // 二维码扫码地址

}
PayResult.java
package com.wxpay.pojo;

import lombok.Data;

/**
 * 支付结果封装类
 */
@Data
public class PayResult {
    private String return_code;                // 返回状态码
    private String appid;                    // 公众账号ID
    private String mch_id;                    // 商户号
    private String nonce_str;                // 随机字符串
    private String sign;                    // 签名
    private String result_code;                // 业务结果
    private String openid;                    // 用户标识
    private String trade_type;                // 交易类型
    private String bank_type;                // 付款银行
    private int total_fee;                    // 总金额
    private int cash_fee;                    // 现金支付金额
    private String transaction_id;            // 微信支付订单号
    private String out_trade_no;            // 商户订单号
    private String time_end;                // 支付完成时间
    private String return_msg;                // 返回信息
    private String device_info;                // 设备号
    private String err_code;                // 错误代码
    private String err_code_des;            // 错误代码描述
    private String is_subscribe;            // 是否关注公众账号
    private String fee_type;                // 货币种类
    private String cash_fee_type;            // 现金支付货币类型
    private String coupon_fee;                // 代金券或立减优惠金额
    private String coupon_count;            // 代金券或立减优惠使用数量
    private String coupon_id_$n;            // 代金券或立减优惠ID
    private String coupon_fee_$n;            // 单个代金券或立减优惠支付金额
    private String attach;                    // 商家数据包

}
PreOrder.java
package com.wxpay.pojo;

import lombok.Data;

/**
 * 统一下单
 */
@Data
public class PreOrder {
    private String appid;// 公众账号ID
    private String mch_id;// 商户号
    private String nonce_str;// 随机字符串
    private String sign;// 签名
    private String body;// 商品描述
    private String out_trade_no;// 商户订单号
    private int total_fee;// 订单总金额,单位为分
    private String spbill_create_ip;// APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
    private String notify_url;// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
    private String trade_type;// 取值如下:JSAPI,NATIVE,APP

}
PreOrderResult.java
package com.wxpay.pojo;

import lombok.Data;

/**
 * 微信支付 - 统一下单返回结果的封装entity
 */
@Data
public class PreOrderResult {

    private String return_code以上是关于手把手教你如何基于Springboot对接微信支付接口_Native支付(奶爸级别)的主要内容,如果未能解决你的问题,请参考以下文章

手把手教你接入微信支付

手把手教你快应用接入微信H5网页支付

手把手教你如何配置DBeaver对接FusionInsigth MRS Spark2x

手把手教你基于Springboot+Vue搭建个人博客网站

手把手教你基于SpringBoot+MyBatis实现员工管理系统‍附完整源码

手把手教你基于SpringBoot+MyBatis实现员工管理系统‍附完整源码