共计 2136 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
最近在开发支付宝订阅 ChatGPT 的服务时,遇到了几个让人头疼的问题。这些问题如果不处理好,很容易导致业务逻辑混乱甚至资金损失。下面分享几个常见的坑点:

-
用户重复支付 :由于网络延迟或用户重复点击,可能导致同一笔订单被多次支付,如果不做好幂等处理,会导致用户被多次扣款。
-
异步通知丢失 :支付宝的回调通知可能因为网络问题丢失,如果没有完善的补偿机制,会导致订阅状态与实际支付状态不一致。
-
环境配置差异 :沙箱环境和生产环境的配置不同,特别是密钥和回调地址,如果没注意区分,会导致鉴权失败。
技术方案
1. 支付宝 RSA2 签名验证
这是最基础也是最重要的环节。支付宝的通知都会附带签名,我们需要用支付宝公钥验证签名的有效性,防止伪造通知。
// Java 示例代码
public boolean verifySignature(Map<String, String> params, String publicKey) {
try {String sign = params.get("sign");
String content = AlipaySignature.getSignCheckContentV2(params);
return AlipaySignature.rsaCheck(content, sign, publicKey, "UTF-8", "RSA2");
} catch (AlipayApiException e) {log.error("验签失败", e);
return false;
}
}
2. 幂等性处理
为了防止重复处理同一个通知,我们采用 Redis 分布式锁 + 本地事务表的双重保障机制。
- Redis 分布式锁 :在处理通知前先获取锁,处理完成后释放锁。
- 本地事务表 :记录已经处理过的通知 ID,下次收到相同 ID 直接返回成功。
3. 支付超时处理
对于未支付的订单,我们通过 RabbitMQ 的延迟队列实现自动关闭功能。订单创建时发送一个延迟消息,如果到期还未支付,则自动关闭订单。
代码示例
支付宝回调处理 Controller
@RestController
@RequestMapping("/alipay")
public class AlipayNotifyController {@PostMapping("/notify")
public String handleNotify(@RequestParam Map<String, String> params) {
// 1. 验证签名
if (!verifySignature(params, alipayPublicKey)) {return "failure";}
// 2. 获取业务参数
String tradeNo = params.get("out_trade_no");
String tradeStatus = params.get("trade_status");
// 3. 处理业务逻辑
if ("TRADE_SUCCESS".equals(tradeStatus)) {// 处理支付成功逻辑} else if ("TRADE_CLOSED".equals(tradeStatus)) {// 处理交易关闭逻辑}
return "success";
}
}
订阅状态机设计
@Entity
public class Subscription {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private SubscriptionStatus status;
// 其他字段...
}
public enum SubscriptionStatus {
CREATED, // 已创建
PAID, // 已支付
ACTIVATED, // 已激活
EXPIRED, // 已过期
CANCELLED // 已取消
}
生产建议
1. 支付宝公钥动态更新
支付宝的公钥可能会更换,建议定期检查并更新公钥,或者实现自动获取公钥的机制。
2. 接口限流
使用 Sentinel 对回调接口进行 QPS 限流,防止恶意攻击或异常流量导致服务不可用。
// Sentinel 配置示例
@PostMapping("/notify")
@SentinelResource(value = "alipayNotify", blockHandler = "handleBlock")
public String handleNotify(...) {// ...}
public String handleBlock(...) {return "系统繁忙,请稍后再试";}
3. 异步处理耗时逻辑
不要在回调接口中直接处理耗时业务逻辑,应该将主要业务逻辑放入消息队列异步处理,回调接口只做最基本的验证和状态更新。
验证环节
1. 支付宝验签工具
支付宝提供了在线验签工具,可以用来验证你的签名逻辑是否正确。
2. Postman 模拟通知
可以编写一个 Postman 集合,模拟各种支付场景:
- 支付成功通知
- 支付失败通知
- 部分退款通知
通过不同的场景测试,确保你的系统能够正确处理各种情况。
总结
接入支付宝订阅服务看似简单,但要保证稳定可靠运行,需要考虑很多细节。特别是支付系统的幂等性、一致性和可靠性,都需要仔细设计。希望本文的经验对你有所帮助。
