SpringBoot集成支付宝沙箱支付的实现示例

yizhihongxing

下面是详细讲解“SpringBoot集成支付宝沙箱支付的实现示例”的完整攻略:

一、前置准备

  1. 注册并开通阿里云账号。
  2. 创建一个支付宝沙箱应用,获取支付宝沙箱应用 ID 和秘钥。
  3. 了解支付宝接口文档和SDK相关内容。
  4. 选择合适的开发语言和开发框架。本教程采用 SpringBoot 框架。

二、添加依赖

在 pom.xml 文件中添加 Alibaba maven repository 中央仓库和支付宝沙箱支付的 SDK 依赖。

<repositories>
    <repository>
        <id>alibaba-nexus</id>
        <name>Alibaba Nexus Repository</name>
        <url>https://maven.aliyun.com/nexus/content/groups/public/</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java-all</artifactId>
        <version>3.3.0.RELEASE</version>
    </dependency>
</dependencies>

三、配置支付宝客户端

在 SpringBoot 的配置文件中,添加以下关于支付宝客户端相关的配置信息。

# 支付宝沙箱环境公共参数
alipay.sandbox.gateway.url=https://openapi.alipaydev.com/gateway.do
alipay.sandbox.app.id=沙箱应用 ID
alipay.sandbox.private.key=沙箱应用私钥
alipay.sandbox.public.key=沙箱应用公钥
alipay.sandbox.notify.url=回调 URL

四、实现支付

在代码实现支付前,需要为客户端读取支付宝沙箱公钥、自己的私钥、支付宝公钥。为了完成这些操作,可以将公钥和私钥放在 resources 目录下,使用密钥工具类 IORUtils 进行加载。

1、IOUtils 工具类

public class IOUtils {

    /**
     * 获取文件内容
     *
     * @param resource 资源信息
     * @return 文件内容
     */
    public static String getResource(String resource) {
        try (InputStream inputStream = new ClassPathResource(resource).getInputStream();
             BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) {
            byte[] bytes = new byte[bufferedInputStream.available()];
            bufferedInputStream.read(bytes);
            return new String(bytes);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取私钥
     *
     * @param resource 私钥文件路径
     * @return 私钥
     */
    public static String getPrivateKey(String resource) {
        try {
            return getResource(resource);
        } catch (Exception e) {
            throw new RuntimeException("获取私钥失败!", e);
        }
    }

    /**
     * 获取公钥证书
     *
     * @param resource 公钥证书路径
     * @return 公钥证书
     */
    public static String getCertPublicKey(String resource) {
        try {
            InputStream inputStream = new ClassPathResource(resource).getInputStream();
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            X509Certificate x509Certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
            inputStream.close();
            return Base64Utils.encodeToString(x509Certificate.getPublicKey().getEncoded());
        } catch (Exception e) {
            throw new RuntimeException("获取公钥证书失败!", e);
        }
    }

    /**
     * 获取PKCS8格式私钥对象
     *
     * @param resource 私钥文件路径
     * @return AlibabaRSA2PrivateKey PKCS8格式私钥对象
     */
    public static AlibabaRSA2PrivateKey getCFGPrivateKey(String resource) {
        try {
            byte[] privateKeyBytes = getPemPrivateKey(readContent(resource));
            if (privateKeyBytes == null) {
                throw new RuntimeException("私钥内容为空,请检查私钥文件!");
            }
            RSAPrivateKeyStructure asn1PrivKey = new RSAPrivateKeyStructure((ASN1Sequence) ASN1Sequence.fromByteArray(privateKeyBytes));
            if (asn1PrivKey == null) {
                throw new RuntimeException("ASN1解析私钥失败,请检查私钥文件!");
            }
            RSAPrivateCrtKey rsaKey = new RSAPrivateCrtKey(
                    asn1PrivKey.getModulus(),
                    asn1PrivKey.getPublicExponent(),
                    asn1PrivKey.getPrivateExponent(),
                    asn1PrivKey.getPrime1(),
                    asn1PrivKey.getPrime2(),
                    asn1PrivKey.getExponent1(),
                    asn1PrivKey.getExponent2(),
                    asn1PrivKey.getCoefficient());
            return new AlibabaRSA2PrivateKey(rsaKey.getEncoded());
        } catch (Exception e) {
            throw new RuntimeException("获取PKCS8格式私钥对象失败!", e);
        }
    }

    /**
     * 获取pem格式的报文
     *
     * @param pemContent pem格式的私钥报文
     * @return byte[] 二进制格式的报文
     */
    public static byte[] getPemPrivateKey(String pemContent) {
        if (pemContent == null) {
            return null;
        }
        String[] strings = pemContent.split("\n");
        StringBuilder pkcs8Lines = new StringBuilder();
        for (String str : strings) {
            if (!str.startsWith("--")) {
                pkcs8Lines.append(str);
            }
        }
        return Base64Utils.decodeFromString(pkcs8Lines.toString());
    }

    /**
     * 读取文件内容
     *
     * @param filePath
     * @return 文件内容
     * @throws Exception
     */
    public static String readContent(String filePath) throws Exception {
        String res = null;
        File file = new File(filePath);
        if (file != null && file.exists()) {
            StringBuilder stringBuilder = new StringBuilder();
            try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line).append("\n");
                }
            }
            res = stringBuilder.toString();
        } else {
            throw new RuntimeException("文件不存在!");
        }
        return res;
    }
}

