在支付系统中实现状态机,一般需要遵循以下步骤:
1. 明确业务流程与状态定义
首先要梳理清楚支付系统中各类业务的流程,明确交易或订单可能出现的所有状态。以一个典型的在线支付场景为例,可能涉及的状态有:
CREATED(已创建)
PENDING(处理中)
SUCCESS(支付成功)
FAILED(支付失败)
REFUNDING(退款中)
REFUNDED(已退款)
CLOSED(已关闭)
同时,也要确定状态之间合法的转换规则,比如:
- 处于
CREATED状态的订单,在发起支付后可以转变为PENDING状态。
PENDING状态的订单,支付成功后会变为SUCCESS状态,若支付失败则转为FAILED状态。
SUCCESS状态的订单,申请退款后会进入REFUNDING状态,退款完成则变为REFUNDED状态。
2. 状态持久化与事件溯源
在实际的支付系统中,需要将状态持久化到数据库,以确保系统重启后状态不会丢失。同时,为了保证数据的一致性和可追溯性,可以考虑引入事件溯源模式,记录所有状态变更事件。
3. 状态监控与异常处理
- 状态监控:要实现完善的日志记录和监控系统,实时追踪状态的变化情况。一旦出现异常状态,能够及时发出警报。
- 超时处理:对于处于中间状态(如
PENDING)的交易,要设置合理的超时时间。超时后自动执行相应的处理逻辑,如关闭订单或发起退款。
- 幂等设计:确保状态转换操作具有幂等性,防止因重复调用而导致状态不一致的问题。
4. 实际案例参考
- 电商平台:订单支付状态机(待支付→支付中→已支付→已发货→已完成)。
- 金融系统:账户充值状态机(充值请求→处理中→成功 / 失败→记账)。
- 第三方支付:支付宝交易状态(WAIT_BUYER_PAY→TRADE_SUCCESS→TRADE_CLOSED)。
以下是一个使用Java实现支付系统状态机的示例,采用状态模式和枚举来定义状态转换规则:
这个Java示例展示了支付系统状态机的核心实现:
- 状态定义:使用枚举
PaymentStatus定义了支付的各种状态
- 状态模式:通过
PaymentState接口和具体状态类实现状态行为的封装
- 上下文管理:
PaymentContext类管理当前状态并委托状态操作
- 事件记录:
PaymentEventLogger类记录所有状态变更事件
- 状态转换规则:每个状态类中实现合法的状态转换逻辑,非法转换会抛出异常
你可以运行PaymentSystem类的main方法查看状态流转的演示。实际应用中,还需要添加数据库持久化、事务管理和异常处理等功能。
1 2 3 4 5 6 7
| package com.xdever.demo.payment;
public enum PaymentStatus { CREATED, PENDING, SUCCESS, FAILED, REFUNDING, REFUNDED, CLOSED }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.xdever.demo.payment;
public enum PaymentEventType { PAYMENT_INITIATED, PAYMENT_SUCCESS, PAYMENT_FAILED, REFUND_INITIATED, REFUND_SUCCESS, REFUND_FAILED, ORDER_CLOSED }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.xdever.demo.payment;
public interface PaymentState { void processPayment(PaymentContext context);
void handleSuccess(PaymentContext context);
void handleFailure(PaymentContext context);
void initiateRefund(PaymentContext context);
void processRefund(PaymentContext context);
void completeRefund(PaymentContext context);
void closePayment(PaymentContext context); }
|
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
| package com.xdever.demo.payment;
class CreatedState implements PaymentState { @Override public void processPayment(PaymentContext context) { context.setStatus(PaymentStatus.PENDING); context.setCurrentState(new PendingState()); System.out.println("支付处理中: " + context.getPaymentId()); PaymentEventLogger.logEvent(context.getPaymentId(), PaymentEventType.PAYMENT_INITIATED); }
@Override public void handleSuccess(PaymentContext context) { throw new IllegalStateException("支付尚未处理,无法标记为成功"); }
@Override public void handleFailure(PaymentContext context) { throw new IllegalStateException("支付尚未处理,无法标记为失败"); }
@Override public void initiateRefund(PaymentContext context) { throw new IllegalStateException("支付尚未处理,无法发起退款"); }
@Override public void processRefund(PaymentContext context) { throw new IllegalStateException("支付尚未处理,无法处理退款"); }
@Override public void completeRefund(PaymentContext context) { throw new IllegalStateException("支付尚未处理,无法完成退款"); }
@Override public void closePayment(PaymentContext context) { context.setStatus(PaymentStatus.CLOSED); context.setCurrentState(new ClosedState()); System.out.println("订单已关闭: " + context.getPaymentId()); PaymentEventLogger.logEvent(context.getPaymentId(), PaymentEventType.ORDER_CLOSED); } }
|
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
| package com.xdever.demo.payment;
class PendingState implements PaymentState { @Override public void processPayment(PaymentContext context) { throw new IllegalStateException("支付已在处理中"); }
@Override public void handleSuccess(PaymentContext context) { context.setStatus(PaymentStatus.SUCCESS); context.setCurrentState(new SuccessState()); System.out.println("支付成功: " + context.getPaymentId()); PaymentEventLogger.logEvent(context.getPaymentId(), PaymentEventType.PAYMENT_SUCCESS); }
@Override public void handleFailure(PaymentContext context) { context.setStatus(PaymentStatus.FAILED); context.setCurrentState(new FailedState()); System.out.println("支付失败: " + context.getPaymentId()); PaymentEventLogger.logEvent(context.getPaymentId(), PaymentEventType.PAYMENT_FAILED); }
@Override public void initiateRefund(PaymentContext context) { throw new IllegalStateException("支付未完成,无法发起退款"); }
@Override public void processRefund(PaymentContext context) { throw new IllegalStateException("支付未完成,无法处理退款"); }
@Override public void completeRefund(PaymentContext context) { throw new IllegalStateException("支付未完成,无法完成退款"); }
@Override public void closePayment(PaymentContext context) { context.setStatus(PaymentStatus.CLOSED); context.setCurrentState(new ClosedState()); System.out.println("订单已关闭: " + context.getPaymentId()); PaymentEventLogger.logEvent(context.getPaymentId(), PaymentEventType.ORDER_CLOSED); } }
|
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
| package com.xdever.demo.payment;
class SuccessState implements PaymentState { @Override public void processPayment(PaymentContext context) { throw new IllegalStateException("支付已成功,不能重复处理"); }
@Override public void handleSuccess(PaymentContext context) { throw new IllegalStateException("支付已成功"); }
@Override public void handleFailure(PaymentContext context) { throw new IllegalStateException("支付已成功,不能标记为失败"); }
@Override public void initiateRefund(PaymentContext context) { context.setStatus(PaymentStatus.REFUNDING); context.setCurrentState(new RefundingState()); System.out.println("退款处理中: " + context.getPaymentId()); PaymentEventLogger.logEvent(context.getPaymentId(), PaymentEventType.REFUND_INITIATED); }
@Override public void processRefund(PaymentContext context) { throw new IllegalStateException("请先发起退款"); }
@Override public void completeRefund(PaymentContext context) { throw new IllegalStateException("请先发起退款"); }
@Override public void closePayment(PaymentContext context) { context.setStatus(PaymentStatus.CLOSED); context.setCurrentState(new ClosedState()); System.out.println("订单已关闭: " + context.getPaymentId()); PaymentEventLogger.logEvent(context.getPaymentId(), PaymentEventType.ORDER_CLOSED); } }
|
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
| package com.xdever.demo.payment;
public class FailedState implements PaymentState { @Override public void processPayment(PaymentContext context) {
}
@Override public void handleSuccess(PaymentContext context) {
}
@Override public void handleFailure(PaymentContext context) {
}
@Override public void initiateRefund(PaymentContext context) {
}
@Override public void processRefund(PaymentContext context) {
}
@Override public void completeRefund(PaymentContext context) {
}
@Override public void closePayment(PaymentContext context) {
} }
|
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
| package com.xdever.demo.payment;
public class RefundingState implements PaymentState { @Override public void processPayment(PaymentContext context) {
}
@Override public void handleSuccess(PaymentContext context) {
}
@Override public void handleFailure(PaymentContext context) {
}
@Override public void initiateRefund(PaymentContext context) {
}
@Override public void processRefund(PaymentContext context) {
}
@Override public void completeRefund(PaymentContext context) {
}
@Override public void closePayment(PaymentContext context) {
} }
|
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
| package com.xdever.demo.payment;
public class ClosedState implements PaymentState { @Override public void processPayment(PaymentContext context) { }
@Override public void handleSuccess(PaymentContext context) {
}
@Override public void handleFailure(PaymentContext context) {
}
@Override public void initiateRefund(PaymentContext context) {
}
@Override public void processRefund(PaymentContext context) {
}
@Override public void completeRefund(PaymentContext context) {
}
@Override public void closePayment(PaymentContext context) {
} }
|
1 2 3 4 5 6 7 8 9 10 11 12
| package com.xdever.demo.payment;
import java.time.LocalDateTime;
public class PaymentEventLogger { public static void logEvent(String paymentId, PaymentEventType eventType) { System.out.printf("[%s] 支付ID: %s, 事件: %s%n", LocalDateTime.now(), paymentId, eventType); } }
|
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
| package com.xdever.demo.payment;
import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.UUID;
public class PaymentContext { private String paymentId; private BigDecimal amount; private PaymentStatus status; private PaymentState currentState; private String transactionId; private LocalDateTime createTime; private LocalDateTime updateTime;
public PaymentContext(BigDecimal amount) { this.paymentId = UUID.randomUUID().toString(); this.amount = amount; this.status = PaymentStatus.CREATED; this.currentState = new CreatedState(); this.createTime = LocalDateTime.now(); this.updateTime = LocalDateTime.now(); }
public String getPaymentId() { return paymentId; }
public BigDecimal getAmount() { return amount; }
public PaymentStatus getStatus() { return status; }
public void setStatus(PaymentStatus status) { this.status = status; this.updateTime = LocalDateTime.now(); }
public PaymentState getCurrentState() { return currentState; }
public void setCurrentState(PaymentState currentState) { this.currentState = currentState; }
public String getTransactionId() { return transactionId; }
public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
public LocalDateTime getCreateTime() { return createTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void processPayment() { currentState.processPayment(this); }
public void handleSuccess() { currentState.handleSuccess(this); }
public void handleFailure() { currentState.handleFailure(this); }
public void initiateRefund() { currentState.initiateRefund(this); }
public void processRefund() { currentState.processRefund(this); }
public void completeRefund() { currentState.completeRefund(this); }
public void closePayment() { currentState.closePayment(this); } }
|
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
| package com.xdever.demo.payment;
import java.math.BigDecimal;
public class PaymentSystem { public static void main(String[] args) { PaymentContext payment = new PaymentContext(new BigDecimal("100.00")); System.out.println("创建支付订单: " + payment.getPaymentId());
payment.processPayment();
payment.handleSuccess();
payment.initiateRefund();
payment.closePayment(); } }
|
1 2 3 4 5 6 7
| 创建支付订单: d612175d-2cc5-469f-b273-9db2af937ede 支付处理中: d612175d-2cc5-469f-b273-9db2af937ede [2025-07-08T23:14:09.524693800] 支付ID: d612175d-2cc5-469f-b273-9db2af937ede, 事件: PAYMENT_INITIATED 支付成功: d612175d-2cc5-469f-b273-9db2af937ede [2025-07-08T23:14:09.540408300] 支付ID: d612175d-2cc5-469f-b273-9db2af937ede, 事件: PAYMENT_SUCCESS 退款处理中: d612175d-2cc5-469f-b273-9db2af937ede [2025-07-08T23:14:09.540408300] 支付ID: d612175d-2cc5-469f-b273-9db2af937ede, 事件: REFUND_INITIATED
|