前言

重复支付作为电商系统中的重要安全问题,直接影响着用户体验和资金安全。通过合理的重复支付防护机制和幂等性设计,能够有效避免用户重复扣款,确保支付系统的稳定运行。本文从重复支付防护到幂等性设计,从基础实现到企业级应用,系统梳理重复支付防护的完整解决方案。

一、重复支付防护架构设计

1.1 重复支付防护整体架构

1.2 重复支付防护策略架构

二、重复支付防护实现

2.1 幂等性控制

2.1.1 幂等性控制服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/**
* 幂等性控制服务
*/
@Service
public class IdempotencyControlService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private PaymentOrderMapper paymentOrderMapper;

@Autowired
private IdempotencyTokenMapper idempotencyTokenMapper;

private final String IDEMPOTENCY_PREFIX = "idempotency:";
private final long IDEMPOTENCY_EXPIRE_TIME = 300; // 5分钟

/**
* 生成幂等性令牌
*/
public String generateIdempotencyToken(String userId, String orderId, String paymentMethod) {
try {
// 生成唯一令牌
String token = UUID.randomUUID().toString().replace("-", "");

// 构建幂等性键
String idempotencyKey = buildIdempotencyKey(userId, orderId, paymentMethod);

// 存储幂等性信息
IdempotencyInfo idempotencyInfo = new IdempotencyInfo();
idempotencyInfo.setToken(token);
idempotencyInfo.setUserId(userId);
idempotencyInfo.setOrderId(orderId);
idempotencyInfo.setPaymentMethod(paymentMethod);
idempotencyInfo.setCreateTime(new Date());
idempotencyInfo.setStatus(IdempotencyStatus.PENDING);

// 存储到Redis
redisTemplate.opsForValue().set(IDEMPOTENCY_PREFIX + token, idempotencyInfo,
Duration.ofSeconds(IDEMPOTENCY_EXPIRE_TIME));

// 存储到数据库
idempotencyTokenMapper.insert(idempotencyInfo);

log.info("生成幂等性令牌成功: userId={}, orderId={}, token={}", userId, orderId, token);
return token;

} catch (Exception e) {
log.error("生成幂等性令牌失败", e);
throw new PaymentException("生成幂等性令牌失败", e);
}
}

/**
* 检查幂等性
*/
public IdempotencyCheckResult checkIdempotency(String token) {
try {
// 从Redis获取幂等性信息
IdempotencyInfo idempotencyInfo = (IdempotencyInfo) redisTemplate.opsForValue()
.get(IDEMPOTENCY_PREFIX + token);

if (idempotencyInfo == null) {
// 从数据库获取
idempotencyInfo = idempotencyTokenMapper.selectByToken(token);
}

if (idempotencyInfo == null) {
return IdempotencyCheckResult.notFound();
}

// 检查状态
IdempotencyStatus status = idempotencyInfo.getStatus();

IdempotencyCheckResult result = new IdempotencyCheckResult();
result.setToken(token);
result.setIdempotencyInfo(idempotencyInfo);

switch (status) {
case PENDING:
result.setCanProcess(true);
result.setMessage("可以处理");
break;
case PROCESSING:
result.setCanProcess(false);
result.setMessage("正在处理中");
break;
case COMPLETED:
result.setCanProcess(false);
result.setMessage("已经处理完成");
break;
case FAILED:
result.setCanProcess(true);
result.setMessage("处理失败,可以重试");
break;
default:
result.setCanProcess(false);
result.setMessage("未知状态");
break;
}

return result;

} catch (Exception e) {
log.error("检查幂等性失败", e);
throw new PaymentException("检查幂等性失败", e);
}
}

/**
* 更新幂等性状态
*/
public void updateIdempotencyStatus(String token, IdempotencyStatus status, String result) {
try {
// 更新Redis
IdempotencyInfo idempotencyInfo = (IdempotencyInfo) redisTemplate.opsForValue()
.get(IDEMPOTENCY_PREFIX + token);

if (idempotencyInfo != null) {
idempotencyInfo.setStatus(status);
idempotencyInfo.setResult(result);
idempotencyInfo.setUpdateTime(new Date());

redisTemplate.opsForValue().set(IDEMPOTENCY_PREFIX + token, idempotencyInfo,
Duration.ofSeconds(IDEMPOTENCY_EXPIRE_TIME));
}

// 更新数据库
idempotencyTokenMapper.updateStatus(token, status, result);

log.info("更新幂等性状态成功: token={}, status={}", token, status);

} catch (Exception e) {
log.error("更新幂等性状态失败", e);
throw new PaymentException("更新幂等性状态失败", e);
}
}

