SpringBoot接入支付宝教程

码云地址:

 

前言:

今天在一个公众号看到了这样一篇文章,写的是:SpringBoot接入支付宝,看了那篇文章发现写的比较一般,本着求知的心态,今天想着接触下接入支付宝,然后写个博客~

参考博客:

  springboot接入支付宝支付-新版SDK(2020/11/03)

使用到的支付宝开发网址:

  支付宝开发助手

  支付宝开放平台-沙箱环境

  MiniU研发工作台

  ngrok:内网穿透

下面需要使用到的链接或者秘钥我都会用红色标注出来

一、环境搭建

1、应用公钥/私钥的获取

  进入到支付宝开发助手-->MiniU研发工作台,以前的话应该是让下载软件什么的获取秘钥,现在可以直接通过网页进行获取了

复制下来生成的公钥跟私钥,记到小本子上面~

SpringBoot接入支付宝教程

 2、APPID、支付宝公钥的获取

进入到支付宝开放平台-沙箱环境,复制我们刚刚生成的公钥,放在对应的框框中即可生成"支付宝公钥"

复制你的APPID

SpringBoot接入支付宝教程

 复制生成的支付宝公钥~

SpringBoot接入支付宝教程

记得复制下来支付宝提供的测试账号,下面测试要用:

SpringBoot接入支付宝教程

 

 

3、内网穿透设置

我使用的是Windows环境,这里我就下载Windows版本的ngrok,这是国外的网站有的小伙伴下载比较慢,可以使用蓝奏云下载:https://wws.lanzoui.com/izXZZsuw9wb

SpringBoot接入支付宝教程

 下载完成之后,使用命令行进入到解压后的目录,复制内网穿透的链接:注意先别把窗口关闭了。

1、进入目录:鼠标放到导航栏上面,输入cmd,即可快速进入

2、启动应用:ngrok http 端口

SpringBoot接入支付宝教程

二、代码整理

新建SpringBoot项目(只需添加SpringBootWeb依赖即可),项目结构如下:

SpringBoot接入支付宝教程

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-easysdk</artifactId>
    <version>2.1.0</version>
</dependency>

application.properties  

## 端口
server.port=8080
## 支付宝配置
# 应用ID
alipay.appId=把你的应用id填到这里
# 应用私钥
alipay.privateKey=把你的应用私钥填到这里
# 支付宝公钥,注意不是生成的应用公钥
alipay.publicKey=把你的支付宝公钥填到这里
#支付网关配置,这一项是写死的,正式环境是openapi.alipay.com
alipay.gateway=openapi.alipaydev.com
# 支付宝前台跳转地址
alipay.returnUrl=生成的外网穿透链接/return_url.html
# 支付宝后台通知地址
alipay.notifyUrl=生成的外网穿透链接/api/alipay/notify_url
# 支付宝前台手机网页支付中途取消跳转地址
alipay.errorUrl=生成的外网穿透链接/error_url.html

config/AlipayConfig.java

SpringBoot接入支付宝教程
/**
 * @author zhangzhixi
 * @version 1.0
 * @date 2021-8-19 15:02
 */
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class AlipayConfig implements ApplicationRunner {

    // 应用id
    @Value("${alipay.appId}")
    private String appId;

    // 私钥
    @Value("${alipay.privateKey}")
    private String privateKey;

    // 公钥
    @Value("${alipay.publicKey}")
    private String publicKey;

    // 支付宝网关
    @Value("${alipay.gateway}")
    private String gateway;

    // 支付成功后的接口回调地址,不是回调的友好页面,不要弄混了
    @Value("${alipay.notifyUrl}")
    private String notifyUrl;

    /**
     *  项目初始化事件
     * */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        //初始化支付宝SDK
        Factory.setOptions(getOptions());
        System.out.println("**********支付宝SDK初始化完成**********");
    }

    private Config getOptions() {
        //这里省略了一些不必要的配置,可参考文档的说明

        Config config = new Config();
        config.protocol = "https";
        config.gatewayHost = this.gateway;
        config.signType = "RSA2";

        config.appId = this.appId;

        // 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = this.privateKey;

        // 注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = this.publicKey;

        // 可设置异步通知接收服务地址(可选)
        config.notifyUrl = notifyUrl;

        return config;
    }

}
View Code

controller/AlipayController.java

SpringBoot接入支付宝教程
/**
 * @author zhangzhixi
 * @version 1.0
 * @date 2021-8-19 15:06
 */

import com.alipay.easysdk.factory.Factory;
import com.zhixi.aliyunpay.service.AlipayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/alipay")
public class AlipayController {

    private final AlipayService alipayService;

    @Autowired
    public AlipayController(AlipayService alipayService) {
        this.alipayService = alipayService;
    }

    /**
     * 支付宝电脑网页支付
     * @param subject: 订单名称
     * @param total:   金额
     * @return java.lang.String
     */
    @PostMapping("/page")
    public String page(String subject, String total) {
        subject = "测试支付";
        total = "1000";

        return alipayService.page(subject, total);
    }

    /**
     * 支付宝手机网页支付
     * @param subject: 订单名称
     * @param total:   金额
     * @return java.lang.String
     */
    @PostMapping("/wap")
    public String wap(String subject, String total) {
        subject = "测试支付";
        total = "1000";

        return alipayService.wap(subject, total);
    }

