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日

相关文章

  • 详解.vue文件中style标签的几个标识符

    当我们使用 Vue 开发前端项目时,Vue 的单文件组件 .vue 文件中允许我们在 <template> 标签中定义模板、在 <script> 标签中定义 JS 代码、在 <style> 标签中定义 CSS 样式。对于 <style> 标签,Vue 允许我们使用一些特殊的标识符来扩展它们的功能和特性。 下面是…

    Vue 2023年5月28日
    00
  • Vue 打包体积优化方案小结

    下面我来详细讲解一下“Vue 打包体积优化方案小结”的完整攻略。 1. 按需引入第三方插件 第一步,可以通过按需引入第三方插件来减小打包体积。对于一些比较大的第三方插件,我们可以使用按需引入的方式,在需要使用插件的具体页面中引入。具体实现方式可以使用 babel-plugin-import 插件来完成。 例如,在使用 Element UI 的项目中,可以通过…

    Vue 2023年5月28日
    00
  • Vue两个版本的区别和使用方法(更深层次了解)

    下面是详细讲解“Vue两个版本的区别和使用方法(更深层次了解)”的完整攻略。 概述 Vue.js是一个流行的JavaScript库,用于构建现代交互式Web界面。Vue.js具有响应式且易于理解的API和一套逐渐增长的生态系统。Vue.js可以通过任何Web服务器和通过浏览器直接访问。 Vue.js的最新版本是Vue3,但Vue2也仍然广泛使用。不同的是,V…

    Vue 2023年5月28日
    00
  • 浅谈vue实现数据监听的函数 Object.defineProperty

    Object.defineProperty 是 JavaScript 提供的一个函数,用于设置对象/类的属性。在 Vue 的实现过程中,它可以用来实现数据的监听。 什么是数据监听? 当我们在 Vue 中对一个变量进行修改时,Vue 会自动更新依赖这个变量的 View,这个过程可以称为数据监听。 Object.defineProperty的使用 Object.…

    Vue 2023年5月28日
    00
  • Vue页面骨架屏注入方法

    Vue页面骨架屏注入方法是一种性能优化的策略,可以让页面在加载数据的过程中显示出一个占位符,给用户提供更好的体验。下面是一个石乐志的攻略,包含具体的流程和示例说明。 步骤一:安装骨架屏插件 首先,你需要安装一个 Vue 骨架屏插件。我们推荐使用 vue-skeleton-webpack-plugin 或 page-skeleton-webpack-plugi…

    Vue 2023年5月28日
    00
  • vue webpack打包优化操作技巧

    下面是关于Vue Webpack打包优化的完整攻略。 为什么需要打包优化? Vue Webpack打包过程通常比较复杂,对于大型项目而言,打包过程中可能会遇到各种各样的问题。我们需要对Webpack打包进行优化,以提高项目的性能和稳定性。通常需要考虑以下几个方面: 减少打包体积 减少打包时间 提高页面加载速度 稳定可靠性 打包优化操作技巧 1. 优化load…

    Vue 2023年5月28日
    00
  • mpvue写一个CPASS小程序的示例

    下面是mpvue写一个CPASS小程序的详细攻略,包含两条示例说明: 一、先决条件 在正式开始编写小程序之前,需要安装好cpass-cli和mpvue框架,并且具有mpvue开发的基础知识。 二、创建项目及配置 2.1 创建项目 使用cpass-cli创建一个mpvue工程: cpass create demo –template mpvue 2.2 配置…

    Vue 2023年5月27日
    00
  • 仿照Element-ui实现一个简易的$message方法

    这是一个完整的攻略,步骤如下: 步骤一:创建组件 首先,我们需要创建一个Vue组件来实现这个消息框功能。 我们可以在src/components目录下创建一个messageBox.vue文件,并在其中写入以下代码: <template> <div v-show="visible" :class="[‘messa…

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