/**
* 构建幂等性键
*/
private String buildIdempotencyKey(String userId, String orderId, String paymentMethod) {
return String.format("%s:%s:%s", userId, orderId, paymentMethod);
}

/**
* 清理过期的幂等性令牌
*/
@Scheduled(fixedRate = 300000) // 5分钟
public void cleanupExpiredTokens() {
try {
// 清理Redis中的过期令牌
Set<String> keys = redisTemplate.keys(IDEMPOTENCY_PREFIX + "*");

for (String key : keys) {
Long ttl = redisTemplate.getExpire(key);
if (ttl != null && ttl <= 0) {
redisTemplate.delete(key);
}
}

// 清理数据库中的过期令牌
idempotencyTokenMapper.deleteExpiredTokens();

log.info("清理过期幂等性令牌完成");

} catch (Exception e) {
log.error("清理过期幂等性令牌失败", e);
}
}
}

2.2 分布式锁

2.2.1 分布式锁服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/**
* 分布式锁服务
*/
@Service
public class DistributedLockService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private final String LOCK_PREFIX = "lock:";
private final long DEFAULT_LOCK_TIME = 30; // 30秒
private final long DEFAULT_WAIT_TIME = 5; // 5秒

/**
* 获取分布式锁
*/
public DistributedLock acquireLock(String lockKey, long lockTime, long waitTime) {
try {
String fullLockKey = LOCK_PREFIX + lockKey;
String lockValue = UUID.randomUUID().toString();

long startTime = System.currentTimeMillis();

while (System.currentTimeMillis() - startTime < waitTime * 1000) {
// 尝试获取锁
Boolean success = redisTemplate.opsForValue().setIfAbsent(fullLockKey, lockValue,
Duration.ofSeconds(lockTime));

if (success) {
DistributedLock lock = new DistributedLock();
lock.setLockKey(fullLockKey);
lock.setLockValue(lockValue);
lock.setLockTime(lockTime);
lock.setAcquireTime(System.currentTimeMillis());

log.info("获取分布式锁成功: {}", lockKey);
return lock;
}

// 等待一段时间后重试
Thread.sleep(100);
}

log.warn("获取分布式锁超时: {}", lockKey);
return null;

} catch (Exception e) {
log.error("获取分布式锁失败", e);
throw new PaymentException("获取分布式锁失败", e);
}
}

/**
* 获取分布式锁(默认参数)
*/
public DistributedLock acquireLock(String lockKey) {
return acquireLock(lockKey, DEFAULT_LOCK_TIME, DEFAULT_WAIT_TIME);
}

/**
* 释放分布式锁
*/
public boolean releaseLock(DistributedLock lock) {
try {
if (lock == null) {
return false;
}

// 使用Lua脚本确保原子性
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";

DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(luaScript);
script.setResultType(Long.class);

Long result = redisTemplate.execute(script,
Collections.singletonList(lock.getLockKey()), lock.getLockValue());

boolean success = result != null && result == 1;

if (success) {
log.info("释放分布式锁成功: {}", lock.getLockKey());
} else {
log.warn("释放分布式锁失败: {}", lock.getLockKey());
}

return success;

} catch (Exception e) {
log.error("释放分布式锁失败", e);
return false;
}
}

/**
* 延长锁时间
*/
public boolean extendLock(DistributedLock lock, long extendTime) {
try {
if (lock == null) {
return false;
}

// 使用Lua脚本确保原子性
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('expire', KEYS[1], ARGV[2]) " +
"else " +
" return 0 " +
"end";

DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(luaScript);
script.setResultType(Long.class);

Long result = redisTemplate.execute(script,
Collections.singletonList(lock.getLockKey()),
lock.getLockValue(), String.valueOf(extendTime));

boolean success = result != null && result == 1;

if (success) {
lock.setLockTime(lock.getLockTime() + extendTime);
log.info("延长锁时间成功: {}, 延长: {}秒", lock.getLockKey(), extendTime);
} else {
log.warn("延长锁时间失败: {}", lock.getLockKey());
}

return success;

} catch (Exception e) {
log.error("延长锁时间失败", e);
return false;
}
}

/**
* 检查锁是否存在
*/
public boolean isLocked(String lockKey) {
try {
String fullLockKey = LOCK_PREFIX + lockKey;
Object lockValue = redisTemplate.opsForValue().get(fullLockKey);

return lockValue != null;

} catch (Exception e) {
log.error("检查锁状态失败", e);
return false;
}
}

