支付新手常犯的十个错误,每一个都是血泪教训

大家好,我是你们的老朋友,一个在支付系统这条路上摸爬滚打了多年的老司机。今天想跟大家聊聊一个让无数后端程序员闻风丧胆的话题——支付系统开发

你是否也遇到过这些崩溃场景:

支付接口刚上线就出现数据错乱,用户投诉不断好不容易搞定微信支付,结果用户付款后订单状态没更新以为支付就是调个接口,结果上线后各种异常情况让人措手不及老板问你支付成功率怎么样,你却拿不出准确的数据

别急,今天我就把这些年踩过的坑、流过的泪全盘托出,让你少走3年弯路!

为什么支付系统这么容易出问题?

在开始正题之前,先聊聊为什么支付系统这么容易出问题:

资金安全要求极高:一分一厘都不能错业务逻辑复杂:涉及订单、库存、账户等多个系统并发处理困难:大促期间高并发,稍有不慎就数据错乱异常情况多样:网络超时、回调丢失、重复通知等等监管合规严格:各种金融法规必须遵守

支付新手常犯的十个错误

错误1:并发更新不加锁,数据错乱成常态

这是新手最容易犯的错误,也是最危险的错误。


// ❌ 错误示例:裸奔式更新
@Service
public class AccountService {
    
    public void updateAccountBalance(Long accountId, BigDecimal amount) {
        // 查询账户
        Account account = accountRepository.findById(accountId);
        
        // 计算新余额
        BigDecimal newBalance = account.getBalance().add(amount);
        
        // 更新余额
        account.setBalance(newBalance);
        accountRepository.save(account);
    }
}

// 悲剧场景:
// 用户账户余额:1000元
// 并发请求1:充值500元
// 并发请求2:充值300元
// 最终结果:账户余额可能是1500元,而不是1800元!

正确姿势:一锁二判三更新


// ✅ 正确示例:一锁二判三更新
@Service
public class SafeAccountService {
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void updateAccountBalance(Long accountId, BigDecimal amount) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    // 一锁:获取数据库行锁
                    Account account = accountRepository.findByIdForUpdate(accountId);
                    
                    // 二判:业务判断
                    if (account == null) {
                        throw new AccountNotFoundException("账户不存在");
                    }
                    
                    if (account.getStatus() != AccountStatus.NORMAL) {
                        throw new InvalidAccountStatusException("账户状态异常");
                    }
                    
                    // 判断余额是否充足(如果是扣款操作)
                    if (amount.compareTo(BigDecimal.ZERO) < 0 && 
                        account.getBalance().add(amount).compareTo(BigDecimal.ZERO) < 0) {
                        throw new InsufficientBalanceException("余额不足");
                    }
                    
                    // 三更新:执行更新操作
                    BigDecimal newBalance = account.getBalance().add(amount);
                    account.setBalance(newBalance);
                    account.setUpdateTime(new Date());
                    accountRepository.save(account);
                    
                    // 记录操作日志
                    accountLogService.recordBalanceChange(accountId, amount, newBalance);
                    
                } catch (Exception e) {
                    // 异常回滚
                    status.setRollbackOnly();
                    log.error("更新账户余额失败", e);
                    throw e;
                }
            }
        });
    }
}

错误2:支付回调不幂等,重复处理酿大祸

支付回调是支付系统中最容易出问题的环节之一。


// ❌ 错误示例:直接处理,可能重复
@RestController
public class PaymentCallbackController {
    
    @PostMapping("/payment/wechat/callback")
    public String handleWechatCallback(@RequestBody String xmlData) {
        // 直接处理支付结果
        processPaymentResult(xmlData);
        return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
    }
    
    private void processPaymentResult(String xmlData) {
        // 解析XML数据
        Map<String, String> resultMap = parseXmlData(xmlData);
        
        String outTradeNo = resultMap.get("out_trade_no"); // 商户订单号
        String transactionId = resultMap.get("transaction_id"); // 微信交易号
        
        // 直接更新订单状态
        orderService.updateOrderStatus(outTradeNo, OrderStatus.PAID, transactionId);
    }
}

// 悲剧场景:
// 微信服务器发送回调通知
// 网络不稳定,微信服务器没收到响应,重复发送回调
// 系统重复处理同一笔订单,可能导致重复发货!

正确姿势:幂等性处理


// ✅ 正确示例:幂等性处理
@RestController
public class SafePaymentCallbackController {
    
    @PostMapping("/payment/wechat/callback")
    public String handleWechatCallback(@RequestBody String xmlData) {
        try {
            // 验证签名
            if (!verifySignature(xmlData)) {
                return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名验证失败]]></return_msg></xml>";
            }
            
