手把手教你接入支付宝开放平台

本文来详细描述一下支付宝开放平台接口方面的接入。

看完本文,你将学会:

✅ 如何在支付宝开放平台启用证书模式  
✅ 如何生成 CSR 文件、配置四个关键的证书与密钥
✅ 这四个关键的证书与密钥是干嘛用的
✅ 如何在 Java 项目中优雅地接入支付宝 SDK(支持证书模式)  

控制台首页 - 开放平台

关于开放平台账号注册、企业认证、应用创建等基础流程,本篇文章不再赘述,大家按照支付宝开放平台的页面指引操作即可。重点将放在接口接入与证书模式的实战流程。

应用创建成功后,我们即可开始准备接入开放平台的功能了。比如收款、商户转账等。

一、获取密钥和证书

我们首先需要获取一下密钥和证书。进入到你的应用页:登录 - 支付宝

选择开发设置、点击配置接口加签方式。

1,设置加签的方式。

如果你仅仅想做扫码支付、H5 支付等简单收款业务,可以使用密钥模式。但涉及到更高权限接口(如商户代扣、企业付款到支付宝账户等),则必须使用证书模式,因为证书能更安全地识别应用身份。

2,生成CSR文件。

按指定步骤,下载密钥工具,生成密钥文件。密钥工具下载 - 支付宝文档中心

3,上传

打开生成的文件位置,会有三个文件:1,CSR文件 2,你的应用公钥 3,应用私钥

把这个CSR上传上去,验证一下手机号就可以了。

上传完之后,生成的三个文件,留下私钥就行了。

它其实就是一串很长的字符串,所以你也可以直接复制下来放到项目中配置好。

4,下载

上传 CSR 后,支付宝会返回三个证书。这三个证书 + 上一步留下的应用私钥 四个文件,就构成了整个支付宝证书模式接入的安全凭证体系。只要这四个文件齐全、且未过期,就能在后续请求支付宝开放平台接口时完成签名、身份验证、数据验签等操作。