2、支付宝支付服务启动类

在 SpringBootApplication 启动类下创建 PayService 类,创建拼装支付请求的方法aliPay,订单查询方法queryOrder,退款申请方法refundOrder等。这里只展示aliPay函数的实现。

@Service
@Slf4j
public class PayService {

    @Value("${alipay.sandbox.app.id}")
    private String appId;

    @Value("${alipay.sandbox.private.key}")
    private String privateKey;

    @Value("${alipay.sandbox.public.key}")
    private String publicKey;

    @Value("${alipay.sandbox.gateway.url}")
    private String gatewayUrl;

    @Value("${alipay.sandbox.notify.url}")
    private String notifyUrl;

    @Autowired
    private OrderService orderService;

    /**
     * 支付宝支付
     *
     * @param order
     * @return
     */
    public String aliPay(Order order) {
        try {
            log.info(">>> 支付宝支付开始:{}", order);
            // 1、构建AlipayClient实例
            AlipayClient alipayClient = buildAlipayClient();
            // 2、构建AlipayTradeAppPayRequest实例
            AlipayTradeAppPayRequest payRequest = buildTradePayRequest(order);
            // 3、调用SDK生成支付串
            AlipayTradeAppPayResponse payResponse = alipayClient.sdkExecute(payRequest);
            // 4、处理支付结果
            String resultString = payResponse.getBody();
            log.info(">>> 支付宝支付结果:{}", resultString);
            return resultString;
        } catch (AlipayApiException e) {
            throw new RuntimeException("支付宝支付失败!", e);
        }
    }

    /**
     * 构建AlipayClient实例
     *
     * @return
     */
    private AlipayClient buildAlipayClient() {
        return new DefaultAlipayClient(
                gatewayUrl, appId, privateKey, "json", "utf-8", publicKey, "RSA2");
    }

    /**
     * 构建AlipayTradeAppPayRequest实例
     *
     * @param order
     * @return
     */
    private AlipayTradeAppPayRequest buildTradePayRequest(Order order) {
        // 1、构建生成支付串请求对象
        AlipayTradeAppPayRequest payRequest = new AlipayTradeAppPayRequest();
        payRequest.setNotifyUrl(notifyUrl);
        payRequest.setBizContent(buildTradePayBizContent(order));
        return payRequest;
    }

    /**
     * 构建生成支付串请求BizContent
     *
     * @param order
     * @return
     */
    private String buildTradePayBizContent(Order order) {
        AlipayTradeAppPayModel payModel = new AlipayTradeAppPayModel();
        payModel.setSubject("测试商品");
        payModel.setOutTradeNo(order.getOrderNo());
        payModel.setTotalAmount(order.getOrderAmount().toString());
        payModel.setBody("测试商品描述");
        payModel.setProductCode("QUICK_MSECURITY_PAY");
        return JSON.toJSONString(payModel);
    }

}

五、测试支付

添加测试类进行支付调用,并在阿里云管理后台查看支付宝账单确认支付已经成功。

@SpringBootTest
public class PayServiceTest {

    @Autowired
    private PayService payService;

    @Test
    public void testAliPay() {
        Order order = new Order();
        order.setOrderNo(UUID.randomUUID().toString());
        order.setOrderAmount(new BigDecimal("0.01"));

        String result = payService.aliPay(order);
        log.info(">>> 支付结果:{}", result);
    }

}

六、遇到问题处理

如果支付请求失败,可以通过以下方式查看日志,找到错误原因。

# 打开支付宝验签日志开关
log4j.logger.com.alipay.api=com.alipay.api.log4j.AlipayLog4jLogger

# 查看支付宝验签日志
tail -f /path/to/your/tomcat/logs/catalina.out|egrep "com.alipay.api.log"

# 自定义消息处理类
@ControllerAdvice
public class AlipayExceptionHandler {

    @ExceptionHandler(AlipayApiException.class)
    public void handleAlipayException(AlipayApiException e) {
        // 处理支付宝请求异常
        log.error("支付宝请求异常!", e);
    }