            // 幂等性处理
            if (processPaymentResult(xmlData)) {
                return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
            } else {
                return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[处理失败]]></return_msg></xml>";
            }
        } catch (Exception e) {
            log.error("处理微信支付回调异常", e);
            return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[系统异常]]></return_msg></xml>";
        }
    }
    
    private boolean processPaymentResult(String xmlData) {
        // 解析XML数据
        Map<String, String> resultMap = parseXmlData(xmlData);
        
        String outTradeNo = resultMap.get("out_trade_no"); // 商户订单号
        String transactionId = resultMap.get("transaction_id"); // 微信交易号
        String tradeState = resultMap.get("trade_state"); // 交易状态
        
        // 幂等性检查
        if (paymentService.isPaymentProcessed(outTradeNo, transactionId)) {
            log.info("支付已处理,订单号:{}", outTradeNo);
            return true; // 已处理过,直接返回成功
        }
        
        // 根据交易状态处理
        if ("SUCCESS".equals(tradeState)) {
            return paymentService.processSuccessPayment(outTradeNo, transactionId, resultMap);
        } else if ("REFUND".equals(tradeState)) {
            return paymentService.processRefundPayment(outTradeNo, transactionId, resultMap);
        } else if ("CLOSED".equals(tradeState)) {
            return paymentService.processClosedPayment(outTradeNo, transactionId, resultMap);
        } else {
            log.warn("未知的交易状态:{},订单号:{}", tradeState, outTradeNo);
            return false;
        }
    }
}

@Service
public class PaymentService {
    
    // 幂等性检查
    public boolean isPaymentProcessed(String outTradeNo, String transactionId) {
        PaymentRecord record = paymentRecordRepository.findByOutTradeNo(outTradeNo);
        return record != null && 
               record.getTransactionId() != null && 
               record.getTransactionId().equals(transactionId) &&
               record.getStatus() == PaymentStatus.SUCCESS;
    }
    
    // 处理成功支付
    @Transactional
    public boolean processSuccessPayment(String outTradeNo, String transactionId, Map<String, String> resultMap) {
        try {
            // 1. 检查订单是否存在
            Order order = orderRepository.findByOrderNo(outTradeNo);
            if (order == null) {
                log.error("订单不存在,订单号:{}", outTradeNo);
                return false;
            }
            
            // 2. 检查订单状态
            if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
                log.warn("订单状态不正确,订单号:{},当前状态:{}", outTradeNo, order.getStatus());
                return true; // 状态已更新,返回成功
            }
            
            // 3. 更新订单状态
            order.setStatus(OrderStatus.PAID);
            order.setTransactionId(transactionId);
            order.setPaidTime(new Date());
            order.setUpdateTime(new Date());
            orderRepository.save(order);
            
            // 4. 记录支付信息
            PaymentRecord paymentRecord = new PaymentRecord();
            paymentRecord.setOutTradeNo(outTradeNo);
            paymentRecord.setTransactionId(transactionId);
            paymentRecord.setAmount(new BigDecimal(resultMap.get("total_fee")));
            paymentRecord.setStatus(PaymentStatus.SUCCESS);
            paymentRecord.setPayTime(new Date());
            paymentRecordRepository.save(paymentRecord);
            
            // 5. 发送支付成功事件
            paymentEventPublisher.publishPaymentSuccessEvent(order);
            
            log.info("支付处理成功,订单号:{},交易号:{}", outTradeNo, transactionId);
            return true;
            
        } catch (Exception e) {
            log.error("处理支付成功失败,订单号:{}", outTradeNo, e);
            throw e; // 回滚事务
        }
    }
}

错误3:异常处理不完善,系统崩溃无从查

支付系统中的异常处理至关重要,但很多新手往往忽视。


// ❌ 错误示例:异常处理不完善
@Service
public class PaymentService {
    
    public PaymentResult createPayment(Order order) {
        try {
            // 调用第三方支付接口
            ThirdPartyPaymentResponse response = thirdPartyPayService.createPayment(order);
            
            // 直接返回结果
            return PaymentResult.success(response.getPaymentUrl());
        } catch (Exception e) {
            // 简单处理异常
            return PaymentResult.failure("支付失败");
        }
    }
}

// 悲剧场景:
// 网络超时异常被吞掉
// 系统异常没有记录日志
// 问题排查时无从下手

正确姿势:完善的异常处理


// ✅ 正确示例:完善的异常处理
@Service
@Slf4j
public class RobustPaymentService {
    
    @Autowired
    private ThirdPartyPayService thirdPartyPayService;
    
    @Autowired
    private PaymentRecordRepository paymentRecordRepository;
    
