1. 引言
随着移动支付的普及,为应用接入支付宝支付功能已成为标配。IJPay 作为一款轻量级、易用的 Java 支付工具库,封装了微信支付、支付宝支付等主流支付方式的 API,让开发者能更专注于业务逻辑。本文将详细介绍如何在 Spring Boot 项目中集成 IJPay 来实现支付宝的当面付(扫码支付)或手机网站支付,并重点解决因支付宝 SDK response 类版本更新带来的兼容性问题。
注意: 本文假设你已具备基本的 Spring Boot 开发经验,并已注册支付宝开放平台账号,获取了应用的
AppId、私钥 (private_key)和公钥 (public_key)。沙箱环境可用于测试。
2. 环境准备与依赖引入
首先,创建一个 Spring Boot 项目(推荐使用 Spring Boot 2.x 或 3.x)。
在 pom.xml 中引入 IJPay 核心依赖。IJPay 本身会传递依赖支付宝官方 SDK。
<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- IJPay 核心依赖 -->
<!-- 请检查 IJPay 官方 GitHub 获取最新版本号 -->
<dependency>
<groupId>com.ijpay</groupId>
<artifactId>ijpay-core</artifactId>
<version>3.10.2</version> <!-- 示例版本,请使用最新版 -->
</dependency>
<!-- Lombok (可选,用于简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- 其他你需要的依赖 -->
</dependencies>
关键点: 确保使用的 IJPay 版本足够新,它内部集成的支付宝 SDK (
alipay-sdk-java) 版本也较新(通常是4.15.112.ALL或更高),这样才能使用com.alipay.api.response包下的新Response类。
3. 配置支付宝参数
在 application.yml (或 application.properties) 中配置支付宝相关参数。
# application.yml
alipay:
appId: your_app_id_here # 你的支付宝应用ID
privateKey: |
-----BEGIN RSA PRIVATE KEY-----
your_private_key_content
-----END RSA PRIVATE KEY-----
publicKey: |
-----BEGIN PUBLIC KEY-----
your_public_key_content
-----END PUBLIC KEY-----
# 支付宝公钥(非应用公钥,用于验签,沙箱环境可选,生产环境强烈建议配置)
alipayPublicKey: |
-----BEGIN PUBLIC KEY-----
alipay_public_key_content
-----END PUBLIC KEY-----
notifyUrl: http://your-domain.com/alipay/notify # 异步通知回调地址
returnUrl: http://your-domain.com/alipay/return # 同步返回地址(支付成功后浏览器跳转)
# 网关地址
gatewayUrl: https://openapi.alipay.com/gateway.do # 正式环境
# gatewayUrl: https://openapi.alipaydev.com/gateway.do # 沙箱环境
charset: UTF-8
format: JSON
signType: RSA2
安全提示: 生产环境中,私钥等敏感信息不应硬编码在配置文件中,应使用配置中心(如 Nacos, Apollo)或环境变量管理。
4. 创建支付宝配置类 (AlipayConfig)
创建一个配置类来加载 application.yml 中的参数,并构建 IJPay 所需的 AlipayConfigModel。
import cn.hutool.core.io.IoUtil;
import com.ijpay.alipay.AliPayApiConfig;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.io.InputStream;
@Data
@Configuration
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
private String appId;
private String privateKey;
private String publicKey;
private String alipayPublicKey;
private String notifyUrl;
private String returnUrl;
private String gatewayUrl;
private String charset;
private String format;
private String signType;
/**
* 创建 IJPay 的 AliPayApiConfig 实例
* 这是 IJPay 与支付宝交互的核心配置
*/
public AliPayApiConfig getApiConfig() {
// 方式一:使用字符串私钥(从配置文件读取)
return AliPayApiConfig.builder()
.appId(appId)
.privateKey(privateKey) // 直接传入私钥字符串
.alipayPublicKey(alipayPublicKey) // 支付宝公钥,用于验签
.notifyUrl(notifyUrl)
.returnUrl(returnUrl)
.gatewayUrl(gatewayUrl)
.charset(charset)
.format(format)
.signType(signType)
.build();
// 方式二:如果私钥存储在文件中,可以使用 InputStream
/*
try (InputStream privateKeyStream = ...; // 获取私钥文件输入流
InputStream alipayPublicKeyStream = ...) { // 获取支付宝公钥文件输入流
return AliPayApiConfig.builder()
.appId(appId)
.privateKey(IoUtil.readUtf8(privateKeyStream))
.alipayPublicKey(IoUtil.readUtf8(alipayPublicKeyStream))
.notifyUrl(notifyUrl)
.returnUrl(returnUrl)
.gatewayUrl(gatewayUrl)
.charset(charset)
.format(format)
.signType(signType)
.build();
} catch (Exception e) {
throw new RuntimeException("加载支付宝密钥文件失败", e);
}
*/
}
}
5. 支付服务实现 (AlipayService)
创建服务类来封装支付逻辑。这是核心部分,我们将展示如何调用 IJPay 方法并处理新版 Response。
import cn.hutool.core.util.StrUtil;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.model.AlipayTradePayModel;
import com.ijpay.alipay.model.AlipayTradePreCreateModel;
import com.ijpay.core.model.BaseResult;
import com.ijpay.core.utils.JsonKit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
// 注意导入的是新版 Response
import com.alipay.api.response.AlipayTradePayResponse;
import com.alipay.api.response.AlipayTradePreCreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
@Service
@RequiredArgsConstructor
@Slf4j
public class AlipayService {
private final AlipayConfig alipayConfig;
/**
* 场景:当面付 - 扫码支付 (用户扫商家二维码)
* 生成支付二维码
*/
public String createQrCode(String outTradeNo, String subject, String totalAmount) {
// 构建请求参数模型
AlipayTradePreCreateModel model = new AlipayTradePreCreateModel();
model.setOutTradeNo(outTradeNo);
model.setSubject(subject);
model.setTotalAmount(totalAmount);
model.setNotifyUrl(alipayConfig.getNotifyUrl());
// 调用 IJPay 封装的方法
// **关键:IJPay 的 AliPayApi.tradePreCreate 方法返回的是 BaseResult<AlipayTradePreCreateResponse>**
BaseResult<AlipayTradePreCreateResponse> result = AliPayApi.tradePreCreate(model, alipayConfig.getApiConfig());
// **核心:处理新版 Response**
if (result != null && result.isSuccess()) {
// result.getModel() 返回 AlipayTradePreCreateResponse 实例
AlipayTradePreCreateResponse response = result.getModel();
// 检查支付宝返回的业务结果
if (StrUtil.equals(response.getCode(), "10000")) { // 10000 表示成功
return response.getQrCode(); // 返回二维码内容,前端可生成二维码
} else {
log.error("支付宝预下单失败,错误码:{},错误信息:{}", response.getCode(), response.getMsg());
throw new RuntimeException("支付创建失败: " + response.getMsg());
}
} else {
log.error("IJPay 调用失败或网络异常");
throw new RuntimeException("支付创建失败: 网络或系统异常");
}
}
/**
* 场景:条码支付 (商家扫用户付款码)
* 直接调用支付接口
*/
public boolean tradePay(String outTradeNo, String subject, String totalAmount, String authCode) {
AlipayTradePayModel model = new AlipayTradePayModel();
model.setOutTradeNo(outTradeNo);
model.setSubject(subject);
model.setTotalAmount(totalAmount);
model.setAuthCode(authCode); // 用户的付款码
model.setNotifyUrl(alipayConfig.getNotifyUrl());
// **关键:AliPayApi.tradePay 返回 BaseResult<AlipayTradePayResponse>**
BaseResult<AlipayTradePayResponse> result = AliPayApi.tradePay(model, alipayConfig.getApiConfig());
if (result != null && result.isSuccess()) {
AlipayTradePayResponse response = result.getModel();
if (StrUtil.equals(response.getCode(), "10000")) {
log.info("支付成功,交易号:{}", response.getTradeNo());
return true;
} else if (StrUtil.equals(response.getCode(), "10003")) {
// 交易处理中,需要轮询查询
log.warn("交易处理中,需查询确认,错误码:{}", response.getSubCode());
return false;
} else {
log.error("支付失败,错误码:{},错误信息:{}", response.getCode(), response.getMsg());
return false;
}
} else {
log.error("调用支付宝支付接口失败");
return false;
}
}
/**
* 查询交易状态
*/
public AlipayTradeQueryResponse queryTrade(String outTradeNo) {
BaseResult<AlipayTradeQueryResponse> result = AliPayApi.tradeQuery(outTradeNo, null, alipayConfig.getApiConfig());
if (result != null && result.isSuccess()) {
return result.getModel(); // 直接返回新版 Response 对象
} else {
log.error("查询交易失败");
return null;
}
}
// 可以添加退款、关闭订单等其他方法...
}
6. 控制器 (AlipayController)
提供 REST API 供前端调用。
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
@RestController
@RequestMapping("/alipay")
public class AlipayController {
@Autowired
private AlipayService alipayService;
/**
* 创建支付二维码
* 前端调用此接口获取二维码内容
*/
@PostMapping("/create-qr")
public String createQrCode(@RequestParam String outTradeNo,
@RequestParam String subject,
@RequestParam String totalAmount) {
return alipayService.createQrCode(outTradeNo, subject, totalAmount);
}
// 其他接口,如处理同步返回、异步通知等...
}
7. 重点:Response 版本不同的替代方案与最佳实践
这是本文的核心价值所在。许多老教程使用的是过时的 AlipayResponse 或直接处理 String 响应,这非常不推荐。
问题: 旧代码可能这样写:
// ❌ 过时且不推荐的方式
AlipayResponse response = alipayClient.execute(request);
if(response.isSuccess()){
// 处理...
}
或者直接解析 JSON 字符串。
IJPay 的现代解决方案与最佳实践:
-
拥抱
BaseResult<T>泛型封装:- IJPay 的设计非常优秀,它没有直接返回支付宝 SDK 的
Response,而是将其封装在BaseResult<T>中。 T就是新版的AlipayTradeXXXResponse。- 优势:
BaseResult提供了isSuccess()方法,它综合判断了网络调用是否成功(result != null && result.isSuccess())和支付宝业务是否成功(response.getCode().equals("10000"))。这比直接调用response.isSuccess()更可靠,因为后者只判断了 HTTP 层面的成功。
- IJPay 的设计非常优秀,它没有直接返回支付宝 SDK 的
-
直接使用新版
Response类:- 通过
BaseResult.getModel()方法获取到的就是com.alipay.api.response.AlipayTradePreCreateResponse等标准对象。 - 可以利用 IDE 的自动补全和类型检查,安全地访问
getCode(),getMsg(),getSubCode(),getSubMsg(),getQrCode(),getTradeNo()等属性。 - 这是最推荐的方式,代码清晰、类型安全、易于维护。
- 通过
-
避免手动 JSON 解析:
- 不要尝试将
result或response转成String再用JSONObject解析。IJPay 和支付宝 SDK 已经完成了反序列化工作。
- 不要尝试将
-
统一错误处理:
- 建立全局异常处理器 (
@ControllerAdvice),捕获支付服务中抛出的业务异常,并返回统一的 JSON 格式错误信息给前端。
- 建立全局异常处理器 (
8. 异步通知与同步返回
- 异步通知 (
notify_url): 支付宝服务器在交易状态变更后会主动调用此地址。必须在此接口中:- 验证通知的签名(IJPay 提供了
AliPayApi.verifyNotify()方法)。 - 检查
trade_status是否为TRADE_SUCCESS。 - 根据
out_trade_no查询并更新自己数据库中的订单状态。 - 处理成功后必须返回
success字符串给支付宝,否则会持续重试通知。
- 验证通知的签名(IJPay 提供了
- 同步返回 (
return_url): 用户在支付宝客户端完成支付后,浏览器会跳转回此地址。不能在此处更新订单状态,仅用于展示支付结果页面。最终状态以异步通知为准。
9. 测试与部署
- 沙箱环境测试: 务必先在支付宝开放平台的沙箱环境中进行充分测试。IJPay 也提供了沙箱测试工具。
- 日志监控: 添加详细的日志记录,特别是在支付、查询、通知处理等关键环节。
- 生产环境: 确认所有配置(尤其是密钥、网关地址)已切换到生产环境,并确保服务器时间准确(NTP 同步),因为签名依赖时间戳。
10. 总结
通过 IJPay 集成支付宝支付,可以显著降低开发复杂度。本文的关键在于展示了如何正确处理新版 com.alipay.api.response 包下的 Response 对象。记住:
- 依赖
BaseResult<T>:它是 IJPay 提供的可靠封装。 - 使用
getModel():获取类型安全的新版Response实例。 - 双重检查:先检查
BaseResult.isSuccess(),再检查Response.getCode() == "10000"。 - 重视异步通知:它是最终确认支付结果的唯一可靠途径。
遵循这些实践,你就能构建出一个稳定、健壮且符合现代 Java 开发规范的支付宝支付系统。
794






