支付系统中状态机的实现步骤

在支付系统中实现状态机,一般需要遵循以下步骤:

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示例展示了支付系统状态机的核心实现:

  1. 状态定义:使用枚举PaymentStatus定义了支付的各种状态
  2. 状态模式:通过PaymentState接口和具体状态类实现状态行为的封装
  3. 上下文管理PaymentContext类管理当前状态并委托状态操作
  4. 事件记录PaymentEventLogger类记录所有状态变更事件
  5. 状态转换规则:每个状态类中实现合法的状态转换逻辑,非法转换会抛出异常

你可以运行PaymentSystem类的main方法查看状态流转的演示。实际应用中,还需要添加数据库持久化、事务管理和异常处理等功能。

  • PaymentStatus.java
1
2
3
4
5
6
7
package com.xdever.demo.payment;

// 支付状态枚举
public enum PaymentStatus {
CREATED, PENDING, SUCCESS, FAILED, REFUNDING, REFUNDED, CLOSED
}

  • PaymentEventType.java
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
}

  • PaymentState.java
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);
}
  • CreatedState.java
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);
}
}

  • PendingState.java
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);
}
}

  • SuccessState.java
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);
}
}

  • FailedState.java
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) {

}
}

  • RefundingState.java
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) {

}
}

  • ClosedState.java
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) {

}
}

  • PaymentEventLogger.java
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);
}
}

  • PaymentContext.java
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();
}

// Getters and setters
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);
}
}

  • PaymentSystem.java
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.processRefund(); // 假设处理退款的方法

// 完成退款
// payment.completeRefund(); // 假设完成退款的方法

// 关闭订单
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