    public PaymentResult createPayment(Order order) {
        String traceId = TraceContext.getTraceId();
        
        try {
            log.info("[{}] [PAYMENT_CREATE] 开始创建支付 | orderId={}, amount={}", 
                    traceId, order.getOrderNo(), order.getAmount());
            
            // 1. 参数校验
            validateOrder(order);
            
            // 2. 创建支付记录
            PaymentRecord paymentRecord = createPaymentRecord(order);
            
            // 3. 调用第三方支付接口
            ThirdPartyPaymentResponse response = thirdPartyPayService.createPayment(order);
            
            // 4. 更新支付记录
            updatePaymentRecord(paymentRecord, response);
            
            log.info("[{}] [PAYMENT_CREATE] 支付创建成功 | orderId={}, paymentId={}", 
                    traceId, order.getOrderNo(), paymentRecord.getId());
            
            return PaymentResult.success(response.getPaymentUrl())
                    .setPaymentId(paymentRecord.getId());
                    
        } catch (ValidationException e) {
            log.warn("[{}] [PAYMENT_CREATE] 参数校验失败 | orderId={}, errors={}", 
                    traceId, order.getOrderNo(), e.getErrors());
            return PaymentResult.failure("PARAM_ERROR", e.getMessage());
        } catch (ThirdPartyPaymentException e) {
            log.error("[{}] [PAYMENT_CREATE] 第三方支付异常 | orderId={}, errorCode={}, errorMsg={}", 
                    traceId, order.getOrderNo(), e.getErrorCode(), e.getErrorMsg());
            return PaymentResult.failure(e.getErrorCode(), e.getErrorMsg());
        } catch (DatabaseException e) {
            log.error("[{}] [PAYMENT_CREATE] 数据库异常 | orderId={}", 
                    traceId, order.getOrderNo(), e);
            return PaymentResult.failure("DB_ERROR", "系统繁忙,请稍后重试");
        } catch (Exception e) {
            log.error("[{}] [PAYMENT_CREATE] 系统异常 | orderId={}", 
                    traceId, order.getOrderNo(), e);
            return PaymentResult.failure("SYSTEM_ERROR", "系统异常,请联系客服");
        }
    }
    
    private void validateOrder(Order order) throws ValidationException {
        List<String> errors = new ArrayList<>();
        
        if (order == null) {
            errors.add("订单不能为空");
        } else {
            if (StringUtils.isBlank(order.getOrderNo())) {
                errors.add("订单号不能为空");
            }
            
            if (order.getAmount() == null || order.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
                errors.add("订单金额必须大于0");
            }
            
            if (order.getAmount().compareTo(new BigDecimal("100000")) > 0) {
                errors.add("订单金额不能超过10万元");
            }
        }
        
        if (!errors.isEmpty()) {
            throw new ValidationException(errors);
        }
    }
    
    private PaymentRecord createPaymentRecord(Order order) throws DatabaseException {
        try {
            PaymentRecord paymentRecord = new PaymentRecord();
            paymentRecord.setOrderNo(order.getOrderNo());
            paymentRecord.setAmount(order.getAmount());
            paymentRecord.setStatus(PaymentStatus.PENDING);
            paymentRecord.setCreateTime(new Date());
            paymentRecord.setUpdateTime(new Date());
            
            return paymentRecordRepository.save(paymentRecord);
        } catch (Exception e) {
            throw new DatabaseException("创建支付记录失败", e);
        }
    }
    
    private void updatePaymentRecord(PaymentRecord paymentRecord, ThirdPartyPaymentResponse response) 
            throws DatabaseException {
        try {
            paymentRecord.setThirdPartyPaymentId(response.getPaymentId());
            paymentRecord.setPaymentUrl(response.getPaymentUrl());
            paymentRecord.setUpdateTime(new Date());
            
            paymentRecordRepository.save(paymentRecord);
        } catch (Exception e) {
            throw new DatabaseException("更新支付记录失败", e);
        }
    }
}

错误4:安全措施不到位,用户信息被泄露

支付系统涉及用户敏感信息,安全措施必须到位。


// ❌ 错误示例:安全措施不到位
@RestController
public class UnsafePaymentController {
    
    @PostMapping("/payment")
    public PaymentResult createPayment(@RequestBody PaymentRequest request) {
        // 直接记录用户银行卡信息
        log.info("用户支付请求:{}", request);
        
        // 调用支付接口
        PaymentResult result = paymentService.createPayment(request);
        
        return result;
    }
}

// 悲剧场景:
// 日志中记录了用户银行卡号
// 敏感信息在网络中明文传输
// 违反PCI DSS合规要求

正确姿势:严格的安全措施


// ✅ 正确示例:严格的安全措施
@RestController
@Slf4j
public class SecurePaymentController {
    
    @Autowired
    private PaymentService paymentService;
    
    @PostMapping("/payment")
    public PaymentResult createPayment(@RequestBody @Valid PaymentRequest request) {
        // 1. 敏感信息脱敏
        String maskedCardNo = maskCardNo(request.getCardNo());
        String maskedMobile = maskMobile(request.getMobile());
        
        log.info("用户支付请求 | userId={}, cardNo={}, mobile={}", 
                request.getUserId(), maskedCardNo, maskedMobile);
        
        // 2. 参数校验
        validatePaymentRequest(request);
        
        // 3. 调用支付接口
        PaymentResult result = paymentService.createPayment(request);
        
        // 4. 响应数据脱敏
        return maskResponseData(result);
    }
    
    private String maskCardNo(String cardNo) {
        if (StringUtils.isBlank(cardNo) || cardNo.length() < 8) {
            return cardNo;
        }
        return cardNo.substring(0, 4) + "****" + cardNo.substring(cardNo.length() - 4);
    }
    
    private String maskMobile(String mobile) {
        if (StringUtils.isBlank(mobile) || mobile.length() != 11) {
            return mobile;
        }
        return mobile.substring(0, 3) + "****" + mobile.substring(7);
    }
    
