栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间:发布时间: 百科书网 趣学号

 目录
  • 一、SpringBoot - 支付宝实现当面付产品支付(二维码扫码支付)
    • 1.1 当面付产品介绍
    • 1.2 具体实现步骤
    • 1.3 搭建测试
    • 1.4 配置类的定义和注入
  • 二、二维码生成工具
    • 2.1 导入相关资料
    • 2.2 生成支付二维码
  • 三、SpringBoot - 支付宝二维码支付 -数据渲染工作
    • 3.1 产品数据接口的定义和测试
    • 3.2 Vue 实现产品页面和数据渲染
    • 3.3 产品数据和支付二维码对接
  • 四、SpringBoot - 支付宝二维码支付 - 支付回调和轮询监听
    • 4.1 支付回调流程概述
  • 五、SpringBoot - 支付宝二维码支付 - 支付组件封装
  • 六、Springboot - jasypt - 配置文件yml密码加密
  • 七、支付宝统一支付参数接口说明

一、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引入支付相关依赖,将使用下面依赖替换


    org.springframework.boot
    spring-boot-starter-freemarker



    org.springframework.boot
    spring-boot-starter-web



    org.projectlombok
    lombok
    true



    org.springframework.boot
    spring-boot-starter-test
    test


    junit
    junit
    4.13



    com.alipay.sdk
    alipay-sdk-java
    4.13.0.ALL



    org.apache.commons
    commons-lang3
    3.10



    com.google.zxing
    core
    3.3.0



    commons-codec
    commons-codec
    1.11



    commons-httpclient
    commons-httpclient
    3.1



    com.github.ulisesbocchio
    jasypt-spring-boot-starter
    2.1.0

  1. 定义application.yml配置相关测试参数

application.yml

server:
  port: 8989
spring:
  freemarker:
    suffix: .html
  profiles:
    active: dev
  1. 在 templates 下面创建一个 index.html 页面



    
    支付宝



支付宝二维码支付

  1. 创建包 com.zql.controller,并在下面创建一个 IndexController.java
package com.zql.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;



@Controller
public class IndexController {
    
    
    @GetMapping("/index")
    public String index(){
        
        return "index";
    }
    
}
  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. 创建包 com.zql.config 并且定义支付的配置类 AlipayConfig.java 拷贝下面代码
package com.zql.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


@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 + ''' +
                '}';
    }
}

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

二、二维码生成工具 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;


public interface AlipayService {
    
    byte[] alipay(PayVo payVo);
}

定义支付接口实现 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;

@Service
@Log4j2
public class AlipayServiceImpl implements AlipayService {

    @Autowired
    private AlipayConfig alipayConfig;
    
    @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;
        }
    }
    
    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();
    }
    
    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;
    }
}
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;



@Controller
public class AlipayController {

    @Autowired
    private AlipayService alipayService;

    @GetMapping("https://blog.csdn.net/alipay/pay")
    @ResponseBody
    public byte[] alipay(){

        PayVo payVo = new PayVo();
        payVo.setUserId("1");
        payVo.setCourseid("1");
        return alipayService.alipay(payVo);
    }

}
  1. 在index.html中定义img标签引入支付宝二维码



    
    支付宝



        

支付宝二维码支付

没找到

浏览器刷新 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.js 和axios.min.js和main.css
  9. 完成课程产品的数据异步渲染工作
  10. 在浏览器访问测试异步查询课程信息
  11. 最后完整的项目结构
  1. 创建数据库daniel-db,导入SQL课程产品脚本
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');


2. 在项目中追加引入 mybatis-plus相关依赖与数据库链接



    mysql
    mysql-connector-java
    5.1.47



    com.baomidou
    mybatis-plus-boot-starter
    3.4.0


    org.springframework.boot
    spring-boot-starter-jdbc

  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
@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. 在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;



@Repository
public interface ProductCourseMapper extends BaseMapper {
    
}

  1. 在 service下面创建接口 ProductCourseService.java 并继承 IService
package com.zql.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.zql.entity.ProductCourse;


public interface ProductCourseService extends IService {
    
}
  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;


@Service
public class ProductCourseServiceImpl extends ServiceImpl implements ProductCourseService{

}
  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;



@Controller
public class CourseController {

    @Autowired
    private ProductCourseService productCourseService;


    @GetMapping("/api/course/list")
    @ResponseBody
    public R main() {
        List courseList = productCourseService.list();
        return R.ok().data("courseList", courseList);
    }
}
  1. 在 templates/mapper/ProductCourseMapper.xml(可以不配置)




  1. 测试查询接口

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

使用postman 测试查询接口

3.2 Vue 实现产品页面和数据渲染
  1. 获取定义支付产品页面(静态数据,即非数据库)

在资源 获取 - 然后放置在项目路的static 和 templates即可

并在IndexController中定义跳转页面

@GetMapping("/main")
public String main(){

    return "main";
}

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

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

修改 main.html


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


修改代码:


再次启动程序发现渲染到了页面上

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

{{course.title}}

{{course.intro}}

点击支付

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

修改 AlipayController.java

修改 main.html

修改main.html 看是否能拿到 courseid

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

结果成功绑定

再次添加绑定取数据库courseid

结果确实取到了 

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



修改接口实现类 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;
        }
    }

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

四、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;

@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. 在mapper包下创建接口 OrderDetailMapper.java
package com.zql.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zql.entity.OrderDetail;
import org.springframework.stereotype.Repository;


@Repository
public interface OrderDetailMapper extends BaseMapper {
}
  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;

public interface OrderDetailService extends IService {
}

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;

@Service
public class OrderDetailServiceImpl extends ServiceImpl implements OrderDetailService {
}
  1. 完成支付回调

在 AlipayController.java 中完成回调


@ResponseBody
@RequestMapping("/alipay/notifyUrl")
public String notify_url(HttpServletRequest request) throws Exception {
    boolean result = alipayCallback(request);
    if (result) {
        return "success"; // 请不要修改或删除
    } else {
        // 验证失败
        return "fail";
    }
}

private boolean alipayCallback(HttpServletRequest request) throws Exception {
    // 获取支付宝GET过来反馈信息
    Map params = new HashMap();
    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. 在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;

@Component
@Log4j2
public class PayCommonService {
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private ProductCourseMapper productCourseMapper;
    
    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);
    }
}

服务器端接收的回调信息

监听数据的异步回调如下
定义 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.*;


@Controller
@Log4j2
public class OrderDetailController {
    @Autowired
    private OrderDetailService orderDetailService;
    
    @PostMapping("/api/paycallback/course")
    @ResponseBody
    public R payCallback(@RequestBody PayVo payVo) {
        String userid = "1";
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userid", userid);
        queryWrapper.eq("courseid", payVo.getCourseid());
        int count = orderDetailService.count(queryWrapper);
        return count > 0 ? R.ok() : R.error();
    }
}
五、SpringBoot - 支付宝二维码支付 - 支付组件封装 六、Springboot - jasypt - 配置文件yml密码加密 七、支付宝统一支付参数接口说明
转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/1077860.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号