/**
* 获取锁剩余时间
*/
public long getLockTtl(String lockKey) {
try {
String fullLockKey = LOCK_PREFIX + lockKey;
Long ttl = redisTemplate.getExpire(fullLockKey);

return ttl != null ? ttl : -1;

} catch (Exception e) {
log.error("获取锁剩余时间失败", e);
return -1;
}
}
}

2.3 状态机控制

2.3.1 支付状态机服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/**
* 支付状态机服务
*/
@Service
public class PaymentStateMachineService {

@Autowired
private PaymentOrderMapper paymentOrderMapper;

@Autowired
private PaymentStateTransitionMapper stateTransitionMapper;

/**
* 状态机定义
*/
private final Map<PaymentStatus, Set<PaymentStatus>> stateTransitions = new HashMap<>();

@PostConstruct
public void initStateMachine() {
// 初始化状态转换规则
stateTransitions.put(PaymentStatus.PENDING,
Set.of(PaymentStatus.PROCESSING, PaymentStatus.CANCELLED));

stateTransitions.put(PaymentStatus.PROCESSING,
Set.of(PaymentStatus.SUCCESS, PaymentStatus.FAILED, PaymentStatus.CANCELLED));

stateTransitions.put(PaymentStatus.SUCCESS,
Set.of(PaymentStatus.REFUNDED));

stateTransitions.put(PaymentStatus.FAILED,
Set.of(PaymentStatus.PROCESSING, PaymentStatus.CANCELLED));

stateTransitions.put(PaymentStatus.CANCELLED,
Set.of(PaymentStatus.PROCESSING));

stateTransitions.put(PaymentStatus.REFUNDED,
Set.of());
}

/**
* 检查状态转换是否合法
*/
public boolean canTransition(PaymentStatus fromStatus, PaymentStatus toStatus) {
try {
Set<PaymentStatus> allowedTransitions = stateTransitions.get(fromStatus);

if (allowedTransitions == null) {
return false;
}

return allowedTransitions.contains(toStatus);

} catch (Exception e) {
log.error("检查状态转换失败", e);
return false;
}
}

/**
* 执行状态转换
*/
@Transactional
public boolean transitionState(String orderId, PaymentStatus fromStatus, PaymentStatus toStatus,
String reason) {
try {
// 检查状态转换是否合法
if (!canTransition(fromStatus, toStatus)) {
log.warn("非法状态转换: {} -> {}, 订单: {}", fromStatus, toStatus, orderId);
return false;
}

// 更新订单状态
int updateCount = paymentOrderMapper.updateStatus(orderId, fromStatus, toStatus);

if (updateCount == 0) {
log.warn("状态转换失败,订单不存在或状态不匹配: {}", orderId);
return false;
}

// 记录状态转换日志
PaymentStateTransition transition = new PaymentStateTransition();
transition.setOrderId(orderId);
transition.setFromStatus(fromStatus);
transition.setToStatus(toStatus);
transition.setReason(reason);
transition.setCreateTime(new Date());

stateTransitionMapper.insert(transition);

log.info("状态转换成功: {} -> {}, 订单: {}", fromStatus, toStatus, orderId);
return true;

} catch (Exception e) {
log.error("状态转换失败", e);
throw new PaymentException("状态转换失败", e);
}
}

/**
* 获取订单当前状态
*/
public PaymentStatus getCurrentStatus(String orderId) {
try {
PaymentOrder order = paymentOrderMapper.selectByOrderId(orderId);

if (order == null) {
return null;
}

return order.getStatus();

} catch (Exception e) {
log.error("获取订单状态失败", e);
throw new PaymentException("获取订单状态失败", e);
}
}

/**
* 获取状态转换历史
*/
public List<PaymentStateTransition> getStateTransitionHistory(String orderId) {
try {
return stateTransitionMapper.selectByOrderId(orderId);

} catch (Exception e) {
log.error("获取状态转换历史失败", e);
throw new PaymentException("获取状态转换历史失败", e);
}
}

/**
* 检查订单是否可以支付
*/
public boolean canPay(String orderId) {
try {
PaymentStatus currentStatus = getCurrentStatus(orderId);

if (currentStatus == null) {
return false;
}

return canTransition(currentStatus, PaymentStatus.PROCESSING);

} catch (Exception e) {
log.error("检查订单是否可以支付失败", e);
return false;
}
}

/**
* 检查订单是否可以取消
*/
public boolean canCancel(String orderId) {
try {
PaymentStatus currentStatus = getCurrentStatus(orderId);

if (currentStatus == null) {
return false;
}

return canTransition(currentStatus, PaymentStatus.CANCELLED);

} catch (Exception e) {
log.error("检查订单是否可以取消失败", e);
return false;
}
}

/**
* 检查订单是否可以退款
*/
public boolean canRefund(String orderId) {
try {
PaymentStatus currentStatus = getCurrentStatus(orderId);

if (currentStatus == null) {
return false;
}

return canTransition(currentStatus, PaymentStatus.REFUNDED);

} catch (Exception e) {
log.error("检查订单是否可以退款失败", e);
return false;
}
}
}