    private void validatePaymentRequest(PaymentRequest request) {
        // 使用Bean Validation进行参数校验
        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<PaymentRequest>> violations = validator.validate(request);
        
        if (!violations.isEmpty()) {
            List<String> errors = violations.stream()
                    .map(v -> v.getPropertyPath() + ": " + v.getMessage())
                    .collect(Collectors.toList());
            throw new ValidationException("参数校验失败: " + String.join(", ", errors));
        }
    }
    
    private PaymentResult maskResponseData(PaymentResult result) {
        // 对响应数据进行脱敏处理
        if (result != null && result.getData() != null) {
            // 根据业务需要对敏感数据进行处理
            result.getData().remove("sensitive_info");
        }
        return result;
    }
}

// 支付请求参数校验
@Data
public class PaymentRequest {
    
    @NotBlank(message = "用户ID不能为空")
    private String userId;
    
    @NotBlank(message = "银行卡号不能为空")
    @Pattern(regexp = "^d{16,19}$", message = "银行卡号格式不正确")
    private String cardNo;
    
    @NotBlank(message = "手机号不能为空")
    @Pattern(regexp = "^1[3-9]d{9}$", message = "手机号格式不正确")
    private String mobile;
    
    @NotNull(message = "支付金额不能为空")
    @DecimalMin(value = "0.01", message = "支付金额必须大于0")
    @DecimalMax(value = "100000", message = "支付金额不能超过10万元")
    private BigDecimal amount;
}

错误5:事务管理不规范,数据一致性无法保证

支付系统对数据一致性要求极高,事务管理必须规范。


// ❌ 错误示例:事务管理不规范
@Service
public class PaymentService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private AccountRepository accountRepository;
    
    public void processPayment(String orderNo, BigDecimal amount) {
        // 更新订单状态
        Order order = orderRepository.findByOrderNo(orderNo);
        order.setStatus(OrderStatus.PAID);
        orderRepository.save(order);
        
        // 扣减用户账户余额
        Account account = accountRepository.findByUserId(order.getUserId());
        account.setBalance(account.getBalance().subtract(amount));
        accountRepository.save(account);
        
        // 如果中间出现异常,数据就不一致了!
    }
}

正确姿势:规范的事务管理


// ✅ 正确示例:规范的事务管理
@Service
@Slf4j
public class TransactionalPaymentService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private AccountRepository accountRepository;
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public PaymentResult processPayment(String orderNo, BigDecimal amount) {
        return transactionTemplate.execute(new TransactionCallback<PaymentResult>() {
            @Override
            public PaymentResult doInTransaction(TransactionStatus status) {
                try {
                    // 1. 查询并锁定订单
                    Order order = orderRepository.findByOrderNoForUpdate(orderNo);
                    if (order == null) {
                        throw new OrderNotFoundException("订单不存在");
                    }
                    
                    if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
                        throw new IllegalOrderStateException("订单状态不正确");
                    }
                    
                    // 2. 查询并锁定账户
                    Account account = accountRepository.findByUserIdForUpdate(order.getUserId());
                    if (account == null) {
                        throw new AccountNotFoundException("账户不存在");
                    }
                    
                    if (account.getBalance().compareTo(amount) < 0) {
                        throw new InsufficientBalanceException("账户余额不足");
                    }
                    
                    // 3. 更新订单状态
                    order.setStatus(OrderStatus.PAID);
                    order.setPaidTime(new Date());
                    order.setUpdateTime(new Date());
                    orderRepository.save(order);
                    
                    // 4. 扣减账户余额
                    account.setBalance(account.getBalance().subtract(amount));
                    account.setUpdateTime(new Date());
                    accountRepository.save(account);
                    
                    // 5. 记录支付日志
                    paymentLogService.recordPayment(orderNo, amount, account.getUserId());
                    
                    log.info("支付处理成功 | orderNo={}, amount={}, userId={}", 
                            orderNo, amount, account.getUserId());
                    
                    return PaymentResult.success();
                    
                } catch (Exception e) {
                    // 标记事务回滚
                    status.setRollbackOnly();
                    log.error("支付处理失败 | orderNo={}", orderNo, e);
                    throw new PaymentProcessingException("支付处理失败", e);
                }
            }
        });
    }
}

错误6:监控告警不完善,问题发现太滞后

支付系统必须有完善的监控告警机制。


// ❌ 错误示例:监控告警不完善
@Service
public class PaymentService {
    
    public PaymentResult processPayment(PaymentRequest request) {
        // 处理支付逻辑
        PaymentResult result = doProcessPayment(request);
        
        // 没有监控和告警
        return result;
    }
}

正确姿势:完善的监控告警


// ✅ 正确示例:完善的监控告警
@Service
@Slf4j
public class MonitoredPaymentService {
    
    @Autowired
    private MeterRegistry meterRegistry;
    
    @Autowired
    private AlertService alertService;
    
