VietBilling
Tất cả hướng dẫn
VietBilling Engineering Guides

Gia hạn subscription khi PayOS không tự động trừ tiền

Mô hình hóa renewal order, payment link, grace period và quyền truy cập khi mỗi chu kỳ cần người dùng chủ động thanh toán lại.

VietBilling EngineeringKiểm chứng 2026-08-319 phút đọc

Renewal không đồng nghĩa auto-debit

Với PayOS payment link, khách hàng chủ động hoàn tất thanh toán cho từng kỳ. Vì vậy hệ thống không nên hứa “tự động trừ tiền”. Nó cần tạo một renewal flow rõ ràng, nhắc đúng lúc và chỉ kéo dài quyền truy cập sau payment hợp lệ.

active → renewal_due → renewal_checkout_created
       → paid → active(next period)
       → unpaid → past_due → canceled

Dùng thời gian tuyệt đối làm nguồn sự thật

Subscription nên giữ current_period_start, current_period_end, status và tùy chọn cancel_at_period_end. Đừng suy ra kỳ hiện tại từ ngày tạo payment gần nhất; payment có thể đến muộn hoặc được đối soát lại.

Khi tới thời điểm chuẩn bị gia hạn:

  1. Tạo một Order cho kỳ kế tiếp với snapshot giá.
  2. Tạo Checkout/PayOS payment link gắn với Order đó.
  3. Gửi URL gia hạn qua kênh liên lạc đã được người dùng cho phép.
  4. Sau payment hợp lệ, dịch chuyển period trong một transaction.

Chống tạo nhiều renewal order

Cron có thể chạy lại và nhiều worker có thể chạm cùng subscription. Dùng một khóa duy nhất theo subscription và period:

create unique index renewal_once_per_period
on orders (subscription_id, period_start)
where kind = 'renewal';

Job có thể chạy mỗi giờ, nhưng cùng kỳ chỉ có một logical renewal order. Nếu provider request timeout, retry Checkout bằng cùng idempotency key.

Grace period là quyết định sản phẩm

past_due nên là trạng thái rõ ràng, không phải boolean rải rác. Ví dụ:

Thời lượng grace cần nhất quán với email, UI và backend authorization. Không để frontend tự tính vì clock hoặc timezone khác nhau.

Thanh toán muộn và race condition

Một payment có thể đến đúng lúc job past-due chạy. Khóa subscription row trong transaction, đọc lại Payment/Order hiện tại, rồi chỉ cancel nếu không có succeeded payment. Completion service cũng cần nhận ra Order đã hoàn tất và trả kết quả idempotent.

Hủy cuối kỳ

Khi người dùng chọn hủy, đặt cancel_at_period_end=true; không thu hồi quyền ngay nếu họ đã trả cho kỳ hiện tại. Job renewal không tạo kỳ mới, và job kết thúc kỳ chuyển subscription sang canceled rồi revoke entitlement.

Điều cần đo

Các metric này mô tả sức khỏe renewal tốt hơn tổng số payment, vì chúng cho biết người dùng có hoàn tất kỳ tiếp theo hay không.