三、企业级支付安全方案

3.1 支付防重复服务

3.1.1 支付防重复服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/**
* 支付防重复服务
*/
@Service
public class PaymentDuplicatePreventionService {

@Autowired
private IdempotencyControlService idempotencyControlService;

@Autowired
private DistributedLockService distributedLockService;

@Autowired
private PaymentStateMachineService stateMachineService;

@Autowired
private PaymentOrderMapper paymentOrderMapper;

@Autowired
private PaymentDuplicateCheckMapper duplicateCheckMapper;

/**
* 支付防重复处理
*/
@Transactional
public PaymentResult processPayment(PaymentRequest request) {
try {
String orderId = request.getOrderId();
String userId = request.getUserId();
String paymentMethod = request.getPaymentMethod();

// 1. 生成幂等性令牌
String idempotencyToken = idempotencyControlService.generateIdempotencyToken(
userId, orderId, paymentMethod);

// 2. 检查幂等性
IdempotencyCheckResult checkResult = idempotencyControlService.checkIdempotency(idempotencyToken);

if (!checkResult.isCanProcess()) {
return PaymentResult.duplicate(checkResult.getMessage());
}

// 3. 获取分布式锁
String lockKey = "payment:" + orderId;
DistributedLock lock = distributedLockService.acquireLock(lockKey);

if (lock == null) {
return PaymentResult.failed("获取支付锁失败");
}

try {
// 4. 再次检查订单状态
PaymentStatus currentStatus = stateMachineService.getCurrentStatus(orderId);

if (currentStatus == null) {
return PaymentResult.failed("订单不存在");
}

if (!stateMachineService.canPay(orderId)) {
return PaymentResult.failed("订单状态不允许支付");
}

// 5. 更新幂等性状态为处理中
idempotencyControlService.updateIdempotencyStatus(idempotencyToken,
IdempotencyStatus.PROCESSING, "开始处理支付");

// 6. 执行支付逻辑
PaymentResult result = executePayment(request);

// 7. 更新幂等性状态
if (result.isSuccess()) {
idempotencyControlService.updateIdempotencyStatus(idempotencyToken,
IdempotencyStatus.COMPLETED, "支付成功");
} else {
idempotencyControlService.updateIdempotencyStatus(idempotencyToken,
IdempotencyStatus.FAILED, result.getMessage());
}

return result;

} finally {
// 8. 释放分布式锁
distributedLockService.releaseLock(lock);
}

} catch (Exception e) {
log.error("支付防重复处理失败", e);
return PaymentResult.failed("支付处理失败: " + e.getMessage());
}
}

/**
* 执行支付逻辑
*/
private PaymentResult executePayment(PaymentRequest request) {
try {
String orderId = request.getOrderId();

// 1. 更新订单状态为处理中
boolean transitionSuccess = stateMachineService.transitionState(
orderId, PaymentStatus.PENDING, PaymentStatus.PROCESSING, "开始支付");

if (!transitionSuccess) {
return PaymentResult.failed("更新订单状态失败");
}

// 2. 调用第三方支付接口
PaymentResult paymentResult = callThirdPartyPayment(request);

// 3. 根据支付结果更新订单状态
if (paymentResult.isSuccess()) {
stateMachineService.transitionState(orderId, PaymentStatus.PROCESSING,
PaymentStatus.SUCCESS, "支付成功");
} else {
stateMachineService.transitionState(orderId, PaymentStatus.PROCESSING,
PaymentStatus.FAILED, "支付失败: " + paymentResult.getMessage());
}

return paymentResult;

} catch (Exception e) {
log.error("执行支付逻辑失败", e);
return PaymentResult.failed("支付执行失败: " + e.getMessage());
}
}

/**
* 调用第三方支付接口
*/
private PaymentResult callThirdPartyPayment(PaymentRequest request) {
try {
// 模拟调用第三方支付接口
// 实际实现中需要调用真实的支付接口

// 模拟支付成功
PaymentResult result = new PaymentResult();
result.setSuccess(true);
result.setTransactionId(UUID.randomUUID().toString());
result.setMessage("支付成功");

return result;

} catch (Exception e) {
log.error("调用第三方支付接口失败", e);
return PaymentResult.failed("调用支付接口失败: " + e.getMessage());
}
}

/**
* 检查重复支付
*/
public DuplicateCheckResult checkDuplicatePayment(String orderId, String userId,
String paymentMethod, BigDecimal amount) {
try {
// 1. 检查订单状态
PaymentStatus currentStatus = stateMachineService.getCurrentStatus(orderId);

if (currentStatus == PaymentStatus.SUCCESS) {
return DuplicateCheckResult.duplicate("订单已支付成功");
}

if (currentStatus == PaymentStatus.PROCESSING) {
return DuplicateCheckResult.duplicate("订单正在支付中");
}

// 2. 检查时间窗口内的重复支付
Date checkTime = new Date(System.currentTimeMillis() - 5 * 60 * 1000); // 5分钟内

List<PaymentDuplicateCheck> duplicateChecks = duplicateCheckMapper.selectByTimeRange(
userId, checkTime, new Date());

for (PaymentDuplicateCheck check : duplicateChecks) {
if (check.getOrderId().equals(orderId) &&
check.getPaymentMethod().equals(paymentMethod) &&
check.getAmount().compareTo(amount) == 0) {

return DuplicateCheckResult.duplicate("检测到重复支付");
}
}

// 3. 记录支付检查
PaymentDuplicateCheck duplicateCheck = new PaymentDuplicateCheck();
duplicateCheck.setOrderId(orderId);
duplicateCheck.setUserId(userId);
duplicateCheck.setPaymentMethod(paymentMethod);
duplicateCheck.setAmount(amount);
duplicateCheck.setCreateTime(new Date());

duplicateCheckMapper.insert(duplicateCheck);

return DuplicateCheckResult.normal();

} catch (Exception e) {
log.error("检查重复支付失败", e);
return DuplicateCheckResult.error("检查重复支付失败: " + e.getMessage());
}
}

/**
* 处理支付回调
*/
@Transactional
public PaymentResult handlePaymentCallback(PaymentCallbackRequest callbackRequest) {
try {
String orderId = callbackRequest.getOrderId();
String transactionId = callbackRequest.getTransactionId();
boolean success = callbackRequest.isSuccess();

// 1. 获取分布式锁
String lockKey = "payment_callback:" + orderId;
DistributedLock lock = distributedLockService.acquireLock(lockKey);

if (lock == null) {
return PaymentResult.failed("获取回调锁失败");
}

try {
// 2. 检查订单状态
PaymentStatus currentStatus = stateMachineService.getCurrentStatus(orderId);

if (currentStatus == PaymentStatus.SUCCESS) {
return PaymentResult.success("订单已支付成功");
}

if (currentStatus != PaymentStatus.PROCESSING) {
return PaymentResult.failed("订单状态不允许回调处理");
}

// 3. 更新订单状态
if (success) {
stateMachineService.transitionState(orderId, PaymentStatus.PROCESSING,
PaymentStatus.SUCCESS, "支付回调成功");
} else {
stateMachineService.transitionState(orderId, PaymentStatus.PROCESSING,
PaymentStatus.FAILED, "支付回调失败");
}

// 4. 更新订单交易ID
paymentOrderMapper.updateTransactionId(orderId, transactionId);

return PaymentResult.success("回调处理成功");

} finally {
distributedLockService.releaseLock(lock);
}

} catch (Exception e) {
log.error("处理支付回调失败", e);
return PaymentResult.failed("回调处理失败: " + e.getMessage());
}
}
}