    public PaymentResult processPayment(PaymentRequest request) {
        String traceId = TraceContext.getTraceId();
        long startTime = System.currentTimeMillis();
        
        try {
            log.info("[{}] [PAYMENT_PROCESS] 开始处理支付 | userId={}, amount={}", 
                    traceId, request.getUserId(), request.getAmount());
            
            // 处理支付逻辑
            PaymentResult result = doProcessPayment(request);
            
            long cost = System.currentTimeMillis() - startTime;
            
            // 记录成功指标
            recordSuccessMetrics(request, cost);
            
            log.info("[{}] [PAYMENT_PROCESS] 支付处理成功 | userId={}, amount={}, cost={}ms", 
                    traceId, request.getUserId(), request.getAmount(), cost);
            
            return result;
            
        } catch (Exception e) {
            long cost = System.currentTimeMillis() - startTime;
            
            // 记录失败指标
            recordFailureMetrics(request, e, cost);
            
            log.error("[{}] [PAYMENT_PROCESS] 支付处理失败 | userId={}, amount={}, cost={}ms", 
                    traceId, request.getUserId(), request.getAmount(), cost, e);
            
            // 发送告警
            sendAlert(request, e);
            
            throw e;
        }
    }
    
    private void recordSuccessMetrics(PaymentRequest request, long cost) {
        // 记录支付成功次数
        Counter.builder("payment.success")
                .description("支付成功次数")
                .tag("payment_method", request.getPaymentMethod())
                .tag("currency", request.getCurrency())
                .register(meterRegistry)
                .increment();
        
        // 记录支付响应时间
        Timer.builder("payment.response.time")
                .description("支付响应时间")
                .tag("payment_method", request.getPaymentMethod())
                .register(meterRegistry)
                .record(cost, TimeUnit.MILLISECONDS);
        
        // 记录支付金额分布
        DistributionSummary.builder("payment.amount")
                .description("支付金额分布")
                .tag("payment_method", request.getPaymentMethod())
                .register(meterRegistry)
                .record(request.getAmount().doubleValue());
    }
    
    private void recordFailureMetrics(PaymentRequest request, Exception e, long cost) {
        // 记录支付失败次数
        Counter.builder("payment.failure")
                .description("支付失败次数")
                .tag("payment_method", request.getPaymentMethod())
                .tag("error_type", e.getClass().getSimpleName())
                .register(meterRegistry)
                .increment();
        
        // 记录失败响应时间
        Timer.builder("payment.failure.time")
                .description("支付失败响应时间")
                .tag("payment_method", request.getPaymentMethod())
                .tag("error_type", e.getClass().getSimpleName())
                .register(meterRegistry)
                .record(cost, TimeUnit.MILLISECONDS);
    }
    
    private void sendAlert(PaymentRequest request, Exception e) {
        // 支付失败率告警
        if (shouldSendAlert()) {
            Alert alert = Alert.builder()
                    .level(AlertLevel.HIGH)
                    .title("支付系统异常")
                    .content(String.format("支付失败,用户ID:%s,金额:%s,错误:%s", 
                            request.getUserId(), request.getAmount(), e.getMessage()))
                    .build();
            
            alertService.sendAlert(alert);
        }
    }
    
    private boolean shouldSendAlert() {
        // 根据失败率等指标判断是否需要发送告警
        // 这里简化处理
        return true;
    }
}

错误7:测试不充分,上线后问题频发

支付系统的测试必须充分,包括各种异常场景。


// ❌ 错误示例:测试不充分
@Test
public class PaymentServiceTest {
    
    @Test
    public void testCreatePayment() {
        // 只测试正常情况
        PaymentResult result = paymentService.createPayment(order);
        assertTrue(result.isSuccess());
    }
}

正确姿势:充分的测试


// ✅ 正确示例:充分的测试
@SpringBootTest
@TestPropertySource(properties = {
    "spring.datasource.url=jdbc:h2:mem:testdb",
    "spring.jpa.hibernate.ddl-auto=create-drop"
})
class ComprehensivePaymentServiceTest {
    
    @Autowired
    private PaymentService paymentService;
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private AccountRepository accountRepository;
    
    @BeforeEach
    void setUp() {
        // 初始化测试数据
        createTestOrder();
        createTestAccount();
    }
    
    @Test
    @DisplayName("正常支付流程测试")
    void testNormalPaymentProcess() {
        PaymentRequest request = createNormalPaymentRequest();
        
        PaymentResult result = paymentService.processPayment(request);
        
        assertTrue(result.isSuccess());
        assertEquals("支付成功", result.getMessage());
        
        // 验证订单状态
        Order order = orderRepository.findByOrderNo(request.getOrderNo());
        assertEquals(OrderStatus.PAID, order.getStatus());
        
        // 验证账户余额
        Account account = accountRepository.findByUserId(request.getUserId());
        assertEquals(new BigDecimal("900.00"), account.getBalance());
    }
    
    @Test
    @DisplayName("余额不足支付失败测试")
    void testInsufficientBalancePayment() {
        PaymentRequest request = createInsufficientBalancePaymentRequest();
        
        PaymentProcessingException exception = assertThrows(
                PaymentProcessingException.class, 
                () -> paymentService.processPayment(request));
        
        assertEquals("账户余额不足", exception.getMessage());
        
        // 验证订单状态未改变
        Order order = orderRepository.findByOrderNo(request.getOrderNo());
        assertEquals(OrderStatus.PENDING_PAYMENT, order.getStatus());
        
        // 验证账户余额未改变
        Account account = accountRepository.findByUserId(request.getUserId());
        assertEquals(new BigDecimal("1000.00"), account.getBalance());
    }
    
