Bỏ qua nội dung

Tạo đơn hàng nạp tiền ví V-credit (HTML)

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 tạo một phiên checkout để nạp V-Credits qua SePay. API trả đủ dữ liệu để client chuyển user sang cổng thanh toán, đồng thời hỗ trợ tiếp tục thanh toán một đơn pending bằng providerOrderId cũ.

Phạm vi

Trong phạm vi (In scope):

  • API POST /wallet/topups để tạo mới hoặc resume checkout.
  • Validate số tiền nạp và ngưỡng nạp tối thiểu.
  • Tạo PaymentOrder provider SEPAY, status CREATED, có hạn thanh toán.
  • Trả WalletTopupCheckoutResponse gồm checkout URL, hidden form fields và HTML form auto-submit.
  • Khi request có providerOrderId, đồng bộ trạng thái provider trước khi resume checkout.
  • Chỉ cho owner hiện tại resume order của chính họ.

Ngoài phạm vi (Out of scope):

  • Huỷ đơn top-up, xem feature wallet-topup-cancel.
  • Đồng bộ detail chủ động từ màn chờ thanh toán, xem feature payment-order-detail-sync.
  • Webhook IPN SePay.
  • UI ví/nạp tiền trên frontend.
  • Nạp qua provider khác ngoài SePay.

User Stories

  • user đã đăng nhập, tôi muốn chọn số tiền nạp để nhận checkout SePay và thanh toán.
  • user bị gián đoạn checkout, tôi muốn tiếp tục thanh toán đơn cũ bằng providerOrderId để không tạo đơn trùng.
  • client app, tôi muốn nhận checkoutFormHtml hoặc formFields để chuyển user sang SePay đúng dữ liệu đã ký.
  • hệ thống kế toán ví, tôi muốn mỗi đơn top-up có PaymentOrder trước khi thanh toán để có thể đối soát với provider.

Luồng chức năng

sequenceDiagram
    actor User
    participant App as FE/Mobile
    participant BE
    participant SePay

    User->>App: Chọn số tiền nạp
    App->>BE: POST /wallet/topups
    BE->>BE: Validate amount và min payment amount

    alt Không có providerOrderId
        BE->>BE: Tạo PaymentOrder status CREATED
    else Có providerOrderId
        BE->>SePay: Query order detail
        BE->>BE: Kiểm tra owner, amount, status CREATED, chưa hết hạn
    end

    BE->>BE: Build signed checkout fields
    BE-->>App: WalletTopupCheckoutResponse
    App->>SePay: Submit checkout form

Acceptance Criteria

  • AC-1: API POST /wallet/topups yêu cầu user đã đăng nhập và quyền wallet:topup.
  • AC-2: Body nhận amount bắt buộc; providerOrderId, paymentMethod, successUrl, errorUrl, cancelUrl là tuỳ chọn.
  • AC-3: amount phải là số nguyên dương.
  • AC-4: amount phải lớn hơn hoặc bằng cấu hình runtime app.vcredit.min-payment-amount-vnd.
  • AC-5: Khi tạo mới, backend sinh providerOrderId dạng TOPUP_<20 ký tự>.
  • AC-6: Khi tạo mới, backend tạo PaymentOrder với provider SEPAY, amount, payment method nếu có, status CREATED, expiredAt theo app.sepay.topup-expiry-minutes.
  • AC-7: Response trả paymentOrderId, providerOrderId, amount, checkoutUrl, formFields, checkoutFormHtml, expiredAt.
  • AC-8: formFields phải có chữ ký SePay và các field cần thiết như amount, merchant, currency, operation, invoice number, customer id.
  • AC-9: Khi resume bằng providerOrderId, backend phải gọi flow detail provider trước khi trả checkout.
  • AC-10: Resume chỉ thành công nếu order thuộc user hiện tại, amount khớp, status là CREATED và chưa hết hạn.
  • AC-11: Resume order SUCCESS, CANCELLED, EXPIRED, FAILED hoặc amount mismatch phải bị từ chối bằng lỗi conflict/business phù hợp.
  • AC-12: Nếu thiếu principal hoặc user không hợp lệ, API trả lỗi auth/security thay vì tạo order.

Quy tắc nghiệp vụ

  • Một top-up checkout luôn gắn với đúng một user owner.
  • Mỗi order dùng một providerOrderId để đối soát với SePay.
  • Số tiền nạp VND cũng là số V-Credits sẽ được cộng khi thanh toán thành công.
  • URL redirect trong request được ưu tiên hơn cấu hình default của server.
  • paymentMethod client gửi lên chỉ là lựa chọn checkout, không chứng minh payment đã hoàn tất.
  • Checkout response chưa đồng nghĩa ví đã được cộng tiền; ví chỉ được cộng khi provider xác nhận CAPTURED.
  • Backend phải escape HTML khi build checkoutFormHtml.
  • Không tạo order mới khi client đang resume order hợp lệ bằng providerOrderId.

Dữ liệu & Trạng thái

Entity nghiệp vụ:

  • CreateWalletTopupRequest: amount, providerOrderId, paymentMethod, successUrl, errorUrl, cancelUrl.
  • PaymentOrder: order nội bộ ghi user, amount, provider, providerOrderId, paymentMethod, status, expiredAt, histories.
  • WalletTopupCheckoutResponse: payload checkout trả cho client.

Trạng thái user-facing:

  • CREATED: order mới tạo và có thể checkout hoặc resume.
  • EXPIRED: order quá hạn, không được resume checkout.
  • SUCCESS: order đã thanh toán, không được resume checkout.
  • CANCELLED: order đã huỷ, không được resume checkout.
  • FAILED: order lỗi, không được resume checkout.

Endpoint BE hiện có:

  • POST /api/wallet/topups

Liên quan