3.2 支付安全监控

3.2.1 支付安全监控服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* 支付安全监控服务
*/
@Service
public class PaymentSecurityMonitoringService {

@Autowired
private PaymentOrderMapper paymentOrderMapper;

@Autowired
private PaymentDuplicateCheckMapper duplicateCheckMapper;

@Autowired
private PaymentSecurityAlertMapper securityAlertMapper;

/**
* 监控重复支付
*/
@Scheduled(fixedRate = 60000) // 1分钟
public void monitorDuplicatePayments() {
try {
// 检查最近5分钟内的重复支付
Date checkTime = new Date(System.currentTimeMillis() - 5 * 60 * 1000);

List<PaymentDuplicateCheck> duplicateChecks = duplicateCheckMapper.selectByTimeRange(
null, checkTime, new Date());

// 按用户分组统计
Map<String, List<PaymentDuplicateCheck>> userGroups = duplicateChecks.stream()
.collect(Collectors.groupingBy(PaymentDuplicateCheck::getUserId));

for (Map.Entry<String, List<PaymentDuplicateCheck>> entry : userGroups.entrySet()) {
String userId = entry.getKey();
List<PaymentDuplicateCheck> checks = entry.getValue();

if (checks.size() > 3) { // 5分钟内超过3次支付尝试
createSecurityAlert(userId, "重复支付异常",
"用户" + userId + "在5分钟内进行了" + checks.size() + "次支付尝试");
}
}

} catch (Exception e) {
log.error("监控重复支付失败", e);
}
}

/**
* 监控异常支付金额
*/
@Scheduled(fixedRate = 300000) // 5分钟
public void monitorAbnormalPaymentAmounts() {
try {
// 检查最近1小时内的支付
Date checkTime = new Date(System.currentTimeMillis() - 60 * 60 * 1000);

List<PaymentOrder> recentPayments = paymentOrderMapper.selectByTimeRange(checkTime, new Date());

// 按用户分组统计支付金额
Map<String, List<PaymentOrder>> userGroups = recentPayments.stream()
.collect(Collectors.groupingBy(PaymentOrder::getUserId));

for (Map.Entry<String, List<PaymentOrder>> entry : userGroups.entrySet()) {
String userId = entry.getKey();
List<PaymentOrder> payments = entry.getValue();

// 计算总支付金额
BigDecimal totalAmount = payments.stream()
.map(PaymentOrder::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);

// 检查是否超过阈值(例如10万元)
BigDecimal threshold = new BigDecimal("100000");

if (totalAmount.compareTo(threshold) > 0) {
createSecurityAlert(userId, "异常支付金额",
"用户" + userId + "在1小时内支付金额超过" + threshold + "元,实际金额: " + totalAmount);
}
}

} catch (Exception e) {
log.error("监控异常支付金额失败", e);
}
}

/**
* 监控支付失败率
*/
@Scheduled(fixedRate = 300000) // 5分钟
public void monitorPaymentFailureRate() {
try {
// 检查最近1小时内的支付
Date checkTime = new Date(System.currentTimeMillis() - 60 * 60 * 1000);

List<PaymentOrder> recentPayments = paymentOrderMapper.selectByTimeRange(checkTime, new Date());

if (recentPayments.isEmpty()) {
return;
}

// 统计失败率
long totalCount = recentPayments.size();
long failedCount = recentPayments.stream()
.filter(payment -> payment.getStatus() == PaymentStatus.FAILED)
.count();

double failureRate = (double) failedCount / totalCount;

// 检查失败率是否超过阈值(例如30%)
if (failureRate > 0.3) {
createSecurityAlert("SYSTEM", "支付失败率异常",
"最近1小时内支付失败率为" + String.format("%.2f%%", failureRate * 100) +
",总支付次数: " + totalCount + ",失败次数: " + failedCount);
}

} catch (Exception e) {
log.error("监控支付失败率失败", e);
}
}

/**
* 创建安全告警
*/
private void createSecurityAlert(String userId, String alertType, String message) {
try {
PaymentSecurityAlert alert = new PaymentSecurityAlert();
alert.setUserId(userId);
alert.setAlertType(alertType);
alert.setMessage(message);
alert.setCreateTime(new Date());
alert.setStatus(AlertStatus.PENDING);

securityAlertMapper.insert(alert);

log.warn("创建安全告警: userId={}, type={}, message={}", userId, alertType, message);

} catch (Exception e) {
log.error("创建安全告警失败", e);
}
}

/**
* 获取安全告警统计
*/
public SecurityAlertStatistics getSecurityAlertStatistics(Date startTime, Date endTime) {
try {
List<PaymentSecurityAlert> alerts = securityAlertMapper.selectByTimeRange(startTime, endTime);

SecurityAlertStatistics statistics = new SecurityAlertStatistics();
statistics.setTotalCount(alerts.size());

// 按类型统计
Map<String, Long> typeStatistics = alerts.stream()
.collect(Collectors.groupingBy(PaymentSecurityAlert::getAlertType,
Collectors.counting()));
statistics.setTypeStatistics(typeStatistics);

// 按状态统计
Map<String, Long> statusStatistics = alerts.stream()
.collect(Collectors.groupingBy(alert -> alert.getStatus().name(),
Collectors.counting()));
statistics.setStatusStatistics(statusStatistics);

return statistics;

} catch (Exception e) {
log.error("获取安全告警统计失败", e);
throw new PaymentException("获取安全告警统计失败", e);
}
}
}