    @Test
    @DisplayName("并发支付测试")
    void testConcurrentPayment() throws InterruptedException {
        int threadCount = 10;
        CountDownLatch latch = new CountDownLatch(threadCount);
        List<PaymentResult> results = Collections.synchronizedList(new ArrayList<>());
        
        // 同时发起多个支付请求
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                try {
                    PaymentRequest request = createNormalPaymentRequest();
                    PaymentResult result = paymentService.processPayment(request);
                    results.add(result);
                } catch (Exception e) {
                    // 记录异常
                    results.add(PaymentResult.failure(e.getMessage()));
                } finally {
                    latch.countDown();
                }
            }).start();
        }
        
        // 等待所有线程完成
        latch.await(30, TimeUnit.SECONDS);
        
        // 验证只有一个支付成功
        long successCount = results.stream().filter(PaymentResult::isSuccess).count();
        assertEquals(1, successCount, "应该只有一个支付成功");
        
        // 验证只有一个订单状态更新
        Order order = orderRepository.findByOrderNo("TEST_ORDER_001");
        if (successCount == 1) {
            assertEquals(OrderStatus.PAID, order.getStatus());
        } else {
            assertEquals(OrderStatus.PENDING_PAYMENT, order.getStatus());
        }
    }
    
    @Test
    @DisplayName("支付回调重复处理测试")
    void testDuplicatePaymentCallback() {
        String outTradeNo = "TEST_ORDER_001";
        String transactionId = "WX_000001";
        
        // 第一次处理
        boolean firstResult = paymentService.processPaymentCallback(outTradeNo, transactionId);
        assertTrue(firstResult, "第一次处理应该成功");
        
        // 第二次处理(重复)
        boolean secondResult = paymentService.processPaymentCallback(outTradeNo, transactionId);
        assertTrue(secondResult, "重复处理应该也成功");
        
        // 验证订单状态
        Order order = orderRepository.findByOrderNo(outTradeNo);
        assertEquals(OrderStatus.PAID, order.getStatus());
        
        // 验证只有一条支付记录
        List<PaymentRecord> records = paymentRecordRepository.findByOutTradeNo(outTradeNo);
        assertEquals(1, records.size());
    }
    
    private PaymentRequest createNormalPaymentRequest() {
        PaymentRequest request = new PaymentRequest();
        request.setUserId("USER_001");
        request.setOrderNo("TEST_ORDER_001");
        request.setAmount(new BigDecimal("100.00"));
        request.setPaymentMethod("BALANCE");
        return request;
    }
    
    private PaymentRequest createInsufficientBalancePaymentRequest() {
        PaymentRequest request = new PaymentRequest();
        request.setUserId("USER_001");
        request.setOrderNo("TEST_ORDER_001");
        request.setAmount(new BigDecimal("2000.00"); // 超过账户余额
        request.setPaymentMethod("BALANCE");
        return request;
    }
    
    private void createTestOrder() {
        Order order = new Order();
        order.setOrderNo("TEST_ORDER_001");
        order.setUserId("USER_001");
        order.setAmount(new BigDecimal("100.00"));
        order.setStatus(OrderStatus.PENDING_PAYMENT);
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        orderRepository.save(order);
    }
    
    private void createTestAccount() {
        Account account = new Account();
        account.setUserId("USER_001");
        account.setBalance(new BigDecimal("1000.00"));
        account.setStatus(AccountStatus.NORMAL);
        account.setCreateTime(new Date());
        account.setUpdateTime(new Date());
        accountRepository.save(account);
    }
}

错误8:日志记录不规范,问题排查困难

支付系统的日志记录必须规范,便于问题排查。


// ❌ 错误示例:日志记录不规范
@Service
public class PaymentService {
    
    public void processPayment(PaymentRequest request) {
        System.out.println("开始处理支付");
        // 处理逻辑
        System.out.println("支付处理完成");
    }
}

正确姿势:规范的日志记录


// ✅ 正确示例:规范的日志记录
@Service
@Slf4j
public class WellLoggedPaymentService {
    
