本文来详细描述一下支付宝开放平台接口方面的接入。
看完本文,你将学会:
✅ 如何在支付宝开放平台启用证书模式
✅ 如何生成 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初始化以及接口调用实战。
支付宝接口的门槛并不高,但证书模式对安全性和规范性要求更高,建议团队在生产环境中统一采用证书模式,以保障数据安全和接口调用稳定性。
如果你已经顺利跑通,请务必妥善保存私钥文件、配置好证书路径,并定期关注证书有效期,避免服务中断。
未来如果你想继续扩展代扣、转账、签约等更多支付宝高级能力,这一套证书机制也将是你的安全基础。
欢迎收藏、分享本文,也欢迎留言交流。