四、性能优化与监控

4.1 性能优化

4.1.1 支付性能优化服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
* 支付性能优化服务
*/
@Service
public class PaymentPerformanceOptimizationService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private CaffeineCache localCache;

private final String PAYMENT_CACHE_PREFIX = "payment_cache:";

/**
* 缓存支付结果
*/
public void cachePaymentResult(String orderId, PaymentResult result) {
String cacheKey = PAYMENT_CACHE_PREFIX + orderId;

try {
// 写入本地缓存
localCache.put(cacheKey, result);

// 写入Redis缓存
String redisCacheKey = "redis_cache:" + cacheKey;
redisTemplate.opsForValue().set(redisCacheKey, result, Duration.ofHours(1));

} catch (Exception e) {
log.error("缓存支付结果失败", e);
}
}

/**
* 获取缓存的支付结果
*/
public PaymentResult getCachedPaymentResult(String orderId) {
String cacheKey = PAYMENT_CACHE_PREFIX + orderId;

try {
// 从本地缓存获取
PaymentResult cachedResult = (PaymentResult) localCache.getIfPresent(cacheKey);
if (cachedResult != null) {
return cachedResult;
}

// 从Redis获取
String redisCacheKey = "redis_cache:" + cacheKey;
PaymentResult redisResult = (PaymentResult) redisTemplate.opsForValue().get(redisCacheKey);
if (redisResult != null) {
localCache.put(cacheKey, redisResult);
return redisResult;
}

} catch (Exception e) {
log.error("获取缓存的支付结果失败", e);
}

return null;
}