    @ExceptionHandler(AopInvocationException.class)
    public void handleInvocationException(AopInvocationException e) {
        // 处理支付宝请求接口调用异常
        log.error("支付宝请求接口调用异常!", e);
    }

}

以上就是SpringBoot集成支付宝沙箱支付的实现示例攻略的完整解释。另外,还可以通过“SpringBoot集成微信支付”的方式实现微信支付功能。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot集成支付宝沙箱支付的实现示例 - Python技术站

(0)
上一篇 2023年5月28日
下一篇 2023年5月28日

相关文章

  • Ant Design Vue日期组件的时间限制方式

    Ant Design Vue 是 Ant Design 在 Vue 中的实现,是一套基于 Vue 实现的高质量 UI 组件库,拥有丰富的组件和良好的设计风格,深受前端工程师的喜爱。 Ant Design Vue 提供了日期组件,我们可以通过设置时间限制方式来限定用户选择日期的范围,例如限制用户只能选择今天及今天之后的日期,或者只能选择本月份的日期等等。 下面…

    Vue 2023年5月29日
    00
  • vue3+vite自定义封装vue组件发布到npm包的全过程

    下面我将详细讲解“vue3+vite自定义封装vue组件发布到npm包的全过程”,并且提供两个示例以供参考。 1. 准备工作 首先,我们需要进行一些准备工作,包括:- 安装node- 创建自己的npm账号 2. 创建项目 我们可以使用vue-cli来创建一个基于vue3和vite的vue项目,命令如下: $ npm install -g @vue/cli $…

    Vue 2023年5月28日
    00
  • TDesign在vitest的实践示例详解

    TDesign在vitest的实践示例详解 简介 TDesign是一种基于消息传递的测试方法,能够快速而准确地发现系统中的错误和缺陷。本文将结合vitest实践,详细讲解如何使用TDesign进行测试,包括两条实例说明。 TDesign测试过程 步骤1:分析系统 在进行TDesign测试之前,需要对系统进行分析,了解其功能模块、交互方式和可能存在的问题。针对…

    Vue 2023年5月28日
    00
  • vue中destroyed方法的使用说明

    当一个组件(component)被销毁时,Vue 会自动调用该组件的生命周期钩子函数 destroyed。destroyed 生命周期是在组件的程序和网络活动结束后被调用的,并且在其它生命周期钩子函数后执行。这意味着 Vue 实例及其数据观察者已被解绑定,所有的事件监听器和子组件已被移除,所有的计时器和异步任务已被清理。下面就详细讲解 destroyed 方…

    Vue 2023年5月28日
    00
  • vue-router 4使用实例详解

    下面是“vue-router 4使用实例详解”的完整攻略。 一、前置知识: Vue.js框架的基础使用,Vue组件、生命周期等基础知识。 Vue-Router的基础使用方法,可以参考Vue-Router官网。 对ES6的基础了解。 二、vue-router 4使用实例 1. 安装 vue-router 使用npm安装Vue Router: npm insta…

    Vue 2023年5月28日
    00
  • Vue.js学习笔记之常用模板语法详解

    当谈到Vue.js时,模板语法是一个不可或缺的部分。在本篇文章中,我们将深入探讨Vue.js模板语法中的常用内容。 插值 插值是Vue.js通常用于在模板中显示数据的方法。我们可以使用双花括号来绑定变量,并将变量的值插入到模板中。 <div> {{ message }} </div> 当一个组件被实例化时,Vue.js会从组件实例中找…

    Vue 2023年5月27日
    00
  • Vue项目中常用的工具函数总结

    下面是“Vue项目中常用的工具函数总结”的攻略: Vue项目中常用的工具函数总结 什么是工具函数 在Vue项目中,我们会经常用到一些通用的、可重复使用的代码片段,这些代码片段被封装成了函数,我们称之为工具函数。通过使用这些函数,我们可以简化代码、提高开发效率、减少出错几率。 常用的工具函数 1.深度复制对象 在Vue项目中,我们经常需要将对象进行深度复制(也…

    Vue 2023年5月27日
    00
  • Vue3基于countUp.js实现数字滚动的插件

    Vue3基于countUp.js实现数字滚动的插件就是一个可以在Vue3中方便地实现数字滚动效果的插件。下面将介绍实现该插件的完整攻略: 确认需求和安装countUp.js 首先需要明确需求,确认需要实现数字滚动的具体元素和动画效果等。然后需要安装countUp.js插件,在Vue3项目中引入countUp.js的CDN链接或安装countUp.js的NPM…

    Vue 2023年5月27日
    00
合作推广
合作推广
分享本页
返回顶部