    public PaymentResult processPayment(PaymentRequest request) {
        String traceId = TraceContext.getTraceId();
        
        // 入口日志
        log.info("[{}] [PAYMENT_PROCESS] 开始处理支付 | userId={}, orderNo={}, amount={}, paymentMethod={}", 
                traceId, request.getUserId(), request.getOrderNo(), 
                request.getAmount(), request.getPaymentMethod());
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 参数校验日志
            log.debug("[{}] [PARAM_VALIDATE] 开始参数校验", traceId);
            validateRequest(request);
            log.debug("[{}] [PARAM_VALIDATE] 参数校验通过", traceId);
            
            // 业务处理日志
            log.debug("[{}] [BUSINESS_PROCESS] 开始业务处理", traceId);
            PaymentResult result = doBusinessProcess(request);
            log.debug("[{}] [BUSINESS_PROCESS] 业务处理完成", traceId);
            
            long cost = System.currentTimeMillis() - startTime;
            
            // 成功日志
            log.info("[{}] [PAYMENT_PROCESS] 支付处理成功 | userId={}, orderNo={}, amount={}, cost={}ms", 
                    traceId, request.getUserId(), request.getOrderNo(), 
                    request.getAmount(), cost);
            
            return result;
            
        } catch (ValidationException e) {
            long cost = System.currentTimeMillis() - startTime;
            log.warn("[{}] [PAYMENT_PROCESS] 参数校验失败 | userId={}, errors={}, cost={}ms", 
                    traceId, request.getUserId(), e.getErrors(), cost);
            return PaymentResult.failure("PARAM_ERROR", e.getMessage());
        } catch (BusinessException e) {
            long cost = System.currentTimeMillis() - startTime;
            log.warn("[{}] [PAYMENT_PROCESS] 业务异常 | userId={}, code={}, message={}, cost={}ms", 
                    traceId, request.getUserId(), e.getCode(), e.getMessage(), cost);
            return PaymentResult.failure(e.getCode(), e.getMessage());
        } catch (Exception e) {
            long cost = System.currentTimeMillis() - startTime;
            log.error("[{}] [PAYMENT_PROCESS] 系统异常 | userId={}, orderNo={}, error={}, cost={}ms", 
                    traceId, request.getUserId(), request.getOrderNo(), e.getMessage(), cost, e);
            return PaymentResult.failure("SYSTEM_ERROR", "系统繁忙");
        }
    }
    
    private void validateRequest(PaymentRequest request) throws ValidationException {
        List<String> errors = new ArrayList<>();
        
        if (request.getAmount() == null || request.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            errors.add("支付金额必须大于0");
        }
        
        if (errors.isEmpty()) {
            return;
        }
        
        throw new ValidationException(errors);
    }
    
    private PaymentResult doBusinessProcess(PaymentRequest request) throws BusinessException {
        // 模拟业务处理
        if (request.getAmount().compareTo(new BigDecimal("10000")) > 0) {
            throw new BusinessException("AMOUNT_TOO_LARGE", "支付金额过大");
        }
        
        return PaymentResult.success();
    }
}

错误9:配置管理混乱,生产环境出问题

支付系统的配置管理必须规范,避免生产环境出问题。


// ❌ 错误示例:配置管理混乱
@Service
public class PaymentService {
    
    private String paymentGatewayUrl = "http://localhost:8080/payment"; // 开发环境URL
    
    public void processPayment(PaymentRequest request) {
        // 使用硬编码的URL,生产环境会出问题
        callPaymentGateway(paymentGatewayUrl, request);
    }
}

正确姿势:规范的配置管理


// ✅ 正确示例:规范的配置管理
@Service
public class ConfigurablePaymentService {
    
    @Value("${payment.gateway.url}")
    private String paymentGatewayUrl;
    
    @Value("${payment.gateway.timeout:5000}")
    private int paymentGatewayTimeout;
    
    @Value("${payment.gateway.retry.times:3}")
    private int retryTimes;
    
    @Autowired
    private RestTemplate restTemplate;
    
    public PaymentResult processPayment(PaymentRequest request) {
        log.info("支付网关配置 | url={}, timeout={}, retryTimes={}", 
                paymentGatewayUrl, paymentGatewayTimeout, retryTimes);
        
        return callPaymentGatewayWithRetry(request);
    }
    
    private PaymentResult callPaymentGatewayWithRetry(PaymentRequest request) {
        for (int i = 0; i < retryTimes; i++) {
            try {
                return callPaymentGateway(request);
            } catch (Exception e) {
                log.warn("调用支付网关失败,第{}次重试", i + 1, e);
                
                if (i == retryTimes - 1) {
                    // 最后一次重试失败,抛出异常
                    throw new PaymentGatewayException("调用支付网关失败", e);
                }
                
                // 等待后重试
                try {
                    Thread.sleep(1000 * (i + 1)); // 递增等待时间
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new PaymentGatewayException("重试被中断", ie);
                }
            }
        }
        
        throw new PaymentGatewayException("调用支付网关失败");
    }
    
    private PaymentResult callPaymentGateway(PaymentRequest request) {
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("X-API-Key", getApiKey());
            
            HttpEntity<PaymentRequest> entity = new HttpEntity<>(request, headers);
            
            ResponseEntity<PaymentResult> response = restTemplate.exchange(
                    paymentGatewayUrl, 
                    HttpMethod.POST, 
                    entity, 
                    PaymentResult.class);
            
            return response.getBody();
        } catch (Exception e) {
            log.error("调用支付网关异常 | url={}, request={}", paymentGatewayUrl, request, e);
            throw new PaymentGatewayException("调用支付网关异常", e);
        }
    }
    
    private String getApiKey() {
        // 从安全存储中获取API密钥
        return secureConfigService.getPaymentApiKey();
    }
}

// 配置文件
/*
# application.yml
payment:
  gateway:
    url: ${PAYMENT_GATEWAY_URL:http://localhost:8080/payment}
    timeout: ${PAYMENT_GATEWAY_TIMEOUT:5000}
    retry:
      times: ${PAYMENT_GATEWAY_RETRY_TIMES:3}
*/

