Huỷ đơn hàng nạp ví theo providerOrderId
Feature URL
Module
payment
Status
shipped
Priority
P0
Platforms
be
AC progress
12 / 12
Last reviewed
2026-05-22
Mục tiêu
Cho phép user đã đăng nhập huỷ một đơn nạp ví còn pending bằng providerOrderId. API giúp user dừng đơn chưa thanh toán, đồng bộ trạng thái huỷ với SePay khi provider đã có order và lưu lại snapshot cuối của payment order.
Phạm vi
Trong phạm vi (In scope):
- API
POST /wallet/topups/{providerOrderId}/cancel. - Xác thực user và quyền
wallet:topup. - Tìm order theo provider
SEPAY,providerOrderIdvà owner hiện tại. - Từ chối huỷ order đã thanh toán thành công.
- Gọi SePay cancel khi provider có order và order chưa settled.
- Chuyển order local sang
CANCELLEDvà trảWalletTopupOrderResponse.
Ngoài phạm vi (Out of scope):
- Tạo order top-up, xem feature
wallet-topup-create. - Hoàn tiền cho order đã
SUCCESS. - Huỷ hàng loạt nhiều order.
- Huỷ order của user khác hoặc bởi admin.
- Xử lý webhook IPN sau khi user huỷ.
User Stories
- Là user đã tạo top-up, tôi muốn huỷ đơn chưa thanh toán để không còn thấy đơn pending.
- Là client app, tôi muốn gọi huỷ bằng
providerOrderIdvì đây là mã được dùng xuyên suốt checkout. - Là hệ thống, tôi muốn đảm bảo order đã thanh toán không thể bị huỷ nhầm.
- Là ops/support, tôi muốn history order ghi nhận việc huỷ để có thể audit.
Luồng chức năng
sequenceDiagram
actor User
participant App as FE/Mobile
participant BE
participant SePay
User->>App: Bấm huỷ đơn nạp
App->>BE: POST /wallet/topups/{providerOrderId}/cancel
BE->>BE: Xác thực user + tìm order owner-only
alt Order SUCCESS
BE-->>App: 409 conflict
else Order đã CANCELLED hoặc EXPIRED
BE-->>App: Snapshot hiện tại
else Order còn huỷ được
BE->>SePay: Cancel provider order nếu tồn tại
BE->>BE: Set status CANCELLED + append history
BE-->>App: WalletTopupOrderResponse
endAcceptance Criteria
- AC-1: API
POST /wallet/topups/{providerOrderId}/cancelyêu cầu user đã đăng nhập và quyềnwallet:topup. - AC-2:
providerOrderIdtrên path là bắt buộc và không được blank. - AC-3: Backend chỉ tìm order provider
SEPAYthuộc user hiện tại. - AC-4: Nếu không tìm thấy order thuộc user hiện tại, API trả lỗi not found.
- AC-5: Nếu order đang
SUCCESS, API trả conflict và không đổi trạng thái. - AC-6: Nếu order đã
CANCELLEDhoặcEXPIRED, API trả snapshot hiện tại, không gọi lại luồng huỷ gây side effect mới. - AC-7: Nếu order còn huỷ được, backend gọi provider cancel khi provider order tồn tại.
- AC-8: Nếu provider order chưa tồn tại trên SePay, backend vẫn có thể huỷ local với metadata ghi rõ local cancel only.
- AC-9: Sau khi huỷ thành công, order local có status
CANCELLED. - AC-10: Response là
WalletTopupOrderResponsegồm id, provider, providerOrderId, amount, status, histories, expiredAt, createdAt. - AC-11: Mọi lần huỷ hợp lệ phải append history mô tả trạng thái
CANCELLED. - AC-12: User không thể huỷ order của user khác dù biết
providerOrderId.
Quy tắc nghiệp vụ
providerOrderIdlà định danh duy nhất client dùng để huỷ checkout SePay.- Chỉ owner của order được phép huỷ order của mình.
- Order
SUCCESSlà trạng thái đã settled, không được huỷ qua API này. - Order
CANCELLEDvàEXPIREDlà trạng thái cuối, trả snapshot hiện tại để API idempotent ở mức user action. - Provider cancel failure do upstream phải được phân loại là lỗi integration, không được âm thầm đổi trạng thái sai.
- Huỷ local khi provider chưa có order là hợp lệ để user không còn tiếp tục checkout order đó.
- Huỷ top-up không tạo wallet ledger cộng/trừ tiền.
Dữ liệu & Trạng thái
Entity nghiệp vụ:
PaymentOrder: order cần huỷ, lookup theo provider, providerOrderId và user id.PaymentProviderCancelResult: metadata kết quả huỷ ở SePay hoặc local cancel.WalletTopupOrderResponse: snapshot trả về sau khi huỷ.
Trạng thái user-facing:
CREATED: order còn có thể huỷ.CANCELLED: order đã huỷ, không cộng credit.EXPIRED: order hết hạn, không cần huỷ thêm.SUCCESS: order đã thanh toán, không được huỷ.FAILED: order lỗi, không còn là checkout bình thường.
Endpoint BE hiện có:
POST /api/wallet/topups/{providerOrderId}/cancel
Liên quan
- Phụ thuộc: wallet-topup-create
- Ảnh hưởng: payment-order-detail-sync