本文最后更新于 2024-04-24,欢迎来到我的Blog! https://www.zpeng.site/

SpringBoot整合alipay

1、简介

https://open.alipay.com/

服务端 SDK

支付宝将与服务端交互的接口(OpenAPI)封装在开发工具包(SDK)中,开发者无需自行实现同服务端交互的复杂逻辑,直接将 SDK 导入自己的工程后,通过 OpenAPI 的示例代码实现同支付宝服务端的交互。根据开发者的使用习惯,支付宝开放平台共推出了通用版 SDK 及轻量化的 Easy SDK,两个版本的 SDK 可以共存,并无冲突。

  • 通用版:适用所有场景及功能,推荐使用。

  • Easy 版:适用于 Java、C#、PHP,轻量化裁剪。

客户端 SDK

开发者的移动应用接入支付宝开放平台提供的部分产品(如 App 支付 和 分享到支付宝 等),需要集成支付宝提供的客户端 SDK。

  • App 支付

  • 分享到支付宝

2、配置沙箱账号

https://open.alipay.com/develop/sandbox/app


需要获取:AppId、支付宝网关地址、应用私钥、支付宝公钥。

3、配置内网穿透账号

https://natapp.cn/

1、注册账号

2、开通免费隧道

3、natapp下载

4、配置启动

新建start.bat natapp.exe -authtoken=4f64a746e1f1173 双击启动

把本地的服务暴露到公网,让支付宝可以调用我们的接口给我们传递数据

5、配置yml

4、SpringBoot整合

支付宝sdk

        <!-- 支付宝SDK -->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.35.79.ALL</version>
        </dependency>

其它

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.18</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

准备工作

前端代码以及order、product表的基本查询(controller、service、mapper、xml等)见gitee项目

https://gitee.com/pengqaq/learn

sql语句

/*
 Navicat Premium Data Transfer

 Source Server         : localhost_3306
 Source Server Type    : MySQL
 Source Server Version : 80034 (8.0.34)
 Source Host           : localhost:3306
 Source Schema         : alipay

 Target Server Type    : MySQL
 Target Server Version : 80034 (8.0.34)
 File Encoding         : 65001

 Date: 19/11/2023 17:06:28
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for goods
-- ----------------------------
DROP TABLE IF EXISTS `goods`;
CREATE TABLE `goods`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品名称',
  `no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品编号',
  `price` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品价格',
  `date` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '生产日期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商品表' ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of goods
-- ----------------------------
INSERT INTO `goods` VALUES (1, 'CHERRY樱桃MX10.0游戏竞技超薄机械键盘', 'CHERRY MX10.0', 1399.00, '2023-11-19');
INSERT INTO `goods` VALUES (2, '达尔优61蓝牙机械键盘', 'RK61', 473.00, '2023-11-19');
INSERT INTO `goods` VALUES (3, '樱桃cherry轴RK机械键盘', '987/988樱桃', 229.00, '2023-11-19');

-- ----------------------------
-- Table structure for orders
-- ----------------------------
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `order_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '订单编号',
  `goods_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品名称',
  `num` int NULL DEFAULT NULL COMMENT '商品数量',
  `total` decimal(10, 2) NULL DEFAULT NULL COMMENT '总价格',
  `create_time` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建时间',
  `pay_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '支付编号',
  `pay_time` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '支付时间',
  `status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '订单状态',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '订单表' ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of orders
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;

application.yml

alipay:
  appId: xxx
  appPrivateKey: xxx
  alipayPublicKey: xxx
  notifyUrl: http://xxx/alipay/notify
 server:
  port: 9090

spring:
  application:
    name: pay
  # 配置数据源
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/learn?useUnicode=true&characterEncoding=utf-8
    username: root
    password: root

#mybatis配置信息
mybatis:
  # xml映射文件所在的路径,一般用模糊匹配来指定最终的xml文件
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    #采用驼峰形式将数据表中以‘_’分隔的字段映射到java类的某个属性,比如表字段user_name可以映射为类里面的userName属性
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


alipay:
  appId: xxx
  appPrivateKey: xxx
  alipayPublicKey: xxx
  notifyUrl: http://xxx/alipay/notify

配置类AliPayConfig

package com.example.pay.alipay.common;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 阿里支付配置组件,用于存储支付宝的配置信息。
 * 该类通过@Component和@ConfigurationProperties注解,使得它成为一个Spring组件,
 * 并且可以从配置文件中自动加载以"alipay"为前缀的配置属性。
 */
