# Server-Side Order Status Flow in mini-shop-server: A Complete Guide

> Explore the server-side order status flow in mini-shop-server. Learn how unpaid, paid, and delivered states transition using integer enums in this comprehensive guide.

- Repository: [A粒麦子/mini-shop-server](https://github.com/allen7d/mini-shop-server)
- Tags: deep-dive
- Published: 2026-02-24

---

**The mini-shop-server implements a linear order status flow using integer enums defined in [`app/libs/enums.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/libs/enums.py), transitioning from `UNPAID` (1) → `PAID` (2) → `DELIVERED` (3), with additional states for out-of-stock handling reserved for future admin workflows.**

The server-side order status flow in mini-shop-server governs how e-commerce transactions progress from creation to fulfillment. This Flask-based application centralizes status definitions in `OrderStatusEnum` and enforces state transitions through service-layer validation in [`app/service/order.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/service/order.py) and [`app/service/pay.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/service/pay.py).

## Order Status Enum Definition

All order states are centralized in [`app/libs/enums.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/libs/enums.py) as integer values to ensure database efficiency and code consistency.

```python

# app/libs/enums.py

class OrderStatusEnum(Enum):
    UNPAID = 1            # 待支付

    PAID = 2              # 已支付

    DELIVERED = 3         # 已发货

    PAID_BUT_OUT_OF = 4   # 已支付，但库存不足

    HANDLED_OUT_OF = 5    # 已处理 PAID_BUT_OUT_OF

```

The **default status** for new orders is `UNPAID` (1), which represents the initial state before any payment processing begins.

## Database Schema and Default Status

The `Order` model in [`app/models/order.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/models/order.py) defines the status column with a default value that aligns with the enum.

```python

# app/models/order.py

order_status = Column(SmallInteger, default=1,
                     comment='订单状态 1:未支付 2:已支付 3:已发货 4:已支付，但库存不足 ')

```

When `OrderService` creates a new instance via `__create_order()`, it relies on this default rather than explicit assignment, ensuring every order begins in the `UNPAID` state.

## Lifecycle State Transitions

The server-side logic enforces a strict linear progression through the order lifecycle, with validation gates at each transition point.

### Order Creation (UNPAID)

The `OrderService.palce()` method (note: method name appears as `palce` in source) initiates the flow by creating an order record after validating stock availability.

```python

# app/service/order.py – __create_order()

order = Order()
order.user_id = self.uid
order.order_no = order_no

# … other fields …

db.session.add(order)

```

Because the model defines `default=1`, the resulting database row has `order_status = 1 (UNPAID)` without explicit assignment in the service code.

### Payment Validation (UNPAID Check)

Before processing payment, `PayService.__check_order_valid()` enforces that the order must be in `UNPAID` status to prevent duplicate charges.

```python

# app/service/pay.py – __check_order_valid()

if order.order_status != OrderStatusEnum.UNPAID:
    raise OrderException(msg='订单已支付', error_code=8003, code=404)

```

This guard ensures that only orders in the initial state can proceed to the external payment gateway. Upon successful payment confirmation (typically handled by a webhook or callback not fully implemented in the current repository), the status would transition to `PAID` (2).

### Delivery Processing (PAID → DELIVERED)

The `OrderService.delivery()` method handles the final transition, moving an order from `PAID` to `DELIVERED` when an admin marks it as shipped.

```python

# app/service/order.py – delivery()

order = Order.query.filter_by(id=order_id).first_or_404()
if order.order_status != OrderStatusEnum.PAID:
    raise OrderException(code=403, error_code=8002,
                         msg='订单未支付，或已经更新过订单了')
order.order_status = OrderStatusEnum.DELIVERED

```

This strict validation prevents shipping orders that have not been paid, maintaining data integrity in the fulfillment workflow.

## Out-of-Stock Handling States

The enum defines two additional states for edge cases: `PAID_BUT_OUT_OF` (4) and `HANDLED_OUT_OF` (5). These represent a scenario where payment succeeds but inventory is subsequently found insufficient.

Currently, the repository defines these states in `OrderStatusEnum` but contains **no automated logic** to transition orders into these states. They serve as placeholders for a future admin workflow where staff manually handle stock shortages after payment confirmation.

## Implementation Examples

### Creating a New Order

```python
from app.service.order import OrderService

uid = 12                      # current user id

cart_items = [{'product_id': 3, 'count': 2},
              {'product_id': 7, 'count': 1}]
order_info = OrderService().palce(uid, cart_items)

# order_info contains order_no, order_id, create_time and pass=True

```

The resulting database record has `order_status = 1 (UNPAID)`.

### Validating Payment Eligibility

```python
from app.service.pay import PayService

pay = PayService(order_id=order_info['order_id'])
pay.pay()                     # raises OrderException if order_status != UNPAID

```

The `__check_order_valid` method ensures only unpaid orders proceed to the payment gateway.

### Marking an Order as Delivered

```python
from app.service.order import OrderService

OrderService.delivery(order_id=order_info['order_id'])

# order_status becomes 3 (DELIVERED)

```

This only succeeds if the order is currently in `PAID` status.

## Summary

- **Status definitions** live in [`app/libs/enums.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/libs/enums.py) as `OrderStatusEnum`, using integer values for database efficiency.
- **Default state** is `UNPAID` (1), set automatically by the `order_status` column default in [`app/models/order.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/models/order.py).
- **Payment validation** occurs in `PayService.__check_order_valid()`, which blocks requests for orders not in `UNPAID` status.
- **Delivery transition** is handled by `OrderService.delivery()`, strictly requiring `PAID` status before updating to `DELIVERED`.
- **Out-of-stock states** (`PAID_BUT_OUT_OF`, `HANDLED_OUT_OF`) are defined but lack automated transition logic in the current codebase.

## Frequently Asked Questions

### How does mini-shop-server prevent duplicate payments for the same order?

The server enforces a status check in `PayService.__check_order_valid()` located in [`app/service/pay.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/service/pay.py). Before initiating any payment gateway interaction, the code verifies that `order.order_status == OrderStatusEnum.UNPAID`. If the status has already transitioned to `PAID` or any other state, an `OrderException` is raised with the message "订单已支付" (order already paid), preventing duplicate transactions.

### What happens if an admin tries to ship an order that hasn't been paid?

The `OrderService.delivery()` method in [`app/service/order.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/service/order.py) explicitly guards against this scenario. It queries the order and checks `if order.order_status != OrderStatusEnum.PAID` before proceeding. If the order is not in `PAID` status, the method raises an `OrderException` with code 403 and the message "订单未支付，或已经更新过订单了" (order not paid or already updated), ensuring only paid orders can enter the `DELIVERED` state.

### Are the out-of-stock order statuses automatically triggered by inventory checks?

No, the statuses `PAID_BUT_OUT_OF` (4) and `HANDLED_OUT_OF` (5) defined in [`app/libs/enums.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/libs/enums.py) are not automatically triggered by any existing logic in the repository. While the enum defines these states for scenarios where payment succeeds but inventory is insufficient, the current codebase in [`app/service/order.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/service/order.py) and [`app/service/pay.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/service/pay.py) contains no code paths that transition orders into these states. They serve as placeholders for future manual admin workflows to handle post-payment stock shortages.

### Which database column stores the order status, and what is its default value?

The order status is stored in the `order_status` column of the `Order` model defined in [`app/models/order.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/models/order.py). The column is defined as `Column(SmallInteger, default=1, comment='订单状态 1:未支付 2:已支付 3:已发货 4:已支付，但库存不足 ')`. This default value of `1` corresponds to `OrderStatusEnum.UNPAID`, ensuring every newly created order starts in the unpaid state without requiring explicit assignment in the creation service logic.