State flow in payment scenarios
When developing e-commerce payment modules, we often encounter such state flow requirements:
- Order experience
To be paid
→Paying
→Payment successful/failed
→Refund processing
the full life cycle. - Different operations can be performed under different states (for example, only if the payment is successful can you refund it).
- The status transition needs to trigger an additional operation (released a notification and updated inventory if the payment is successful).
Traditionalif-else
orswitch
Implementation method will result in:
- Blurry code, difficult to maintain
- State transition logic dispersion
- Adding new status requires modifying a large amount of existing code
At this point, State Pattern is the best solution to these problems.
Core idea of state mode
Two key features are achieved by abstracting states into independent classes:
- Behavior state: The same operation in different states produces different results
- Transformation automation: The state object decides the next state on its own
State mode implementation
- State interface design
// Order status abstract interface
public interface IOrderState
{
void Process(OrderContext context);
void Cancel(OrderContext context);
void Refund(OrderContext context);
}
// Order context (maintain the current status)
public class OrderContext
{
private IOrderState _currentState;
public string OrderId { get; } = ().ToString();
public OrderContext(IOrderState initialState)
{
TransitionTo(initialState);
}
public void TransitionTo(IOrderState state)
{
($"Order status change: {_currentState?.GetType().Name} → {().Name}");
_currentState = state;
}
public void ProcessPayment() => _currentState.Process(this);
public void CancelOrder() => _currentState.Cancel(this);
public void RequestRefund() => _currentState.Refund(this);
}
- Specific state class implementation
Status to be paid:
public class PendingState : IOrderState
{
public void Process(OrderContext context)
{
("Start payment processing...");
// Call the payment gateway interface
(new ProcessingState());
}
public void Cancel(OrderContext context)
{
("Order cancelled");
(new CanceledState());
}
public void Refund(OrderContext context)
{
("The order to be paid cannot be refunded");
throw new InvalidOperationException("The order to be paid cannot be refunded");
}
}
Payment status:
public class ProcessingState : IOrderState
{
public void Process(OrderContext context)
{
("Payment processing...");
(new PaidState());
}
public void Cancel(OrderContext context)
{
("In payment processing, orders cannot be cancelled...");
throw new InvalidOperationException("The order cannot be cancelled in payment processing...");
}
public void Refund(OrderContext context)
{
("No refund is allowed if payment is not completed");
throw new InvalidOperationException("No refund is allowed if payment is not completed");
}
}
Payment success status:
public class PaidState : IOrderState
{
public void Process(OrderContext context)
{
("Order completed payment");
throw new InvalidOperationException("Order completed payment");
}
public void Cancel(OrderContext context)
{
("Paid orders require a refund process");
throw new InvalidOperationException("Refund process is required for paid orders");
}
public void Refund(OrderContext context)
{
("Initiate a refund application...");
// Call the refund interface
(new RefundingState());
}
}
Refund status:
public class RefundingState : IOrderState
{
public void Process(OrderContext context)
{
("Refund processing...");
(new RefundedState());
}
public void Cancel(OrderContext context)
{
("Refund processing, orders cannot be cancelled...");
throw new InvalidOperationException("Refund processing, order cannot be cancelled...");
}
public void Refund(OrderContext context)
{
("Refund processing, no recurrence is allowed");
throw new InvalidOperationException("Refund processing, no recurrence is allowed");
}
}
Refund completion status:
public class RefundedState : IOrderState
{
public void Process(OrderContext context)
{
("Refund is completed, order cancelled");
throw new InvalidOperationException("Refund is completed, order has been cancelled");
}
public void Cancel(OrderContext context)
{
("Order refunded, no cancellation is required");
throw new InvalidOperationException("Order refunded, no cancellation is required");
}
public void Refund(OrderContext context)
{
("Order has been refunded, no refund is required");
throw new InvalidOperationException("Order has been refunded, no refund is required");
}
}
Order cancellation status:
public class CanceledState : IOrderState
{
public void Process(OrderContext context)
{
("Order cancelled, no payment is required");
throw new InvalidOperationException("Order cancelled, no payment is required");
}
public void Cancel(OrderContext context)
{
("Order cancelled, no cancellation is required");
throw new InvalidOperationException("Order cancelled, no cancellation is required");
}
public void Refund(OrderContext context)
{
("Order cancelled, no refund is required");
throw new InvalidOperationException("Order cancelled, no refund is required");
}
}
Full state flow example
var order = new OrderContext(new PendingState());
(); // Enter ProcessingState
(); // Output warning message
// Simulated payment successful callback
(new PaidState());
(); // Enter RefundingState
Related code:/huangmingji/design-pattern-learning/tree/main/design-pattern-learning-2