@Component
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {

    // 支付宝分配给开发者的应用ID
    private String appId;
    // 应用的私钥,用于和支付宝进行RSA256加密通信
    private String appPrivateKey;
    // 支付宝的公钥,用于验证支付宝的签名
    private String alipayPublicKey;
    // 支付宝异步通知的URL地址,必须是公网可访问的接口地址
    private String notifyUrl;

    /**
     * 获取支付宝的应用ID
     *
     * @return 返回支付宝的AppId字符串
     */
    public String getAppId() {
        return appId;
    }

    /**
     * 设置支付宝的应用ID
     *
     * @param appId 支付宝分配的应用ID
     */
    public void setAppId(String appId) {
        this.appId = appId;
    }

    /**
     * 获取应用的私钥
     *
     * @return 返回应用的私钥字符串
     */
    public String getAppPrivateKey() {
        return appPrivateKey;
    }

    /**
     * 设置应用的私钥
     *
     * @param appPrivateKey 应用的私钥字符串
     */
    public void setAppPrivateKey(String appPrivateKey) {
        this.appPrivateKey = appPrivateKey;
    }

    /**
     * 获取支付宝的公钥
     *
     * @return 返回支付宝的公钥字符串
     */
    public String getAlipayPublicKey() {
        return alipayPublicKey;
    }

    /**
     * 设置支付宝的公钥
     *
     * @param alipayPublicKey 支付宝的公钥字符串
     */
    public void setAlipayPublicKey(String alipayPublicKey) {
        this.alipayPublicKey = alipayPublicKey;
    }

    /**
     * 获取支付宝异步通知的URL地址
     *
     * @return 返回支付宝异步通知的URL地址字符串
     */
    public String getNotifyUrl() {
        return notifyUrl;
    }

    /**
     * 设置支付宝异步通知的URL地址
     *
     * @param notifyUrl 支付宝异步通知的URL地址字符串
     */
    public void setNotifyUrl(String notifyUrl) {
        this.notifyUrl = notifyUrl;
    }
}

AliPayController

package com.example.pay.alipay.controller;

import cn.hutool.json.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;

import com.example.pay.alipay.common.AliPayConfig;
import com.example.pay.alipay.entity.Orders;
import com.example.pay.alipay.service.OrdersService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;


@RestController
@RequestMapping("/alipay")
public class AliPayController {

    // 支付宝网关URL
    private static final String GATEWAY_URL = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
    // 返回格式
    private static final String FORMAT = "JSON";
    // 字符集
    private static final String CHARSET = "UTF-8";
    // 签名类型
    private static final String SIGN_TYPE = "RSA2";

    @Resource
    private AliPayConfig aliPayConfig;

    @Resource
    private OrdersService ordersService;

    /**
     * 用户支付接口
     *
     * @param orderNo      订单号
     * @param httpResponse 响应对象
     * @throws Exception 异常
     */
    @GetMapping("/pay")
    public void pay(String orderNo, HttpServletResponse httpResponse) throws Exception {
        // 根据订单号查询订单信息
        Orders orders = ordersService.selectByOrderNo(orderNo);
        if (orders == null) {
            return;
        }

        // 创建支付宝客户端
        AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);

        // 创建页面支付请求,并设置相关参数
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setNotifyUrl(aliPayConfig.getNotifyUrl());
        JSONObject bizContent = new JSONObject();
        bizContent.set("out_trade_no", orders.getOrderNo());
        bizContent.set("total_amount", orders.getTotal());
        bizContent.set("subject", orders.getGoodsName());
        bizContent.set("product_code", "FAST_INSTANT_TRADE_PAY");
        request.setBizContent(bizContent.toString());
        request.setReturnUrl("http://localhost:8080/orders");

        // 执行支付请求,生成表单并返回给客户端
        String form = "";
        try {
            form = alipayClient.pageExecute(request).getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        httpResponse.setContentType("text/html;charset=" + CHARSET);
        httpResponse.getWriter().write(form);
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
    }

    /**
     * 支付宝异步通知接口
     *
     * @param request 请求对象
     * @throws Exception 异常
     */
    @PostMapping("/notify")
    public void payNotify(HttpServletRequest request) throws Exception {
        // 验证交易状态
        if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
            System.out.println("=========支付宝异步回调========");

            // 构建参数map
            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                params.put(name, request.getParameter(name));
            }

            // 验证签名
            String sign = params.get("sign");
            String content = AlipaySignature.getSignCheckContentV1(params);
            boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, aliPayConfig.getAlipayPublicKey(), "UTF-8");

            if (checkSignature) {
                // 处理支付成功逻辑
                System.out.println("交易名称: " + params.get("subject"));
                System.out.println("交易状态: " + params.get("trade_status"));
                System.out.println("支付宝交易凭证号: " + params.get("trade_no"));
                System.out.println("商户订单号: " + params.get("out_trade_no"));
                System.out.println("交易金额: " + params.get("total_amount"));
                System.out.println("买家在支付宝唯一id: " + params.get("buyer_id"));
                System.out.println("买家付款时间: " + params.get("gmt_payment"));
                System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));

                // 更新订单支付状态
                String tradeNo = params.get("out_trade_no");
                String gmtPayment = params.get("gmt_payment");
                String alipayTradeNo = params.get("trade_no");

                Orders orders = ordersService.selectByOrderNo(tradeNo);
                orders.setStatus("已支付");
                orders.setPayTime(gmtPayment);
                orders.setPayNo(alipayTradeNo);
                ordersService.updateById(orders);
            }
        }
    }

}

测试

http://localhost:8081/

点击购买

查看订单

点击支付

输入账号密码

输入支付密码

付款成功

触发回调函数

更新数据库成功

商品已支付