/**
* 批量处理支付
*/
public List<PaymentResult> batchProcessPayments(List<PaymentRequest> requests) {
List<PaymentResult> results = new ArrayList<>();

try {
// 按用户分组
Map<String, List<PaymentRequest>> userGroups = requests.stream()
.collect(Collectors.groupingBy(PaymentRequest::getUserId));

// 并行处理各用户组
userGroups.entrySet().parallelStream().forEach(entry -> {
String userId = entry.getKey();
List<PaymentRequest> userRequests = entry.getValue();

try {
List<PaymentResult> userResults = processUserPayments(userId, userRequests);

synchronized (results) {
results.addAll(userResults);
}

} catch (Exception e) {
log.error("处理用户支付失败: {}", userId, e);
}
});

} catch (Exception e) {
log.error("批量处理支付失败", e);
}

return results;
}

/**
* 处理用户支付
*/
private List<PaymentResult> processUserPayments(String userId, List<PaymentRequest> requests) {
List<PaymentResult> results = new ArrayList<>();

for (PaymentRequest request : requests) {
try {
PaymentResult result = processPayment(request);
results.add(result);
} catch (Exception e) {
log.error("处理支付失败: {}", request.getOrderId(), e);
results.add(PaymentResult.failed("处理支付失败: " + e.getMessage()));
}
}

return results;
}

/**
* 处理支付
*/
private PaymentResult processPayment(PaymentRequest request) {
// 实现支付处理逻辑
return PaymentResult.success("支付成功");
}

/**
* 预热支付缓存
*/
@PostConstruct
public void warmupPaymentCache() {
try {
// 预热常用支付结果
List<String> commonOrderIds = Arrays.asList("order_1", "order_2", "order_3");

for (String orderId : commonOrderIds) {
try {
PaymentResult result = PaymentResult.success("预热成功");
cachePaymentResult(orderId, result);
} catch (Exception e) {
log.error("预热支付缓存失败: {}", orderId, e);
}
}

} catch (Exception e) {
log.error("预热支付缓存失败", e);
}
}

/**
* 清理过期缓存
*/
@Scheduled(fixedRate = 300000) // 5分钟
public void cleanupExpiredCache() {
try {
// 清理本地缓存
localCache.cleanUp();

// 清理Redis过期缓存
cleanupRedisExpiredCache();

} catch (Exception e) {
log.error("清理过期缓存失败", e);
}
}

/**
* 清理Redis过期缓存
*/
private void cleanupRedisExpiredCache() {
try {
Set<String> cacheKeys = redisTemplate.keys("redis_cache:" + PAYMENT_CACHE_PREFIX + "*");

for (String key : cacheKeys) {
Long ttl = redisTemplate.getExpire(key);
if (ttl != null && ttl <= 0) {
redisTemplate.delete(key);
}
}

} catch (Exception e) {
log.error("清理Redis过期缓存失败", e);
}
}
}

