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

The mini-shop-server implements a linear order status flow using integer enums defined in 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 and app/service/pay.py.

Order Status Enum Definition

All order states are centralized in app/libs/enums.py as integer values to ensure database efficiency and code consistency.


# 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 defines the status column with a default value that aligns with the enum.


# 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.


# 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.


# 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.


# 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

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

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

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 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.
  • 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. 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 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 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 and 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. 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →