网站微信扫码支付开发文档含代码

微信扫码支付文档

前言:2021年2月左右完成,内容借鉴哔哩哔哩的教学视频(忘记up的昵称了)

1.官方文档

官网链接:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_0.shtml

官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html

SDK下载路径:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1

2.需要提前在官网获取的参数

公司通常已经申请好啦

1.mch_id 商户号

微信支付功能先要申请微信(企业)公众平台,然后开通企业公众平台付功能。下图为微信(企业)公众平台页面,可以看到商户号等信息

2.appId 公众号ID

从开发-基本配置中获取APPID

3.密钥

API密钥,key设置路径:微信商户平台(pay.weixin.qq.com)–>账户设置–>API安全–>密钥设置

3.流程和流程图

1.业务流程

1)商户后台调用统一下单API,向微信端传入参数(xml),微信端接收参数并返回状态(xml),若状态为SUCCESS,则返回二维码url:code_url ,若状态为FAIL,则返回错误信息

2)商户根据返回的code_url ,通过前端生成为二维码展示给客户

3)客户扫描二维码并支付,消息返回微信端,微信给客户反馈支付结果,并通过回调URL给商户返回订单信息和状态(xml

4)商户根据返回的状态,进行后续操作,并将接收信息返回微信端(如果微信端没有接收到商户返回,会多次向商户发送订单信息)(xml

2.流程图

img

4.项目的搭建和解构

1.新建一个Spring Boot 项目
2.下载java版本的SDK,解压后将工具类直接复制到项目里

在这里插入图片描述

3.项目目录

在这里插入图片描述

4.解构

在这里插入图片描述

5.统一下单

URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder

请求参数参照官网,必填必须传递

SDK里提供了将map转换成xml的方法,没必要自己写(太容易报错)

controller: orderSubmitController 相关代码
 //支付
    @RequestMapping("/getUrl")
    @ResponseBody
    public Map<String ,String>  getUrl(
                                       @RequestParam(value = "totalFee") String totalFee,
                                       @RequestParam(value = "orderNo") String orderNo1,
                                       @RequestParam(value = "remark") String remark){
        Map<String ,String> result = new HashMap<>();
        try {
            //获取订单号
            String orderNo = orderNo1;
            //获取订单描述
            String body = remark;
            //获取订单价格
            String price = totalFee;
			//自己封装的工具类
            DataJoinUtils wxPayUtils = new DataJoinUtils();

            //指定回调地址(应该是外网可访问地址,不能包含参数,此次用内网穿透获取的url)
            String url = "http://rh6sm4.natappfree.cc/orders/unifiedorderNotify";
            //统一下单,微信返回结果
            result = wxPayUtils.wxPay(url,orderNo,price,"127.0.0.1",body);
            
           //此处仅为了看返回状态和二维码url,无意义
            if (result.get("return_code").equals("SUCCESS")) {
                System.out.println("成功:"+result.get("return_code"));
            }else {
                System.out.println("失败:"+result.get("return_code"));
            }
            System.out.println("UQ:"+result.get("code_url"));

        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

工具类:DataJoinUtils 对应的代码
 /**
     * 微信统一下单接口
     * @param notify_url 回调地址
     * @param out_trade_no 商户订单号
     * @param total_fee 订单总金额
     * @param ip IP
     * @param body 商品内容描述
     * @return
     * @throws Exception
     */
    public Map<String, String> wxPay(String notify_url, String out_trade_no, String total_fee, String ip, String body) throws Exception {
    //将请求参数放入map
        Map<String ,String> map = new HashMap<String, String>();
        //APPTID
        map.put("appid",ConfigConstant.appId);
		//非必填项
        map.put("attach","支付测试");
        //商户号
        map.put("mch_id",ConfigConstant.mchId);
        //随机字符串
        map.put("nonce_str",WXPayUtil.generateNonceStr());
        //商品描述
        map.put("body",body);
        //商户订单号
        map.put("out_trade_no",out_trade_no);
        //标价币种
        map.put("fee_type", "CNY");
        //订单总金额,分
        map.put("total_fee",total_fee);
        //终端ip
        map.put("spbill_create_ip",ip);
        //回调地址
        map.put("notify_url",notify_url);
        //支付类型
        map.put("trade_type","NATIVE");
        //签名类型
        map.put("sign_type", WXPayConstants.MD5);
        //
        map.put("product_id",out_trade_no);

        System.out.println("map:"+map.toString());
        //生成带有 sign 的 XML 格式字符串
        StringBuilder builder = new StringBuilder();
        String xml = WXPayUtil.generateSignedXml(map,ConfigConstant.key);

        //指定与微信交互的url地址
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        //调用自己封装的工具类提交响应
        String str = UrlPreUtils.post(url,xml);

        System.out.println("str:"+str);

        Map<String,String> retmap = new HashMap<String, String>();
        try {
            //与微信交互并获取信息,将xml转换成map
           retmap = WXPayUtil.xmlToMap(str);
        }catch (Exception e){
            e.printStackTrace();
        }

        return retmap;
    }

工具类:UrlPreUtils 对应代码
 //编码格式
    private static final String ENCODING = "UTF-8";
    private final static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);

    /**
     *Post JSON
     * @param url 提交url
     * @param json 提交响应
     * @return
     */
    public static  String post(String url, String json){
        StringBuffer requestText = new StringBuffer();
        CloseableHttpResponse response = null;
        CloseableHttpClient client = null;
        HttpPost httpPost = new HttpPost(url);
        StringEntity entityParams = null;
        try {
            entityParams = new StringEntity(json,"utf-8");
            httpPost.setEntity(entityParams);
            httpPost.setHeader("Content-Type","application/x-www-form-urlencoded");

            client = HttpClients.createDefault();
            response = client.execute(httpPost);
            byte[] x = EntityUtils.toByteArray(response.getEntity());
            requestText.append(new String(x,"utf-8"));
        } catch (Exception e) {
            logger.info("Request-"+ requestText.toString());
        } finally {
            return requestText.toString();
        }

    }

6.查询订单

通常客户支付后,会调用回调url 返回状态,但商户也可以自己查询

orderSubmitController

    //订单支付状态查询
    @RequestMapping("/getOrderData")
    @ResponseBody
    public Map<String ,String> getOrderData(@RequestParam(value = "orderNo") String orderNo1){
        Map<String,String> resultSet = new HashMap<>();
        try {
            //获取订单号
            String orderNo = orderNo1;
            DataJoinUtils wxPayUtils = new DataJoinUtils();
            //微信订单查询结果
            resultSet = wxPayUtils.wxOrderQuery(orderNo);
        }catch (Exception e){
            e.printStackTrace();
        }
        return resultSet;
    }
工具类:DataJoinUtils 对应代码
/**
 * 微信查询
 * @param out_trade_no 订单编号
 * @return
 * @throws Exception
 */
public Map<String,String> wxOrderQuery(String out_trade_no) throws Exception {
    Map<String,String> map = new HashMap<String,String>();

    //APPTID
    map.put("appid",ConfigConstant.appId);
    //商户号
    map.put("mch_id",ConfigConstant.mchId);
    //随机字符串
    map.put("nonce_str",WXPayUtil.generateNonceStr());
    //商户订单号
    map.put("out_trade_no",out_trade_no);

	//生成带有 sign 的 XML 格式字符串
    String xml = WXPayUtil.generateSignedXml(map,ConfigConstant.key);

    //指定与微信交互的url地址
    String url = "https://api.mch.weixin.qq.com/pay/orderquery";
	//与微信交互并获取信息,发送请求并获取响应
    String str = UrlPreUtils.post(url,xml);
    Map<String,String> retmap = new HashMap<String, String>();
    try {
        //xml转换成map
        retmap = WXPayUtil.xmlToMap(str);
    }catch (Exception e){
        e.printStackTrace();
    }

    return retmap;

}

7.关闭订单

关闭订单后,二维码不再刷新

orderSubmitController
  //关闭订单(订单关闭二维码失效,不能再刷新)
    @RequestMapping("/closeOrder")
    @ResponseBody
    public String closeOrder(@RequestParam(value = "orderNo") String orderNo1){
        String status = "";
        try {
            //获取订单号
            String orderNo = orderNo1;
            DataJoinUtils wxPayUtils = new DataJoinUtils();
            //关闭订单结果
            Map<String,String> resultSet = wxPayUtils.wxCloseOrder(orderNo);
            //返回的状态
            status = resultSet.get("result_code");
            
        }catch (Exception e){
            e.printStackTrace();
        }
        return status;
    }
工具类:DataJoinUtils 对应代码
    /**
     * 关闭订单
     * @param out_trade_no
     * @return
     * @throws Exception
     */
    public Map<String,String> wxCloseOrder(String out_trade_no) throws Exception{
        Map<String,String> map = new HashMap<String,String>();

        //APPTID
        map.put("appid",ConfigConstant.appId);
        //商户号
        map.put("mch_id",ConfigConstant.mchId);
        //随机字符串
        map.put("nonce_str",WXPayUtil.generateNonceStr());
        //商户订单号
        map.put("out_trade_no",out_trade_no);
		//生成带有 sign 的 XML 格式字符串
        String xml = WXPayUtil.generateSignedXml(map,ConfigConstant.key);
        //指定与微信交互的url地址
        String url = "https://api.mch.weixin.qq.com/pay/closeorder";
		//与微信交互并获取信息
        String str = UrlPreUtils.post(url,xml);
        Map<String,String> retmap = new HashMap<String, String>();
        try {
            //xml转换成map
            retmap = WXPayUtil.xmlToMap(str);
        }catch (Exception e){
            e.printStackTrace();
        }

        return retmap;
    }

8.回调接口

回调地址在统一下单传递给微信端,但如果微信端没有调用的话,需要去官网设置回调地址

orderSubmitController
/**
 * 回调接口
 */
@RequestMapping(value = {"/unifiedorderNotify"})
public void unifiedorderNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
    System.out.println("回调中");
    //商户订单号
    String outTradeNo = null;
    String  xmlContent =null;


    try {
         //输入流里获取微信端发送的data
        InputStream inputStream = request.getInputStream();
        String requestXml = DataJoinUtils.getStreamString(inputStream);
        //xml转换成map
        Map<String, String> map = WXPayUtil.xmlToMap(requestXml);
        String returnCode = map.get("return_code");
        //校验是否已经支付成功&& 签名是否正确
        if (returnCode.equals("SUCCESS") && WXPayUtil.isSignatureValid(map, ConfigConstant.key,WXPayConstants.SignType.MD5)) {
            //商户订单号
            outTradeNo = map.get("out_trade_no");
            System.out.println("outTradeNo : " + outTradeNo);
            //微信支付订单号
            String transactionId = map.get("transaction_id");
            System.out.println("transactionId : " + transactionId);
            //支付完成时间
            SimpleDateFormat payFormat = new SimpleDateFormat("yyyyMMddHHmmss");
            Date payDate = payFormat.parse(map.get("time_end"));

            SimpleDateFormat systemFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("支付时间:" + systemFormat.format(payDate));

            //todo 根据支付结果修改数据库订单状态
            // ....

            //通过 response 给微信的应答 xml
            xmlContent = "<xml>" +
                    "<return_code><![CDATA[SUCCESS]]></return_code>" +
                    "<return_msg><![CDATA[OK]]></return_msg>" +
                    "</xml>";

        }else {
            xmlContent = "<xml>" +
                    "<return_code><![CDATA[FAIL]]></return_code>"+
                    "<return_msg><![CDATA[签名失败]]></return_msg>"+
                    "</xml>";
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
   	//给微信反馈
    DataJoinUtils.responsePrint(response, xmlContent);
}
工具类:DataJoinUtils 对应代码
/**
 * 输入流转化为字符串
 *
 * @param inputStream 流
 * @return String 字符串
 * @throws Exception
 */
public static String getStreamString(InputStream inputStream) throws Exception {
    StringBuffer buffer = new StringBuffer();
    InputStreamReader inputStreamReader = null;
    BufferedReader bufferedReader = null;
    try {
        inputStreamReader = new InputStreamReader(inputStream,"UTF-8");
        bufferedReader = new BufferedReader(inputStreamReader);
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            buffer.append(line);
        }
    } catch (Exception e) {
        throw new Exception();
    } finally {
        if (bufferedReader != null) {
            bufferedReader.close();
        }
        if (inputStreamReader != null) {
            inputStreamReader.close();
        }
        if (inputStream != null) {
            inputStream.close();
        }
    }
    return buffer.toString();
}


/**
     * 返回信息给微信 商户已经接收到回调
     *
     * @param response
     * @param content  内容
     * @throws Exception
     */
    public static void responsePrint(HttpServletResponse response, String content) throws Exception {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/xml");
        response.getWriter().print(content);
        response.getWriter().flush();
        response.getWriter().close();
    }

9.内网穿透

地址:http://grbj7n.natappfree.cc

请按照官网教程操作

10.二维码生成

选择了jQuery 和QRCode

参考地址:http://code.ciaoca.com/javascript/qrcode/

<script src="qrcode.js"></script>
<div id="qrcode"></div>
   var qrcode = new QRCode("qrcode");
   qrcode.makeCode(codeUrl);

11.注意事项

1.回调地址必须是外网可访问的且无参

2.微信端与商户间传递的都是xml格式

3.jar包冲突可以将引用的依赖版本降低

4.前端和后端交互记得配置application.properties文件,

spring.thymeleaf.prefix=classpath:/templates/
#禁止thymeleaf缓存(建议:开发环境设置为false,生成环境设置为true)
spring.thymeleaf.cache=false


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值