总结一下申请流程:

    二、四个关键文件详解

    对于初学者来说,这四个文件可能不知道是干嘛用的:

    文件名作用
    应用私钥(private_key.txt)对请求参数进行签名
    应用公钥证书(appCertPublicKey.crt)提供身份识别,带上它的序列号
    支付宝公钥证书(alipayCertPublicKey.crt)验签支付宝响应的签名
    支付宝根证书(alipayRootCert.crt)验证支付宝证书链合法性

     用一个流程图来解释一下:

    支付宝需要知道“你是你”(用证书SN识别你是谁)+“这真的是你发的”(签名),你也需要知道“支付宝是支付宝”(验签+信任链),这些就靠这4个文件配合完成。当然,支付宝的SDK都替你做好了。因此你在接入时就只要配置好即可。

    后面就开始用Springboot接入:

    三、Java接入支付宝API

    接下来就能使用支付宝的API了。

    支付宝提供了非常完善的SDK。我们在使用的时候可以很方便的接入

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

    我这里也提供了一部分我封装的代码:

    配置文件:

    # 正式环境
    alipay:
      #  # 应用的APPID
      appId: 202100...
      # 应用私钥,用于签名
      privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgE...
      # 接口网关
      gatewayUrl: https://openapi.alipay.com/gateway.do
      # 支付回调接口地址
      notifyUrl: https://kk1084g...

      # 密钥模式
      # 支付宝公钥,用于验签 (和选一个就行了)
      alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
      aesKey: F4aauuC...

      # 证书模式 直接写Resource包下的路径
      # 应用公钥证书 表示你的应用公钥身份
      appCertPath: alipay/cert/appCertPublicKey_2021005157628684.crt
      # 支付宝公钥证书    用来验证支付宝返回数据的签名
      alipayCertPath: alipay/cert/alipayCertPublicKey_RSA2.crt
      # 支付宝根证书 用于验证支付宝证书链是否可信
      alipayRootCertPath: alipay/cert/alipayRootCert.crt

    但是支付宝SDK都是读取PATH,是没法直接读到Resource的。这里教大家一个好办法:

     AlipayConfig

    import com.alipay.api.AlipayApiException;
    import com.alipay.api.AlipayClient;
    import com.alipay.api.AlipayConstants;
    import com.alipay.api.DefaultAlipayClient;
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.ResourceLoader;
    
    import javax.annotation.Resource;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.nio.file.Files;
    
    @Configuration
    @ConfigurationProperties("alipay")
    @Data
    public class AlipayConfig {
    
        // 应用的APPID (沙箱也有个自己的)
        private String appId;
    
        // 应用私钥
        private String privateKey;
    
        // 支付宝公钥
        private String alipayPublicKey;
    
        // 支付宝的网关地址 有固定值
        private String gatewayUrl;
    
        // 接口内容加密
        private String aesKey;
    
        // 回调地址
        private String notifyUrl;
    
        /**
         * 应用公钥证书 这三个证书 都是Resource路径的,然后在 alipayClient()方法中, 会被转换成临时文件的路径
         */
        private String appCertPath;
    
        /**
         * 支付宝公钥证书
         */
        private String alipayCertPath;
    
        /**
         * 支付宝根证书
         */
        private String alipayRootCertPath;
    
        @Resource
        private ResourceLoader resourceLoader;
    
        /**
         * 证书模式
         */
        @Bean
        public AlipayClient alipayClient() throws Exception {
    
            // 从 classpath 读取资源并转成临时文件
            appCertPath = copyClasspathToTempFile(appCertPath);
            alipayCertPath = copyClasspathToTempFile(alipayCertPath);
            alipayRootCertPath = copyClasspathToTempFile(alipayRootCertPath);
    
            com.alipay.api.AlipayConfig alipayConfig = new com.alipay.api.AlipayConfig();
            //设置网关地址
            alipayConfig.setServerUrl(gatewayUrl);
            //设置应用APPID
            alipayConfig.setAppId(appId);
            //设置应用私钥
            alipayConfig.setPrivateKey(privateKey);
            //设置应用公钥证书路径
            alipayConfig.setAppCertPath(appCertPath);
            //设置支付宝公钥证书路径
            alipayConfig.setAlipayPublicCertPath(alipayCertPath);
            //设置支付宝根证书路径
            alipayConfig.setRootCertPath(alipayRootCertPath);
            //设置请求格式,固定值json
            alipayConfig.setFormat("json");
            //设置字符集
            alipayConfig.setCharset(AlipayConstants.CHARSET_UTF8);
            //设置签名类型
            alipayConfig.setSignType(AlipayConstants.SIGN_TYPE_RSA2);
    
            //接口内容加密
            alipayConfig.setEncryptKey(aesKey); // AES 密钥(开放平台配置的 32 位字符串)
            alipayConfig.setEncryptType("AES"); // 加密类型,必须明确设置为 AES
    
            //构造client
            return new DefaultAlipayClient(alipayConfig);
        }
    
        /**
         * 把 classpath 下的资源文件复制到临时目录,返回临时文件路径
         */
        private String copyClasspathToTempFile(String classpathLocation) throws Exception {
            org.springframework.core.io.Resource resource = resourceLoader.getResource("classpath:" + classpathLocation);
    
    //        ClassPathResource resource = new ClassPathResource(classpathLocation);
            File tempFile = Files.createTempFile("alipay-cert-", ".crt").toFile();
            tempFile.deleteOnExit(); // JVM退出时自动删除
    
            try (InputStream in = resource.getInputStream(); FileOutputStream out = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[4096];
                int len;
                while ((len = in.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
            }
    
            return tempFile.getAbsolutePath();
        }
    }
    

    直接读取文件,生成新文件Path就好了。

    现在就可以开始调用支付宝接口了。调用非常简单,我们只要打开文档复制它的示例即可。

    我们以创建订单为例:小程序文档 - 支付宝文档中心

    每个接口的业务参数都有选填、必填项,然后具体的又有相应的格式、长度要求等等,具体看文档。右侧是示例。我们直接CV示例。我们的配置类已经配置了Bean。因此不需要像文档中这样去new出来,直接注入即可。

        @Resource
        private AlipayClient alipayClient;
    
        @PostMapping("/create")
        @ApiOperation("app支付下单")
        public Response<Map<String, String>> createPayOrder(@RequestBody @Validated TransferDTO dto) throws AlipayApiException {
            // 构造请求参数以调用接口
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
    
            // 设置商户订单号
            model.setOutTradeNo("70501111111S001111119");
    
            // 设置订单总金额
            model.setTotalAmount("9.00");
    
            // 设置订单标题
            model.setSubject("大乐透");
    
            // 设置产品码
            model.setProductCode("QUICK_MSECURITY_PAY");
    
            // 设置订单绝对超时时间
            model.setTimeExpire("2016-12-31 10:05:00");
    
            // 设置签约参数
            SignParams agreementSignParams = new SignParams();
            AccessParams accessParams = new AccessParams();
            accessParams.setChannel("ALIPAYAPP");
            agreementSignParams.setAccessParams(accessParams);
            PeriodRuleParams periodRuleParams = new PeriodRuleParams();
            periodRuleParams.setPeriod(3L);
            periodRuleParams.setTotalAmount("600");
            periodRuleParams.setExecuteTime("2019-01-23");
            periodRuleParams.setSingleAmount("10.99");
            periodRuleParams.setTotalPayments(12L);
            periodRuleParams.setPeriodType("DAY");
            agreementSignParams.setPeriodRuleParams(periodRuleParams);
            agreementSignParams.setSignNotifyUrl("http://www.merchant.com/receiveSignNotify");
            agreementSignParams.setExternalLogonId("13888888888");
            agreementSignParams.setPersonalProductCode("CYCLE_PAY_AUTH_P");
            agreementSignParams.setExternalAgreementNo("test20190701");
            agreementSignParams.setProductCode("GENERAL_WITHHOLDING");
            agreementSignParams.setSignScene("INDUSTRY|DIGITAL_MEDIA");
            agreementSignParams.setEffectTime("600");
            model.setAgreementSignParams(agreementSignParams);
    
            request.setBizModel(model);
            // 第三方代调用模式下请设置app_auth_token
            // request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->");
    
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            String orderStr = response.getBody();
            System.out.println(orderStr);
    
            if (response.isSuccess()) {
                System.out.println("调用成功");
            } else {
                System.out.println("调用失败");
                // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
                // String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
                // System.out.println(diagnosisUrl);
            }
        }
    

    注:我们使用的是证书模式,因此Client应该要调用certificateExecute(request);

    另外,有个方便的地方就是,它的Request格式都是这样的:API的URL改驼峰+request和model。非常方便。为支付宝SDK维护人员点赞。

    # alipay.trade.app.pay(app支付接口2.0)
    AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
    AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
    
    # alipay.trade.pay(统一收单交易支付接口)
    AlipayTradePayRequest request = new AlipayTradePayRequest();
    AlipayTradePayModel model = new AlipayTradePayModel();

    当然,我也封装了一些我自己业务上的调用的代码,大家可自取:

    package com.za.base.service.alipay;
    
    import com.alipay.api.AlipayApiException;
    import com.alipay.api.response.AlipayFundTransUniTransferResponse;
    import com.alipay.api.response.AlipayTradeQueryResponse;
    import com.alipay.api.response.AlipayTradeRefundResponse;
    import com.za.base.domain.enums.AliPayOrderTypeEnum;
    
    import java.math.BigDecimal;
    import java.util.Map;
    
    /**
     * @author 卓东东
     * @description: TODO
     * @since 2025/6/6
     */
    public interface AliPayService {
    
        /**
         * 根据业务生成一个32位的唯一的商户订单号
         * 格式: 业务缩写-时间戳(秒)-UUID
         *
         * @param aliPayOrderTypeEnum 业务类型枚举
         * @return 32位数唯一订单号字符串
         */
        String createBizOrderNo(AliPayOrderTypeEnum aliPayOrderTypeEnum);
    
        /**
         * 根据业务生成一个指定位数的唯一的商户订单号
         * 格式: 业务缩写-时间戳(秒)-UUID,长度不小于业务缩写+时间戳+2个分隔符长度
         *
         * @param aliPayOrderTypeEnum 业务类型枚举
         * @param length              目标订单号总长度
         * @return 指定位数唯一订单号字符串
         */
        String createBizOrderNo(AliPayOrderTypeEnum aliPayOrderTypeEnum, Integer length);
    
        /**
         * 向支付宝用户发起转账请求(通过 userId 或 登录账号)
         * <p>
         * 此接口用于向用户发起转账,支持以下收款人身份类型:
         * <ul>
         *     <li>ALIPAY_USER_ID:支付宝的用户唯一 ID(UID)</li>
         *     <li>ALIPAY_LOGON_ID:支付宝登录号,如邮箱、手机号</li>
         *     <li>ALIPAY_OPEN_ID:支付宝小程序的 openId(不常用)</li>
         *     <li>EXPRESS_DC_STFA:对公快捷银行卡(企业付款场景)</li>
         *     <li>BANKCARD_ACCOUNT:银行卡账号(需开通相关权限)</li>
         * </ul>
         *
         * <strong>注意事项:</strong>
         * <ul>
         *     <li>当 identityType 为 <code>ALIPAY_LOGON_ID</code> 时,必须提供 <code>realName</code> 参数用于实名认证校验,否则支付宝会报错。</li>
         *     <li>本接口为同步调用,返回的是支付宝的第一响应结果。</li>
         *     <li>转账金额必须为字符串类型,单位为元,支持两位小数。</li>
         *     <li>建议调用方确保 <code>bizNo</code> 全局唯一,避免重复转账。</li>
         * </ul>
         * <p>
         * 官方接口文档:
         * https://opendocs.alipay.com/open/62987723_alipay.fund.trans.uni.transfer
         *
         * @param bizNo        外部业务订单号(商户自定义,保持唯一 也就是我们自己的订单号)
         * @param amount       转账金额,单位为元(格式示例:"100.00"),必须为字符串,保留两位小数
         * @param identity     收款人标识,如支付宝UID、手机号或邮箱等。当 identity_type=ALIPAY_USER_ID 时,填写支付宝用户 UID,ALIPAY_LOGON_ID 时,填写支付宝登录号。
         * @param identityType 收款人标识类型,可选值如:ALIPAY_USER_ID、ALIPAY_LOGON_ID 等
         * @param realName     收款人真实姓名(当 identityType 为 ALIPAY_LOGON_ID 时必填,其它类型可为空)
         * @return 支付宝转账响应结果 {@link AlipayFundTransUniTransferResponse}
         * @throws AlipayApiException 支付宝SDK异常,建议调用方处理异常逻辑,如重试或记录失败原因
         */
        AlipayFundTransUniTransferResponse transferToUser(String bizNo, String amount, String identity, String identityType, String realName) throws AlipayApiException;
    
        /**
         * 查询支付宝订单支付状态(统一收单交易查询接口:alipay.trade.query)。
         * <p>
         * 可用于查询支付结果是否成功,适用于业务回调未收到、支付结果不确定等场景。
         *
         * @param outTradeNo 商户订单号(即创建订单时传给支付宝的 out_trade_no)
         * @return 支付宝返回的交易查询响应对象 {@link AlipayTradeQueryResponse} 注意,这里AlipayTradeQueryResponse中的success是http请求的success,不代表支付成功! 具体要看trade_status
         * @throws AlipayApiException 如果请求支付宝网关发生异常(网络错误、签名错误、配置问题等)
         */
        AlipayTradeQueryResponse queryPayOrder(String outTradeNo) throws AlipayApiException;
    
        /**
         * 统一收单交易退款接口正是支付宝的标准退款接口
         * 适用于绝大多数场景下的支付订单退款,比如: 当面付(扫码) 手机网站支付(H5) APP 支付 电脑网站支付 小程序支付(间接通过服务商接入
         * 官方API文档: https://opendocs.alipay.com/open/de34d4fa_alipay.trade.refund?scene=common&pathHash=46ea3fea
         *
         * @param outTradeNo   外部业务订单号(商户自定义,保持唯一 也就是我们自己的订单号)
         * @param amount       退款金额 该金额不能大于订单金额,单位为元,支持两位小数。
         *                     注:如果正向交易使用了营销,该退款金额包含营销金额,支付宝会按业务规则分配营销和买家自有资金分别退多少,默认优先退买家的自有资金。
         * @param refundReason 退款原因 例:正常退款
         * @return 退款结果
         */
        AlipayTradeRefundResponse refund(String outTradeNo, BigDecimal amount, String refundReason) throws AlipayApiException;
    
        /**
         * 跳转支付宝人脸核身初始化 + 跳转支付宝人脸核身开始认证
         * 官方API文档: https://opendocs.alipay.com/open/b3788c04_datadigital.fincloud.generalsaas.face.certify.verify?scene=common&pathHash=8a439a37
         *
         * @param name      真实姓名
         * @param idCard    身份证
         * @param returnUrl 认证成功后需要跳转的地址,业务页面;若无跳转地址可填空字符""
         */
        void appRealNameInit(String name, String idCard, String returnUrl);
    
        /**
         * 验签接口
         *
         * @param params 支付宝传过来的参数
         * @return 是否验签通过 通过为true
         */
        boolean signVerified(Map<String, String> params) throws AlipayApiException;
    
        //    String appPay(String outTradeNo, BigDecimal amount, String subject);
    //    AlipayTradeQueryResponse query(String outTradeNo);
    }
    
    package com.za.base.domain.enums;
    
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    
    import java.util.Arrays;
    import java.util.Map;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    /**
     * 支付宝-支付订单类型枚举类
     */
    @Getter
    @AllArgsConstructor
    public enum AliPayOrderTypeEnum {
        APP_PAY("appPay", "APP支付下单"),
    
        FACE_CERTIFY("faceCertify", "刷脸认证"),
    
        WEB_PAY("webPay", "网页支付下单"),
    
        WAP_PAY("wapPay", "手机网页支付"),
    
        QR_CODE_PAY("qrCodePay", "扫码支付"),
    
        TRANSFER("transfer", "支付宝转账"),
    
        MINI_PROGRAM_PAY("miniProgramPay", "小程序支付");
    
        private final String code;
        private final String description;
    
        private static final Map<String, AliPayOrderTypeEnum> CACHE =
                Arrays.stream(values()).collect(Collectors.toMap(AliPayOrderTypeEnum::getCode, Function.identity()));
    
        public static AliPayOrderTypeEnum of(String code) {
            return CACHE.get(code);
        }
    }
    
    package com.za.base.service.alipay.impl;
    
    import com.alipay.api.AlipayApiException;
    import com.alipay.api.AlipayClient;
    import com.alipay.api.domain.AlipayFundTransUniTransferModel;
    import com.alipay.api.domain.AlipayTradeQueryModel;
    import com.alipay.api.domain.AlipayTradeRefundModel;
    import com.alipay.api.domain.Participant;
    import com.alipay.api.internal.util.AlipaySignature;
    import com.alipay.api.request.AlipayFundTransUniTransferRequest;
    import com.alipay.api.request.AlipayTradeQueryRequest;
    import com.alipay.api.request.AlipayTradeRefundRequest;
    import com.alipay.api.response.AlipayFundTransUniTransferResponse;
    import com.alipay.api.response.AlipayTradeQueryResponse;
    import com.alipay.api.response.AlipayTradeRefundResponse;
    import com.za.base.config.AlipayConfig;
    import com.za.base.domain.enums.AliPayOrderTypeEnum;
    import com.za.base.service.alipay.AliPayService;
    import com.za.base.util.StringUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.UUID;
    
    /**
     * @author 卓东东
     * @description: TODO
     * @since 2025/6/6
     */
    @Service
    @Slf4j
    public class AliPayServiceImpl implements AliPayService {
    
        @Resource
        private AlipayClient alipayClient;
    
        @Resource
        private AlipayConfig alipayConfig;
    
        /**
         * 根据业务生成一个32位的唯一的商户订单号
         * 格式: 业务缩写-时间戳(秒)-UUID截取部分,整体长度控制32位以内
         *
         * @param aliPayOrderTypeEnum 业务类型枚举
         * @return 32位唯一订单号字符串
         */
        @Override
        public String createBizOrderNo(AliPayOrderTypeEnum aliPayOrderTypeEnum) {
            return createBizOrderNo(aliPayOrderTypeEnum, 32);
        }
    
        /**
         * 根据业务生成一个指定位数的唯一的商户订单号
         * 格式: 业务缩写-时间戳(秒)-UUID,长度不小于业务缩写+时间戳+2个分隔符长度
         *
         * @param aliPayOrderTypeEnum 业务类型枚举
         * @param length              目标订单号总长度
         * @return 指定位数唯一订单号字符串
         */
        @Override
        public String createBizOrderNo(AliPayOrderTypeEnum aliPayOrderTypeEnum, Integer length) {
            if (aliPayOrderTypeEnum == null) {
                throw new IllegalArgumentException("业务类型不能为空");
            }
            if (length == null || length < 10) { // 10位是个粗略下限,防止长度太短
                throw new IllegalArgumentException("长度必须大于等于10");
            }
    
            String prefix = aliPayOrderTypeEnum.getCode();
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
    
            // 两个分隔符的长度
            final int separatorLen = 2;
    
            // 计算UUID可用长度
            int uuidLength = length - prefix.length() - timestamp.length() - separatorLen;
            if (uuidLength <= 0) {
                throw new IllegalArgumentException("长度过短,无法生成符合要求的订单号");
            }
    
            // 去掉UUID中的横线,纯16进制字符串
            String uuid = UUID.randomUUID().toString().replace("-", "");
    
            // 截取uuid部分
            String uuidPart = uuid.substring(0, Math.min(uuidLength, uuid.length()));
    
            return prefix + "-" + timestamp + "-" + uuidPart;
        }
    
        @Override
        public AlipayFundTransUniTransferResponse transferToUser(String bizNo, String amount, String identity, String identityType, String realName) throws AlipayApiException {
            log.info("【转账请求】准备向用户转账,订单号:{},金额:{},收款人身份:{},类型:{}", bizNo, amount, identity, identityType);
    
            AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
            AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
    
            // 设置商户订单号
            model.setOutBizNo(bizNo);
    
            // 支付宝金额必须是字符串类型的“元”单位(保留两位小数),例如 "100.00"
            String amountStr = new BigDecimal(amount).setScale(2, RoundingMode.DOWN).toPlainString();
            model.setTransAmount(amountStr);
            log.debug("【转账请求】转换后金额为:{}", amountStr);
    
            // 必填参数:产品码(无需密码转账)和业务场景(直接转账)
            model.setProductCode("TRANS_ACCOUNT_NO_PWD");
            model.setBizScene("DIRECT_TRANSFER");
    
            // 订单标题(可用于后台账单查询)
            model.setOrderTitle("用户间转账");
    
            // 设置收款人信息
            Participant participant = new Participant();
            participant.setIdentity(identity);         // 支付宝 UID 或登录账号
            participant.setIdentityType(identityType); // ALIPAY_USER_ID 或 ALIPAY_LOGON_ID
    
            // 如果是手机号/邮箱登录名,必须传入收款人姓名
            if ("ALIPAY_LOGON_ID".equals(identityType)) {
                if (StringUtil.nonText(realName)) {
                    throw new AlipayApiException("通过手机号或邮箱登录名转账,比如传入收款人姓名!");
                }
                participant.setName(realName);
            }
            model.setPayeeInfo(participant);
    
            request.setBizModel(model);
    
            try {
                // 发起转账请求
                AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);
    
                if (response.isSuccess() && "SUCCESS".equals(response.getStatus())) {
                    log.info("【转账成功】订单号:{},支付宝单号:{},收款人:{}", bizNo, response.getOrderId(), identity);
                } else {
                    log.error("【转账失败】订单号:{},code={}, msg={}, subCode={}, subMsg={}",
                            bizNo, response.getCode(), response.getMsg(), response.getSubCode(), response.getSubMsg());
                }
    
                return response;
            } catch (AlipayApiException e) {
                log.error("【转账异常】订单号:{},异常信息:{}", bizNo, e.getMessage(), e);
                throw e;
            }
        }
    
        @Override
        public AlipayTradeQueryResponse queryPayOrder(String outTradeNo) throws AlipayApiException {
            // 构造请求参数以调用接口
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            AlipayTradeQueryModel model = new AlipayTradeQueryModel();
    
            // 设置订单支付时传入的商户订单号 或者 支付宝交易号 这两个二选一就行
            model.setOutTradeNo(outTradeNo);
    //        model.setTradeNo("");
    
            // 设置查询选项
            List<String> queryOptions = new ArrayList<>();
            queryOptions.add("trade_settle_info");
            model.setQueryOptions(queryOptions);
    
            request.setBizModel(model);
    
            AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);
    
            log.info("【支付查询】outTradeNo={}, status={}, msg={}", outTradeNo, response.getTradeStatus(), response.getMsg());
    
            return response;
        }
    
        @Override
        public AlipayTradeRefundResponse refund(String outTradeNo, BigDecimal amount, String refundReason) throws AlipayApiException {
            // 构造请求参数以调用接口
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            AlipayTradeRefundModel model = new AlipayTradeRefundModel();
    
            // 设置商户订单号
            model.setOutTradeNo(outTradeNo);
    
            // 设置退款金额
            model.setRefundAmount(amount.toString());
    
            // 设置退款原因说明
            model.setRefundReason(refundReason);
    
            request.setBizModel(model);
    
            return alipayClient.certificateExecute(request);
        }
    
        @Override
        public void appRealNameInit(String name, String idCard, String returnUrl) {
            //todo zdd
        }
    
        @Override
        public boolean signVerified(Map<String, String> params) throws AlipayApiException {
    
            // 验签
    
            // 证书模式
            boolean signVerified = AlipaySignature.rsaCertCheckV1(
                    params,
                    alipayConfig.getAlipayCertPath(),      // 支付宝公钥证书路径
                    "UTF-8",
                    "RSA2"
            );
    
            // 密钥模式
    //        boolean signVerified = AlipaySignature.rsaCheckV1(
    //                params,
    //                alipayConfig.getAlipayPublicKey(),
    //                "UTF-8",
    //                "RSA2"
    //        );
    
            return signVerified;
        }
    
    }
    

    四、总结

    至此,我们完成了支付宝开放平台证书模式的完整接入流程,包括密钥生成、证书配置、SDK初始化以及接口调用实战。

    支付宝接口的门槛并不高,但证书模式对安全性和规范性要求更高,建议团队在生产环境中统一采用证书模式,以保障数据安全和接口调用稳定性。

    如果你已经顺利跑通,请务必妥善保存私钥文件、配置好证书路径,并定期关注证书有效期,避免服务中断。

    未来如果你想继续扩展代扣、转账、签约等更多支付宝高级能力,这一套证书机制也将是你的安全基础。

    欢迎收藏、分享本文,也欢迎留言交流。
     

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值