• 四、支付宝支付对接 - SDK开发、业务对接、支付回调、支付组件(2)


    一、SpringBoot - 支付宝实现当面付产品支付(二维码扫码支付)

    1.1 当面付产品介绍

    官网地址

    1. 简介

    当面付帮助商家在线下消费场景中实现快速收款,支持 条码支付 和 扫码支付 两种付款方式。商家可通过以下两种任一方式进行收款,提升收银效率,实现资金实时到账。
    条码支付:买家出示支付宝钱包中的条码、二维码,商家扫描用户条码即可完成 条码支付 收款。
    扫码支付:买家通过使用支付宝 扫一扫 功能,扫描商家收款二维码即可完成 扫码支付 付款。

    1. 整体开发流程

    在这里插入图片描述
    3. 准备参数

    • APPID
    • 商家私钥
    • 支付宝公钥
    • 支付回调地址
    • 网关地址
    • 加密签名算法RSA2

    最终达成的效果

    1.2 具体实现步骤

    1. 新建一个springboot工程
    2. pom.xml引入支付相关依赖
    3. 定义application.yml和application-dev.yml配置支付相关参数
    4. 定义支付的配置类AlipayConfig.java
    5. 定义二维码生成类QRCodeUtil.java和QrResponse.java
    6. 定义支付接口AlipayService.java和实现类AlipayServiceImpl.java
    7. 定义IndexController和AlipayController进行页面转发
    8. 在index.html中定义img标签引入支付宝二维码
    9. 测试扫码进入支付回调获取支付相关的参数
    10. 真实业务场景分析和封装支付二维码弹窗

    1.3 搭建测试

    1. 新建一个springboot 工程 daniel-alipay

    在这里插入图片描述

    在这里插入图片描述

    1. pom.xml引入支付相关依赖,将使用下面依赖替换
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-freemarkerartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <optional>trueoptional>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.13version>
    dependency>
    
    <dependency>
        <groupId>com.alipay.sdkgroupId>
        <artifactId>alipay-sdk-javaartifactId>
        <version>4.13.0.ALLversion>
    dependency>
    
    <dependency>
        <groupId>org.apache.commonsgroupId>
        <artifactId>commons-lang3artifactId>
        <version>3.10version>
    dependency>
    
    <dependency>
        <groupId>com.google.zxinggroupId>
        <artifactId>coreartifactId>
        <version>3.3.0version>
    dependency>
    
    <dependency>
        <groupId>commons-codecgroupId>
        <artifactId>commons-codecartifactId>
        <version>1.11version>
    dependency>
    
    <dependency>
        <groupId>commons-httpclientgroupId>
        <artifactId>commons-httpclientartifactId>
        <version>3.1version>
    dependency>
    
    <dependency>
        <groupId>com.github.ulisesbocchiogroupId>
        <artifactId>jasypt-spring-boot-starterartifactId>
        <version>2.1.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    1. 定义application.yml配置相关测试参数

    application.yml

    server:
      port: 8989
    spring:
      freemarker:
        suffix: .html
      profiles:
        active: dev
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 在 templates 下面创建一个 index.html 页面
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>支付宝title>
    head>
    <body>
    
    <h1>支付宝二维码支付h1>
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 创建包 com.zql.controller,并在下面创建一个 IndexController.java
    package com.zql.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    
    @Controller
    public class IndexController {
        
        
        @GetMapping("/index")
        public String index(){
            
            return "index";
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. 启动程序测试 http://localhost:8989/index在这里插入图片描述

    1.4 配置类的定义和注入

    1. 在resources下面定义application-dev.yml配置支付相关参数

    application-dev.yml

    # 支付宝支付参数配置
    alipay:
      app_id: 2021003157607237
      merchant_private_key: 公司支付宝商户私钥
      alipay_public_key: 公司支付宝公钥(这个公钥是通过商家公钥换取的公钥哦)
      notify_url: 公司支付宝异步回调地址
      return_url: 公司支付宝同步回调地址(如果是二维码扫码可以不配置,和上面异步暂配一致)
      sign_type: RSA2
      charset: utf-8
      gatewayUrl: 支付宝网关地址
      # 保存支付日志的地址 如果是linux服务器配置没有盘符
      log_path: c:/tmp/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    1. 创建包 com.zql.config 并且定义支付的配置类 AlipayConfig.java 拷贝下面代码
    package com.zql.config;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    @Component
    public class AlipayConfig {
        // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
        @Value("${alipay.app_id}")
        public  String app_id;
        // 商户私钥,您的PKCS8格式RSA2私钥
        @Value("${alipay.merchant_private_key}")
        public String merchant_private_key;
        // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
        @Value("${alipay.alipay_public_key}")
        public String alipay_public_key;
        // 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
        @Value("${alipay.notify_url}")
        public  String notify_url;
        // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
        @Value("${alipay.return_url}")
        public  String return_url;
        // 签名方式
        @Value("${alipay.sign_type}")
        public  String sign_type;
        // 字符编码格式
        @Value("${alipay.charset}")
        public  String charset;
        // 支付宝网关
        @Value("${alipay.gatewayUrl}")
        public String gatewayUrl;
        // 日志存放
        @Value("${alipay.log_path}")
        public String log_path;
        public String getApp_id() {
            return app_id;
        }
        public void setApp_id(String app_id) {
            this.app_id = app_id;
        }
        public String getMerchant_private_key() {
            return merchant_private_key;
        }
        public void setMerchant_private_key(String merchant_private_key) {
            this.merchant_private_key = merchant_private_key;
        }
        public String getAlipay_public_key() {
            return alipay_public_key;
        }
        public void setAlipay_public_key(String alipay_public_key) {
            this.alipay_public_key = alipay_public_key;
        }
        public String getNotify_url() {
            return notify_url;
        }
        public void setNotify_url(String notify_url) {
            this.notify_url = notify_url;
        }
        public String getReturn_url() {
            return return_url;
        }
        public void setReturn_url(String return_url) {
            this.return_url = return_url;
        }
        public String getSign_type() {
            return sign_type;
        }
        public void setSign_type(String sign_type) {
            this.sign_type = sign_type;
        }
        public String getCharset() {
            return charset;
        }
        public void setCharset(String charset) {
            this.charset = charset;
        }
        public String getGatewayUrl() {
            return gatewayUrl;
        }
        public void setGatewayUrl(String gatewayUrl) {
            this.gatewayUrl = gatewayUrl;
        }
        public String getLog_path() {
            return log_path;
        }
        public void setLog_path(String log_path) {
            this.log_path = log_path;
        }
        @Override
        public String toString() {
            return "AlipayConfig{" +
                    "app_id='" + app_id + '\'' +
                    ", merchant_private_key='" + merchant_private_key + '\'' +
                    ", alipay_public_key='" + alipay_public_key + '\'' +
                    ", notify_url='" + notify_url + '\'' +
                    ", return_url='" + return_url + '\'' +
                    ", sign_type='" + sign_type + '\'' +
                    ", charset='" + charset + '\'' +
                    ", gatewayUrl='" + gatewayUrl + '\'' +
                    ", log_path='" + log_path + '\'' +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    分析是否注入成功,看下面:

    在这里插入图片描述

    二、二维码生成工具

    2.1 导入相关资料

    1. 导入二维码生成类QRCodeUtil.java和QrResponse.java和其它 👇🏾👇🏾 资源获取

    在这里插入图片描述

    1. 创建包 com.zql.service 定义支付接口 AlipayService.java和实现类 AlipayServiceImpl.java(直接拷贝)

    定义支付接口 AlipayService.java

    package com.zql.service;
    
    import com.zql.vo.PayVo;
    
    /**
     * @author 徐柯
     * @Title:
     * @Package
     * @Description:
     */
    public interface AlipayService {
        /**
         * @return byte[]
         * @Author Daniel
         * @Description 阿里支付接口
         * @Param [payVo]
         **/
        byte[] alipay(PayVo payVo);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    定义支付接口实现 AlipayServiceImpl.java

    package com.zql.service;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.alipay.api.AlipayApiException;
    import com.alipay.api.AlipayClient;
    import com.alipay.api.DefaultAlipayClient;
    import com.alipay.api.domain.AlipayTradePrecreateModel;
    import com.alipay.api.request.AlipayTradePrecreateRequest;
    import com.alipay.api.response.AlipayTradePrecreateResponse;
    
    import com.zql.config.AlipayConfig;
    import com.zql.qrcode.QRCodeUtil;
    import com.zql.qrcode.QrCodeResponse;
    import com.zql.qrcode.QrResponse;
    import com.zql.util.GenerateNum;
    import com.zql.vo.PayVo;
    import lombok.extern.log4j.Log4j2;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.util.FileCopyUtils;
    import org.springframework.util.ResourceUtils;
    import javax.imageio.ImageIO;
    import javax.imageio.stream.ImageOutputStream;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    /*
     * @Author Daniel
     * @Description
     * @Param
     * @return
     **/
    @Service
    @Log4j2
    public class AlipayServiceImpl implements AlipayService {
    
        @Autowired
        private AlipayConfig alipayConfig;
        /*
         * @Author Daniel
         * @Description 阿里支付 -打赏
         * @Param [payVo]
         * @return byte[]
         **/
        @Override
        public byte[] alipay(PayVo payVo) {
            try {
                // 1:支付的用户
                String userId = payVo.getUserId();
                // 2: 支付金额
                String money = "1";
                // 3: 支付的产品
                String title = "java面向对象";
                // 4: 支付的订单编号
                String orderNumber = GenerateNum.generateOrder();
                // 5:支付宝携带的参数在回调中可以通过request获取
                JSONObject json = new JSONObject();
                json.put("userId", userId);
                json.put("orderNumber", orderNumber);
                json.put("money", money);
                String params = json.toString();
                // 6:设置支付相关的信息
                AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
                model.setOutTradeNo(orderNumber); // 自定义订单号
                model.setTotalAmount(money);// 支付金额
                model.setSubject(title);// 支付的产品名称
                model.setBody(params);// 支付的请求体参数
                model.setTimeoutExpress("30m");// 支付的超时时间
                model.setStoreId(userId+"");// 支付的库存id
                QrCodeResponse qrCodeResponse = qrcodePay(model);
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                String logopath = ResourceUtils.getFile("classpath:favicon.png").getAbsolutePath();
                BufferedImage buffImg = QRCodeUtil.encode(qrCodeResponse.getQr_code(), logopath, false);//获取二维码
                ImageOutputStream imageOut = ImageIO.createImageOutputStream(output);
                ImageIO.write(buffImg, "JPEG", imageOut);
                imageOut.close();
                ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
                return FileCopyUtils.copyToByteArray(input);
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
        /**
         * 扫码运行代码
         * 验签通过返回QrResponse
         * 失败打印日志信息
         * 参考地址:https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay
         *
         * @param model
         * @return
         */
        public QrCodeResponse qrcodePay(AlipayTradePrecreateModel model) {
            // 1: 获取阿里请求客户端
            AlipayClient alipayClient = getAlipayClient();
            // 2: 获取阿里请求对象
            AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
            // 3:设置请求参数的集合,最大长度不限
            request.setBizModel(model);
            // 设置异步回调地址
            request.setNotifyUrl(alipayConfig.getNotify_url());
            // 设置同步回调地址
            request.setReturnUrl(alipayConfig.getReturn_url());
            AlipayTradePrecreateResponse alipayTradePrecreateResponse = null;
            try {
                alipayTradePrecreateResponse = alipayClient.execute(request);
            } catch (AlipayApiException e) {
                e.printStackTrace();
            }
            QrResponse qrResponse = JSON.parseObject(alipayTradePrecreateResponse.getBody(), QrResponse.class);
            return qrResponse.getAlipay_trade_precreate_response();
        }
        /**
         * 获取AlipayClient对象
         *
         * @return
         */
        private AlipayClient getAlipayClient() {
            AlipayClient alipayClient =
                    new DefaultAlipayClient(alipayConfig.getGatewayUrl(), alipayConfig.getApp_id(), alipayConfig.getMerchant_private_key(),
                            "JSON", alipayConfig.getCharset(), alipayConfig.getAlipay_public_key(), alipayConfig.getSign_type()); //获得初始化的AlipayClient
            return alipayClient;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124

    2.2 生成支付二维码

    1. 创建包 com.zql.web 定义 AlipayController.java 进行页面转发,因为IndexController上面已经创建过了

    AlipayController.java

    package com.zql.web;
    
    import com.zql.service.AlipayService;
    import com.zql.vo.PayVo;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    
    @Controller
    public class AlipayController {
    
        @Autowired
        private AlipayService alipayService;
    
        @GetMapping("/alipay/pay")
        @ResponseBody
        public byte[] alipay(){
    
            PayVo payVo = new PayVo();
            payVo.setUserId("1");
            payVo.setCourseid("1");
            return alipayService.alipay(payVo);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    1. 在index.html中定义img标签引入支付宝二维码
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>支付宝title>
    head>
    <body>
    
            <h1>支付宝二维码支付h1>
            <img src="/alipay/pay" alt="没找到">
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    浏览器刷新 localhost:8089/index:则可以看到类似下面的二维码

    在这里插入图片描述

    在这里插入图片描述

    1. 测试扫码进入支付回调获取支付相关的参数

    2. 真实业务场景分析和封装支付二维码弹窗

    三、SpringBoot - 支付宝二维码支付 -数据渲染工作

    3.1 产品数据接口的定义和测试

    实现步骤

    1. 创建数据库 daniel-db,导入SQL课程产品脚本
    2. 在项目中追加引入mybatis-plus 相关依赖与数据库链接
    3. 在application.yaml中配置如下信息
    4. 在application-dev.xml 配置数据的链接相关信息
    5. 在springboot的启动类中增加@MapperScan注解
    6. 然后在项目中分别创建:entity,mapper,service和controller
    7. 获取定义支付产品页面
    8. 在页面中引入vue.min.jsaxios.min.jsmain.css
    9. 完成课程产品的数据异步渲染工作
    10. 在浏览器访问测试异步查询课程信息
    11. 最后完整的项目结构
    1. 创建数据库daniel-db,导入SQL课程产品脚本
    /*
    Navicat MySQL Data Transfer
    Source Server         : localhost
    Source Server Version : 50733
    Source Host           : localhost:3306
    Source Database       : daniel-db
    Target Server Type    : MYSQL
    Target Server Version : 50733
    File Encoding         : 65001
    Date: 2022-10-15 19:48:54
    */
    SET FOREIGN_KEY_CHECKS=0;
    -- ----------------------------
    -- Table structure for kss_courses
    -- ----------------------------
    DROP TABLE IF EXISTS `kss_courses`;
    CREATE TABLE `kss_courses` (
      `courseid` VARCHAR(32) NOT NULL COMMENT '课程唯一id',
      `title` VARCHAR(100) DEFAULT NULL COMMENT '课程标题',
      `intro` VARCHAR(500) DEFAULT NULL COMMENT '课程简短介绍',
      `img` VARCHAR(300) DEFAULT NULL COMMENT '课程封面地址',
      `price` DECIMAL(10,2) DEFAULT NULL COMMENT '课程的活动价',
      `status` INT(1) DEFAULT NULL COMMENT '状态:已发布/未发布',
      `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
      `update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
      PRIMARY KEY (`courseid`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    -- ----------------------------
    -- Records of kss_courses
    -- ----------------------------
    INSERT INTO `kss_courses` VALUES ('1317503462556848129', '预科阶段', '学习编程之前你要了解的知识!', '/assert/course/c1/02.jpg', '0.01', '1', '2021-10-18 00:31:18', '2022-04-01 10:56:38');
    INSERT INTO `kss_courses` VALUES ('1317503769349214209', '入门环境搭建', '工欲善其事,必先利其器!', '/assert/course/c1/03.jpg', '0.01', '1', '2021-10-18 00:32:31', '2022-04-01 10:53:10');
    INSERT INTO `kss_courses` VALUES ('1317504142650658818', '基础语法学习', '基础决定你未来的高度!', '/assert/course/c1/04.jpg', '0.01', '1', '2021-10-18 00:34:00', '2022-04-01 10:54:18');
    INSERT INTO `kss_courses` VALUES ('1317504447027105793', '流程控制学习', '程序的本质就是这些!', '/assert/course/c1/05.jpg', '0.01', '1', '2021-10-18 00:35:13', '2022-04-01 10:56:03');
    INSERT INTO `kss_courses` VALUES ('1317504610634321921', '方法详解', '封装的思想!', '/assert/course/c1/06.jpg', '0.01', '1', '2021-10-18 00:35:52', '2022-04-01 10:55:04');
    INSERT INTO `kss_courses` VALUES ('1317504817342205954', '数组详解', '最简单的数据结构!', '/assert/course/c1/07.jpg', '0.01', '1', '2021-10-18 00:35:52', '2022-10-18 00:35:52');
    INSERT INTO `kss_courses` VALUES ('1317504988834713602', '面向对象编程', 'Java的精髓OOP!', '/assert/course/c1/08.jpg', '0.01', '1', '2021-10-18 00:35:52', '2022-10-18 00:35:52');
    INSERT INTO `kss_courses` VALUES ('1377518279077142529', '第三方支付课程-支付宝', '第三方支付课程-支付宝', '/assert/course/c10/07.jpg', '0.01', '1', '2021-10-18 00:18:08', '2022-04-01 10:54:25');
    -- ----------------------------
    -- Table structure for kss_order_detail
    -- ----------------------------
    DROP TABLE IF EXISTS `kss_order_detail`;
    CREATE TABLE `kss_order_detail` (
      `id` BIGINT(20) NOT NULL,
      `courseid` VARCHAR(20) DEFAULT NULL,
      `coursetitle` VARCHAR(255) DEFAULT NULL,
      `courseimg` VARCHAR(255) DEFAULT NULL,
      `userid` VARCHAR(32) DEFAULT NULL,
      `ordernumber` VARCHAR(100) DEFAULT NULL,
      `tradeno` VARCHAR(100) DEFAULT NULL,
      `create_time` DATETIME DEFAULT NULL,
      `update_time` DATETIME DEFAULT NULL,
      `username` VARCHAR(100) DEFAULT NULL,
      `price` VARCHAR(10) DEFAULT NULL,
      `paymethod` VARCHAR(10) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    -- ----------------------------
    -- Records of kss_order_detail
    -- ----------------------------
    INSERT INTO `kss_order_detail` VALUES ('1377561070331342849', '1317503462556848129', '预科阶段', '/assert/course/c1/02.jpg', '1', '2021040117565301', '2021040122001474081439646913', '2021-04-01 17:58:48', '2021-04-01 17:58:48', 'daniel', '0.01', '1');
    INSERT INTO `kss_order_detail` VALUES ('1377561833455484929', '1317503769349214209', '入门环境搭建', '/assert/course/c1/03.jpg', '1', '2021040118015901', '2021040122001474081440101072', '2021-04-01 18:01:50', '2021-04-01 18:01:50', 'daniel', '0.01', '1');
    INSERT INTO `kss_order_detail` VALUES ('1377562728612233218', '1317503769349214209', '入门环境搭建', '/assert/course/c1/03.jpg', '1', '2021040118053301', '2021040122001474081440405818', '2021-04-01 18:05:23', '2021-04-01 18:05:23', 'daniel', '0.01', '1');
    INSERT INTO `kss_order_detail` VALUES ('1377564997252657153', '1317504142650658818', '基础语法学习', '/assert/course/c1/04.jpg', '1', '2021040118134201', '2021040122001474081440148822', '2021-04-01 18:14:24', '2021-04-01 18:14:24', 'daniel', '0.01', '1');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    在这里插入图片描述
    2. 在项目中追加引入 mybatis-plus相关依赖与数据库链接

    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>5.1.47version>
    dependency>
    
    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-boot-starterartifactId>
        <version>3.4.0version>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-jdbcartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    1. 在 application.yaml 中配置如下信息
    server:
      port: 8989
    spring:
      freemarker:
        suffix: .html
      profiles:
        active: dev
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
        locale: zh_CN
        # 解决json返回过程中long的精度丢失问题
        generator:
          write-numbers-as-strings: true
          write-bigdecimal-as-plain: true
        servlet:
          content-type: text/html
          multipart:
            max-file-size: 2MB
            max-request-size: 2MB
        mvc:
          servlet:
            load-on-startup: 1 #SpringBoot的接口第一次访问都很慢,通过日志可以发现,dispatcherServlet不是一开始就加载的,有访问才开始加载的,即懒加载。
        session:
          store-type: redis
          # session退出以后30分钟清除信息
          timeout: 1800
        main:
          allow-bean-definition-overriding: true
    # mybatis-plus配置
    mybatis-plus:
      mapper-locations: classpath*:/mapper/*.xml
      type-aliases-package: com.zql.entity
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    1. 在 application-dev.xml 配置数据的链接相关信息
    # 数据库连接
    spring:
      datasource:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/daniel-db?serverTimezone=GMT%2b8&useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: root
        hikari:
          connection-timeout: 60000
          validation-timeout: 3000
          idle-timeout: 60000
          login-timeout: 5
          max-lifetime: 60000
          maximum-pool-size: 400
          minimum-idle: 100
          read-only: false
    # 日志管理
    logging:
      level:
        root: info
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. 在springboot的启动类中增加@MapperScan注解
    package com.zql;
    
    import com.zql.config.AlipayConfig;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    @MapperScan("com.zql.mapper")
    public class DanielAlipayApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DanielAlipayApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述
    6. 然后在项目中分别创建包:entity,mapper,servicecontroller

    在 entity下创建数据库对应实体 ProductCourse.java

    package com.zql.entity;
    
    import com.baomidou.mybatisplus.annotation.*;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    import lombok.experimental.Accessors;
    import java.math.BigDecimal;
    import java.util.Date;
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    @Data
    @ToString
    @AllArgsConstructor
    @NoArgsConstructor
    @Accessors(chain = true)
    @TableName("kss_courses")
    public class ProductCourse {
        // 课程ID
        @TableId(type = IdType.ID_WORKER_STR)
        private Long courseid;
        // 课程标题
        private String title;
        // 课程介绍
        private String intro;
        // 课程封面
        private String img;
        // 课程价格
        private BigDecimal price;
        // 课程状态
        private Integer status;
        // 课程创建时间
        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
        // 课程更新时间
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    1. 在mapper下面创建接口 ProductCourseMapper.java 并继承 BaseMapper
    package com.zql.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.zql.entity.ProductCourse;
    import org.springframework.stereotype.Repository;
    
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    
    @Repository
    public interface ProductCourseMapper extends BaseMapper<ProductCourse> {
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 在 service下面创建接口 ProductCourseService.java 并继承 IService
    package com.zql.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.zql.entity.ProductCourse;
    
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    public interface ProductCourseService extends IService<ProductCourse> {
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 在 service下面创建类 ProductCourseServiceImpl.java 继承 ServiceImpl 并实现接口 ProductCourseService
    package com.zql.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.zql.entity.ProductCourse;
    import com.zql.mapper.ProductCourseMapper;
    import com.zql.service.ProductCourseService;
    import org.springframework.stereotype.Service;
    
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    @Service
    public class ProductCourseServiceImpl extends ServiceImpl<ProductCourseMapper, ProductCourse> implements ProductCourseService{
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 在 com.zql.web下面创建 CourseController.java
    package com.zql.web;
    
    import com.zql.entity.ProductCourse;
    import com.zql.service.ProductCourseService;
    import com.zql.vo.R;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.List;
    
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    
    @Controller
    public class CourseController {
    
        @Autowired
        private ProductCourseService productCourseService;
    
    
        @GetMapping("/api/course/list")
        @ResponseBody
        public R main() {
            List<ProductCourse> courseList = productCourseService.list();
            return R.ok().data("courseList", courseList);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    1. 在 templates/mapper/ProductCourseMapper.xml(可以不配置)
    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.zql.mapper.ProductCourseMapper">
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    1. 测试查询接口

    在浏览器访问:http://localhost:8989/api/course/list

    在这里插入图片描述

    使用postman 测试查询接口

    在这里插入图片描述

    3.2 Vue 实现产品页面和数据渲染

    1. 获取定义支付产品页面(静态数据,即非数据库)

    资源 获取 - 然后放置在项目路的static 和 templates即可
    在这里插入图片描述

    并在IndexController中定义跳转页面

    @GetMapping("/main")
    public String main(){
    
        return "main";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    测试浏览器输入: http://localhost:8989/main

    在这里插入图片描述

    1. 获取定义支付产品页面(动态获取,仅浏览器控制台显示)

    修改 main.html

    在这里插入图片描述

    <script>
        var vue = new Vue({
    
            el:"#app",
    
            data:{
    
            },
            created:function () {
                //加载课程产品
                this.loadCourse();
            },
            methods:{
    
                loadCourse:function () {
                    //产品课程的异步请求
    
                    axios.post("/api/course/list").then(function (res) {
    
                       console.log("response===================>",res);
                    })
    
                }
            }
        })
    
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    启动程序查看浏览器控制台:http://localhost:8989/main

    在这里插入图片描述
    修改代码:

    在这里插入图片描述

    <script>
        var vue = new Vue({
            el:"#app",
    
            data:{
    
                courseList:[]
            },
            created:function () {
                //加载课程产品
                this.loadCourse();
            },
            methods:{
                //产品课程的异步请求
                loadCourse:function () {
    
                    var that =  this;
                    axios.post("/api/course/list").then(function (res) {
    
                       console.log("response===================>",res);
    
                       if(res.data.code == 20000){
    
                           that.courseList = res.data.data.courseList;
                       }
                    })
    
                }
            }
        })
    
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    再次启动程序发现渲染到了页面上
    在这里插入图片描述

    1. 获取定义支付产品页面(动态获取,循环遍历数据库中的数据)

    在这里插入图片描述

    <div   v-for="(course,index) in courseList"  class="col-lg-3 col-md-4 col-sm-6 animated fadeInUp delay-1s">
        <div class="course-item">
            <div class="course-img "><a
                    :href="'https://www.kuangstudy.com/course/detail/'+course.courseid"
                    target="_blank" :title="course.title" class="course__img"><img
                    height="140" width="100%" :src="'https://www.kuangstudy.com//'+course.img"> <span
                    class="num">1</span> <span class="stimer">{{course.price}}</span></a></div>
            <div class="course-content"><h3  :title="course.title" class="course__title"><a
                    href="https://www.kuangstudy.com/course/detail/1317503462556848129"
                    target="_blank" :title="course.title" class="course__img">{{course.title}}</a>
            </h3>
                <p class="course__author">{{course.intro}}</p>
                <div class="course-price-wrap"><span class="course__btn"><i
                        class="iconfont iconshouye"></i>  点击支付</span></div>
            </div>
        </div>
    </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    3.3 产品数据和支付二维码对接

    修改 AlipayController.java

    在这里插入图片描述

    修改 main.html

    在这里插入图片描述

    修改main.html 看是否能拿到 courseid
    在这里插入图片描述

    后端打断点,并刷新浏览器,查看控制台

    在这里插入图片描述

    在这里插入图片描述

    结果成功绑定
    在这里插入图片描述

    再次添加绑定取数据库courseid

    在这里插入图片描述

    结果确实取到了 👇🏾👇🏾

    在这里插入图片描述

    然后当你点击哪一个支付,就会对应有自己的courseid ,即可有对应的二维码生成

    在这里插入图片描述
    在这里插入图片描述

    <script>
        var vue = new Vue({
            el:"#app",
    
            data:{
    
                courseList:[],
    
                courseId:""
            },
            created:function () {
                //加载课程产品
                this.loadCourse();
            },
            methods:{
                //产品课程的异步请求
                loadCourse:function () {
                    var that =  this;
                    axios.post("/api/course/list").then(function (res) {
    
                       console.log("response===================>",res);
    
                       if(res.data.code == 20000){
    
                           that.courseList = res.data.data.courseList;
    
                           that.courseId = that.courseList[0].courseid;
                       }
                    })
                },
                changePay:function (index) {
                    //alert(1)
                    var courseId = this.courseList[index].courseid;
                    //alert(courseId)
                    this.courseId = courseId;  //也可以一步到位  this.courseId = this.courseList[index].courseid;
                }
            }
        })
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    修改接口实现类 AlipayServiceImpl.java

        @Autowired
        private ProductCourseService productCourseService;
    
        @Override
        public byte[] alipay(PayVo payVo) {
            try {
                // 1:支付的用户
                String userId = payVo.getUserId();
                ProductCourse productCourse = productCourseService.getById(payVo.getCourseid());
                if(productCourse == null) return null;
                // 2: 支付金额
                String money = productCourse.getPrice().toString();
                // 3: 支付的产品
                String title = productCourse.getTitle();
                // 4: 支付的订单编号
                String orderNumber = GenerateNum.generateOrder();
                // 5:支付宝携带的参数在回调中可以通过request获取
                JSONObject json = new JSONObject();
                json.put("userId", userId);
                json.put("orderNumber", orderNumber);
                json.put("money", money);
                String params = json.toString();
                // 6:设置支付相关的信息
                AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
                model.setOutTradeNo(orderNumber); // 自定义订单号
                model.setTotalAmount(money);// 支付金额
                model.setSubject(title);// 支付的产品名称
                model.setBody(params);// 支付的请求体参数
                model.setTimeoutExpress("30m");// 支付的超时时间
                model.setStoreId(userId+"");// 支付的库存id
                QrCodeResponse qrCodeResponse = qrcodePay(model);
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                String logopath = ResourceUtils.getFile("classpath:favicon.png").getAbsolutePath();
                BufferedImage buffImg = QRCodeUtil.encode(qrCodeResponse.getQr_code(), logopath, false);//获取二维码
                ImageOutputStream imageOut = ImageIO.createImageOutputStream(output);
                ImageIO.write(buffImg, "JPEG", imageOut);
                imageOut.close();
                ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
                return FileCopyUtils.copyToByteArray(input);
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里插入图片描述

    刷新浏览器测试,则支付已经打通,点击支付将显示对应自己的支付二维码

    四、SpringBoot - 支付宝二维码支付 - 支付回调和轮询监听

    4.1 支付回调流程概述

    1. 实现步骤
    1. 增强支付的的类型
    2. 增加订单明细相关的业务
    3. 完成定时轮询监听支付回调的开发工作
    4. 支付过程中的细节已经优化
    1. 增加订单明细相关业务,在entity包下创建 OrderDetail.java
    
    package com.zql.entity;
            import com.baomidou.mybatisplus.annotation.*;
            import lombok.AllArgsConstructor;
            import lombok.Data;
            import lombok.NoArgsConstructor;
            import lombok.ToString;
            import lombok.experimental.Accessors;
            import java.util.Date;
    /**
     * @author Daniel
     * @Title:
     * @Package
     * @Description:
     */
    @Data
    @ToString
    @AllArgsConstructor
    @NoArgsConstructor
    @Accessors(chain = true)
    @TableName("kss_order_detail")
    public class OrderDetail {
        // 主键
        @TableId(value = "id", type = IdType.ID_WORKER)
        private Long id;
        // 支付课程id
        private String courseid;
        // 支付课程标题
        private String coursetitle;
        // 支付课程封面
        private String courseimg;
        // 支付价格
        private String price;
        // 支付用户
        private String userid;
        // 支付用户昵称
        private String username;
        // 支付流水订单号
        private String ordernumber;
        // 支付交易号
        private String tradeno;
        // 1 alipay 2 weixin
        private String paymethod;
        // 课程创建时间
        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
        // 课程更新时间
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    1. 在mapper包下创建接口 OrderDetailMapper.java
    package com.zql.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.zql.entity.OrderDetail;
    import org.springframework.stereotype.Repository;
    
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    @Repository
    public interface OrderDetailMapper extends BaseMapper<OrderDetail> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 在包 service和 impl 下面分别创建接口和实现类 OrderDetailService.java OrderDetailServiceImp.java

    OrderDetailService.java

    package com.zql.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.zql.entity.OrderDetail;
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    public interface OrderDetailService extends IService<OrderDetail> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    OrderDetailServiceImp.java

    package com.zql.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.zql.entity.OrderDetail;
    import com.zql.mapper.OrderDetailMapper;
    import com.zql.service.OrderDetailService;
    import org.springframework.stereotype.Service;
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    @Service
    public class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements OrderDetailService {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 完成支付回调

    在 AlipayController.java 中完成回调

    /**
    * 定义支付回调的地址
    *
    * 1.第一步:打成一个jar,发布到正式服务器
    * 2.第二步:购买一个域名:https://www.wmsspark.top/
    * 3.第三步:部署项目到服务器上。 java -jar daniel-alipay.jar >>1.txt &
    * 4.第四步:获取真实的回调地址 https://www.wmsspark.top/alipay/notifyUrl?body=¶m&
    */
    /**
     * 异步通知
     */
    @ResponseBody
    @RequestMapping("/alipay/notifyUrl")
    public String notify_url(HttpServletRequest request) throws Exception {
        boolean result = alipayCallback(request);
        if (result) {
            return "success"; // 请不要修改或删除
        } else {
            // 验证失败
            return "fail";
        }
    }
    /**
     * 支付宝回调
     * @Author: Daniel
     * @return
     * @throws Exception
     */
    private boolean alipayCallback(HttpServletRequest request) throws Exception {
        // 获取支付宝GET过来反馈信息
        Map<String, String> params = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            params.put(name, new String(valueStr.getBytes("ISO-8859-1"), "UTF-8"));
        }
        // 计算得出通知验证结果
        Log.info("1:---->获取支付宝回传的参数---------------------------------->" + params);
        // 返回公共参数
        String extparamString = request.getParameter("extra_common_param");
        log.info("2---->:支付宝交易返回的参数:{}", extparamString);
        String tradeno = params.get("trade_no");;
        //交易完成
        String body = params.get("body");
        if (StringUtils.isEmpty(tradeno)) {
            tradeno = params.get("trade_no");
        }
        log.info("3---->:【支付宝】交易的参数信息是:{},流水号是:{}", body, tradeno);
        try {
            JSONObject bodyJson = JSONObject.parseObject(body);
            String userId = bodyJson.getString("userId");
            String ptype = bodyJson.getString("ptype");
            String orderNumber = bodyJson.getString("orderNumber");
            log.info("4---->:【支付宝】交易的参数信息是:ptype:{},orderNumber是:{}",  ptype,orderNumber);
            // 课程支付
            if (ptype != null && ptype.equalsIgnoreCase("productcourse")) {
                payCommonService.payproductcourse(bodyJson, userId, orderNumber, tradeno, "1");
            }
        } catch (Exception ex) {
            log.info("支付宝支付出现了异常.....");
            ex.printStackTrace();
            return false;
        }
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    1. 将回调地址添加到支付宝开放平台“授权回调地址”中
      在这里插入图片描述

    2. 打包上传到服务器,测试可得到数据库产生一条支付成功的数据。

    3. 在service包下创建 通用的支付 PayCommonService.java

    package com.zql.service;
    
    import com.alibaba.fastjson.JSONObject;
    import com.zql.entity.OrderDetail;
    import com.zql.entity.ProductCourse;
    import com.zql.mapper.OrderDetailMapper;
    import com.zql.mapper.ProductCourseMapper;
    import lombok.extern.log4j.Log4j2;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    /**
     * @Author:Daniel
     * @Version 1.0
     */
    @Component
    @Log4j2
    public class PayCommonService {
        @Autowired
        private OrderDetailMapper orderDetailMapper;
        @Autowired
        private ProductCourseMapper productCourseMapper;
        /**
         * 支付回调封装
         * @param jsonObject
         * @param userId
         * @param orderNumber
         * @param transaction_id
         * @param paymethod
         */
        public void payproductcourse(JSONObject jsonObject, String userId, String orderNumber, String transaction_id, String paymethod) {
            String courseId = jsonObject.getString("courseId");
            String money = jsonObject.getString("money");
            ProductCourse productCourse = productCourseMapper.selectById(courseId);
            if (productCourse == null) {
                log.info("【" + (paymethod.equals("2") ? "微信" : "支付宝") + "】你支付的课程被删除了:{}", courseId);
                return;
            }
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setUserid(userId);
            orderDetail.setCourseid(courseId);
            orderDetail.setUsername("Daniel");
            orderDetail.setPaymethod(paymethod);
            orderDetail.setCoursetitle(productCourse.getTitle());
            orderDetail.setCourseimg(productCourse.getImg());
            orderDetail.setOrdernumber(orderNumber);
            orderDetail.setTradeno(transaction_id);
            orderDetail.setPrice(money == null ? "0.01" : money);
            orderDetailMapper.insert(orderDetail);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    服务器端接收的回调信息

    监听数据的异步回调如下
    定义 OrderDetailController.java 监听支付是否成功

    package com.zql.web;
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.zql.entity.OrderDetail;
    import com.zql.service.OrderDetailService;
    import com.zql.vo.PayVo;
    import com.zql.vo.R;
    import lombok.extern.log4j.Log4j2;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * @Author:Daniel
     * @description 支付成功和失败回调
     * @Version 1.0
     */
    @Controller
    @Log4j2
    public class OrderDetailController {
        @Autowired
        private OrderDetailService orderDetailService;
        
        @PostMapping("/api/paycallback/course")
        @ResponseBody
        public R payCallback(@RequestBody PayVo payVo) {
            String userid = "1";
            QueryWrapper<OrderDetail> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userid", userid);
            queryWrapper.eq("courseid", payVo.getCourseid());
            int count = orderDetailService.count(queryWrapper);
            return count > 0 ? R.ok() : R.error();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    五、SpringBoot - 支付宝二维码支付 - 支付组件封装

    六、Springboot - jasypt - 配置文件yml密码加密

    七、支付宝统一支付参数接口说明

  • 相关阅读:
    React组件生命周期
    JSD-2204-酷莎商城(管理员模块)-密码加密-Day10
    Thrift/RPC学习分享
    《大话设计模式》学习总结
    【网站架构】集群性能并非无限扩展,几千万几亿的网站系统贵在哪
    【邻接表特点,邻接表的代码实现】
    不依赖vue实例,怎么实现一个eventBus?
    webpack 中,filename 和 chunkFilename 的区别
    UICollectionView 学习笔记
    深度剖析 Vue3 如何通过虚拟DOM更新页面
  • 原文地址:https://blog.csdn.net/weixin_42171159/article/details/127455605