4.2 监控告警

4.2.1 监控指标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* 支付监控指标
*/
@Component
public class PaymentMetrics {

private final MeterRegistry meterRegistry;

public PaymentMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}

/**
* 记录支付次数
*/
public void recordPaymentCount(String paymentMethod, String status) {
Counter.builder("payment.count")
.description("支付次数")
.tag("payment_method", paymentMethod)
.tag("status", status)
.register(meterRegistry)
.increment();
}

/**
* 记录支付金额
*/
public void recordPaymentAmount(String paymentMethod, BigDecimal amount) {
Counter.builder("payment.amount")
.description("支付金额")
.tag("payment_method", paymentMethod)
.register(meterRegistry)
.increment(amount.doubleValue());
}

/**
* 记录支付时间
*/
public void recordPaymentTime(String paymentMethod, long duration) {
Timer.builder("payment.time")
.description("支付时间")
.tag("payment_method", paymentMethod)
.register(meterRegistry)
.record(duration, TimeUnit.MILLISECONDS);
}

/**
* 记录重复支付次数
*/
public void recordDuplicatePaymentCount(String userId) {
Counter.builder("payment.duplicate.count")
.description("重复支付次数")
.tag("user_id", userId)
.register(meterRegistry)
.increment();
}

/**
* 记录支付失败率
*/
public void recordPaymentFailureRate(double failureRate) {
Gauge.builder("payment.failure.rate")
.description("支付失败率")
.register(meterRegistry, failureRate);
}

/**
* 记录支付成功率
*/
public void recordPaymentSuccessRate(double successRate) {
Gauge.builder("payment.success.rate")
.description("支付成功率")
.register(meterRegistry, successRate);
}

/**
* 记录支付异常次数
*/
public void recordPaymentExceptionCount(String exceptionType) {
Counter.builder("payment.exception.count")
.description("支付异常次数")
.tag("exception_type", exceptionType)
.register(meterRegistry)
.increment();
}
}

4.2.2 告警规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# prometheus-rules.yml
groups:
- name: payment_alerts
rules:
- alert: HighPaymentFailureRate
expr: payment_failure_rate > 0.3
for: 2m
labels:
severity: warning
annotations:
summary: "支付失败率过高"
description: "支付失败率超过30%,当前值: {{ $value }}"

- alert: HighDuplicatePaymentCount
expr: rate(payment_duplicate_count[5m]) > 10
for: 2m
labels:
severity: warning
annotations:
summary: "重复支付次数过多"
description: "重复支付频率超过10次/分钟,当前值: {{ $value }}"

- alert: HighPaymentTime
expr: payment_time{quantile="0.95"} > 5000
for: 2m
labels:
severity: warning
annotations:
summary: "支付时间过长"
description: "支付时间P95超过5秒,当前值: {{ $value }}ms"

- alert: HighPaymentExceptionCount
expr: rate(payment_exception_count[5m]) > 5
for: 2m
labels:
severity: critical
annotations:
summary: "支付异常次数过多"
description: "支付异常频率超过5次/分钟,当前值: {{ $value }}"

- alert: LowPaymentSuccessRate
expr: payment_success_rate < 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "支付成功率过低"
description: "支付成功率低于80%,当前值: {{ $value }}"

五、总结

重复支付作为电商系统中的重要安全问题,通过合理的重复支付防护机制和幂等性设计,能够有效避免用户重复扣款。本文从重复支付防护到幂等性设计,从基础实现到企业级应用,系统梳理了重复支付防护的完整解决方案。

5.1 关键要点

  1. 幂等性控制:通过幂等性令牌确保同一请求只处理一次
  2. 分布式锁:使用分布式锁防止并发支付
  3. 状态机控制:通过状态机确保支付状态转换的合法性
  4. 防重复机制:多层防护机制确保支付安全
  5. 监控告警:建立完善的监控体系,及时发现和处理问题

5.2 最佳实践

  1. 多层防护:前端防重复、网关限流、业务层幂等性控制
  2. 状态管理:使用状态机管理支付状态转换
  3. 异常处理:完善的异常处理和重试机制
  4. 监控告警:建立完善的监控体系,确保支付安全
  5. 性能优化:通过缓存和批量处理提高支付性能

通过以上措施,可以构建一个安全、稳定、可扩展的重复支付防护方案,为企业的电商业务提供安全保障。