    /**
     * @param request: 请求
     * @return java.lang.String
     * @description: 支付宝异步回调
     * @date: 2020/11/3
     */
    @PostMapping("/notify_url")
    public String notify_url(HttpServletRequest request) throws Exception {

        if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
            System.out.println("=========支付宝异步回调========");

            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                params.put(name, request.getParameter(name));
                // System.out.println(name + " = " + request.getParameter(name));
            }

            // 支付宝验签
            if (Factory.Payment.Common().verifyNotify(params)) {
                // 验签通过
                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"));
            }
        }

        return "success";
    }

    /**
     * @param outTradeNo:   商家订单号
     * @param refundAmount: 退款金额(不能大于交易金额)
     * @return java.lang.String
     * @description: 支付宝退款
     * @date: 2020/11/3
     */
    @PostMapping("/refund")
    public String refund(String outTradeNo, String refundAmount) {
        return alipayService.refund(outTradeNo, refundAmount);
    }

}
View Code

service/AlipayService.java

SpringBoot接入支付宝教程
/**
 * @author zhangzhixi
 * @version 1.0
 * @date 2021-8-19 15:04
 */
public interface AlipayService {

    /**
     * @description: 支付宝电脑网页支付
     * @param subject: 订单名称
     * @param total: 金额
     * @date: 2020/11/3
     * @return java.lang.String
     */
    String page(String subject, String total);

    /**
     * @description: 支付宝手机网页支付
     * @param subject: 订单名称
     * @param total: 金额
     * @date: 2020/11/3
     * @return java.lang.String
     */
    String wap(String subject, String total);

    /**
     * @description: 支付宝退款
     * @param outTradeNo: 商家订单号
     * @param refundAmount: 退款金额(不能大于交易金额)
     * @date: 2020/11/3
     * @return java.lang.String
     */
    String refund(String outTradeNo, String refundAmount);
}
View Code

service/impl/AlipayService.java

SpringBoot接入支付宝教程
/**
 * @author zhangzhixi
 * @version 1.0
 * @date 2021-8-19 15:04
 */

import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
import com.zhixi.aliyunpay.service.AlipayService;
import com.zhixi.aliyunpay.util.OrderUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class AlipayServiceImpl implements AlipayService {

    // 支付成功后要跳转的页面
    @Value("${alipay.returnUrl}")
    private String returnUrl;

    // 支付宝前台手机网页支付中途取消跳转地址
    @Value("${alipay.errorUrl}")
    private String errorUrl;

    @Override
    public String page(String subject, String total) {

        try {
            AlipayTradePagePayResponse response = Factory.Payment
                    // 选择电脑网站
                    .Page()
                    // 调用支付方法(订单名称, 商家订单号, 金额, 成功页面)
                    .pay(subject, OrderUtil.getOrderNo(), total, returnUrl);

            return response.body;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public String wap(String subject, String total) {

        try {
            AlipayTradeWapPayResponse response = Factory.Payment
                    //选择手机网站
                    .Wap()
                    // 调用支付方法(订单名称, 商家订单号, 金额, 中途退出页面, 成功页面)
                    .pay(subject, OrderUtil.getOrderNo(), total, errorUrl, returnUrl);

            return response.body;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public String refund(String outTradeNo, String refundAmount) {
        try {
            AlipayTradeRefundResponse response = Factory.Payment
                    .Common()
                    // 调用交易退款(商家订单号, 退款金额)
                    .refund(outTradeNo, refundAmount);

            if (response.getMsg().equals("Success")) {
                return "退款成功";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "退款失败";
    }
}
View Code

util/OrderUtil.java

SpringBoot接入支付宝教程
/**
 * @author zhangzhixi
 * @version 1.0
 * @date 2021-8-19 15:03
 */

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

public class OrderUtil {

    /**
     * 根据时间戳生成订单号
     */
    public static String getOrderNo() {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
        LocalDateTime localDateTime = Instant.ofEpochMilli(System.currentTimeMillis()).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
        return df.format(localDateTime);
    }
}
View Code

html页面:

index.html

SpringBoot接入支付宝教程
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试支付</title>
</head>
<body>
<div>
    <h2>测试支付</h2>
    <form enctype="multipart/form-data" action="/api/alipay/page" method="post">
        <button type="submit">电脑确认支付</button>
    </form>
    <form enctype="multipart/form-data" action="/api/alipay/wap" method="post">
        <button type="submit">手机确认支付</button>
    </form>
</div>
</body>
</html>
View Code

return_url.html

SpringBoot接入支付宝教程
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>支付成功</title>
</head>
<body>
<div>
    <h2>支付成功</h2>

    <a href="http://78ff7c27cdb6.ngrok.io/return_url.html">主页</a>
    <a href="index.html">主页</a>
</div>
</body>
</html>
View Code

error_url.html

SpringBoot接入支付宝教程
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>支付失败</title>
</head>
<body>
<div>
    <h2>支付失败</h2>
</div>
</body>
</html>
View Code

三、测试

电脑测试访问:

SpringBoot接入支付宝教程

 

 SpringBoot接入支付宝教程

手机测试访问:

 SpringBoot接入支付宝教程

 

SpringBoot接入支付宝教程

 

 SpringBoot接入支付宝教程

上一篇:支付宝支付(三):周期扣款实现注意细节


下一篇:Profinet协议解析-过程数据