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

下面是详细讲解“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日

相关文章

  • 使用SpringBoot+EasyExcel+Vue实现excel表格的导入和导出详解

    使用SpringBoot+EasyExcel+Vue实现excel表格的导入和导出,主要步骤如下: 1.引入EasyExcel依赖 在pom.xml中引入EasyExcel依赖: <!– 引入EasyExcel –> <dependency> <groupId>com.alibaba</groupId> &…

    Vue 2023年5月28日
    00
  • 解决vue vite启动项目报错ERROR: Unexpected “\x88“ in JSON 的问题

    如果你在使用vue vite启动项目时遇到了JSON解析异常,有可能是因为项目依赖项中某个文件含有非utf-8编码的字符而导致的。 解决此问题的方法如下: 在项目的根路径下创建vite.config.js文件,如果该文件已存在则直接修改它,如果没有请参照以下示例内容创建该文件。 const { createServer } = require("v…

    Vue 2023年5月28日
    00
  • vue3中组件传值的多种方法总结

    下面是详细讲解“vue3中组件传值的多种方法总结”的完整攻略。 1. 组件传值概述 在vue3中,组件传值是非常常见的场景,通常包括父组件向子组件传值和子组件向父组件传值两种类型。 在vue3中,组件传值的方式主要包括如下几种: 父组件向子组件传值(prop) 子组件向父组件传值($emit) 祖先组件向后代组件传值(provide/inject) 跨级组件…

    Vue 2023年5月29日
    00
  • SpringBoot项目实现短信发送接口开发的实践

    下面是关于“SpringBoot项目实现短信发送接口开发的实践”的攻略: 1. 场景与背景 在很多应用场景下,我们需要向用户发送短信信息,比如验证码、通知等。本攻略将介绍如何使用 SpringBoot 来快速实现短信发送接口的开发。 2. 技术选型 SpringBoot 阿里云SMS服务 3. 实践步骤 3.1. 创建SpringBoot项目 首先,我们需要…

    Vue 2023年5月28日
    00
  • vue父组件与子组件之间的数据交互方式详解

    Vue父组件与子组件之间的数据交互方式详解 介绍 Vue是一款流行的前端框架,它提供了一种组件化的开发方式来构建Web应用程序。Vue的组件化开发方式具有很高的灵活性和可重用性,但是在组件化开发中,如何进行组件之间的数据交互是一件非常重要的事情。本文将介绍Vue父组件与子组件之间的数据交互方式。 父组件向子组件传递数据 父组件向子组件传递数据的方式有两种:p…

    Vue 2023年5月28日
    00
  • vue项目配置eslint保存自动格式化问题

    下面是关于Vue项目配置ESLint保存自动格式化的完整攻略: 安装相关插件 首先,需要在Vue项目中安装eslint和eslint-plugin-prettier两个插件,可以使用npm安装,命令如下: npm install eslint eslint-plugin-prettier –save-dev 配置.eslintrc.js文件 在Vue项目的…

    Vue 2023年5月28日
    00
  • Vue页面骨架屏的实现方法

    当用户访问页面时,如果页面加载速度很慢,很容易造成用户的不耐烦和流失,导致用户体验降低。为了避免这种情况的发生,可以使用 skeleton screen(骨架屏)技术,即在页面加载过程中,先呈现出一个简单的页面骨架,等到页面加载完成后,再把数据填充到页面中,使得用户不会感到过长的等待时间。 下面是实现Vue页面骨架屏的具体步骤: 1. 安装 vue-cont…

    Vue 2023年5月27日
    00
  • SpringBoot和Vue.js实现的前后端分离的用户权限管理系统

    下面我将给你详细讲解实现“SpringBoot和Vue.js实现的前后端分离的用户权限管理系统”的完整攻略。 概述 要实现前后端分离的用户权限管理系统,首先需要涉及到的技术栈包括SpringBoot、Vue.js、Spring Security、JWT(token)等,其中Spring Security是前后端分离中实现鉴权的关键技术,JWT是用来生成权限认…

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