// 配置类
@Configuration
@ConfigurationProperties(prefix = "payment.gateway")
@Data
public class PaymentGatewayConfig {
    
    private String url;
    private int timeout = 5000;
    private Retry retry = new Retry();
    
    @Data
    public static class Retry {
        private int times = 3;
    }
}

错误10:缺乏容错机制,系统脆弱易崩溃

支付系统必须有完善的容错机制,保证系统稳定性。


// ❌ 错误示例:缺乏容错机制
@Service
public class PaymentService {
    
    public PaymentResult processPayment(PaymentRequest request) {
        // 直接调用第三方服务,没有容错机制
        return thirdPartyPaymentService.process(request);
    }
}

正确姿势:完善的容错机制


// ✅ 正确示例:完善的容错机制
@Service
@Slf4j
public class FaultTolerantPaymentService {
    
    @Autowired
    private ThirdPartyPaymentService thirdPartyPaymentService;
    
    @Autowired
    private CircuitBreaker circuitBreaker;
    
    @Autowired
    private FallbackPaymentService fallbackPaymentService;
    
    public PaymentResult processPayment(PaymentRequest request) {
        String traceId = TraceContext.getTraceId();
        
        try {
            // 使用熔断器调用第三方支付服务
            return circuitBreaker.executeSupplier(() -> {
                log.debug("[{}] [THIRD_PARTY_CALL] 调用第三方支付服务", traceId);
                PaymentResult result = thirdPartyPaymentService.process(request);
                log.debug("[{}] [THIRD_PARTY_CALL] 第三方支付服务调用成功", traceId);
                return result;
            });
        } catch (CircuitBreakerOpenException e) {
            // 熔断器打开,使用降级方案
            log.warn("[{}] [CIRCUIT_BREAKER] 熔断器打开,使用降级方案", traceId);
            return handleFallback(request);
        } catch (Exception e) {
            // 其他异常,记录日志并使用降级方案
            log.error("[{}] [THIRD_PARTY_CALL] 调用第三方支付服务异常", traceId, e);
            return handleFallback(request);
        }
    }
    
    private PaymentResult handleFallback(PaymentRequest request) {
        try {
            // 尝试使用降级支付服务
            log.info("[{}] [FALLBACK] 使用降级支付服务", TraceContext.getTraceId());
            return fallbackPaymentService.process(request);
        } catch (Exception fallbackException) {
            // 降级服务也失败,记录错误并返回失败结果
            log.error("[{}] [FALLBACK] 降级支付服务也失败", TraceContext.getTraceId(), fallbackException);
            return PaymentResult.failure("ALL_PAYMENT_SERVICES_DOWN", "所有支付服务都不可用");
        }
    }
}

// 熔断器配置
@Configuration
public class CircuitBreakerConfig {
    
    @Bean
    public CircuitBreaker circuitBreaker() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                .failureRateThreshold(50) // 失败率阈值50%
                .waitDurationInOpenState(Duration.ofSeconds(30)) // 熔断器打开时长30秒
                .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
                .slidingWindowSize(10) // 滑动窗口大小10个请求
                .minimumNumberOfCalls(5) // 最小请求数5个
                .build();
        
        return CircuitBreaker.of("payment-service", config);
    }
}

// 降级支付服务
@Service
@Slf4j
public class FallbackPaymentService {
    
    public PaymentResult process(PaymentRequest request) {
        // 实现降级逻辑,比如:
        // 1. 使用备用支付渠道
        // 2. 记录支付请求,后续人工处理
        // 3. 返回特殊状态,让用户稍后重试
        
        log.info("使用降级支付服务处理支付请求 | userId={}, amount={}", 
                request.getUserId(), request.getAmount());
        
        // 这里简化处理,实际应该根据业务需求实现
        return PaymentResult.failure("SERVICE_TEMPORARILY_UNAVAILABLE", "服务暂时不可用,请稍后重试");
    }
}

结语

支付系统开发是一个复杂而严谨的过程,每一个细节都关系到用户的资金安全和公司的声誉。今天分享的这10个常见错误,每一个都是血泪教训,希望能让大家少走弯路。

记住这几点:

并发安全是底线:一锁二判三更新,数据一致性不容妥协幂等性是关键:支付回调必须做幂等处理,避免重复处理异常处理要完善:各种异常场景都要考虑,不能让异常破坏数据一致性安全措施要到位:敏感信息必须脱敏,符合合规要求事务管理要规范:跨表操作必须使用事务,保证数据一致性监控告警要完善:及时发现问题,快速响应处理测试要充分:各种场景都要测试,特别是并发和异常场景日志要规范:便于问题排查和审计配置要规范:避免生产环境配置错误容错机制要完善:保证系统稳定性和可用性

支付系统无小事,每一行代码都关系到用户的资金安全。在追求功能快速上线的同时,一定要把稳定性和安全性放在首位。

如果你觉得这篇文章对你有帮助,欢迎点赞、在看、转发三连,你的支持是我们持续创作的最大动力!


服务端技术精选 | 专注分享实用的后端技术干货和实战经验

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容