Cách thêm gói đăng ký với Stripe: Gói, dùng thử, Webhook và thanh toán thất bại
Để thêm gói đăng ký với Stripe, hãy tạo một sản phẩm với giá định kỳ trong Stripe Dashboard, gửi khách hàng đến Stripe Checkout ở chế độ đăng ký từ máy chủ của bạn, và để một webhook báo cho ứng dụng biết khi nào mỗi khoản thanh toán thành công, thất bại, thay đổi hoặc kết thúc. Lưu trạng thái gói đăng ký gắn với người dùng và cấp quyền truy cập dựa trên đó, chứ không phải dựa trên trang thành công. Thêm nút Quản lý thanh toán mở customer portal được Stripe lưu trữ để mọi người có thể đổi thẻ, chuyển gói và hủy mà bạn không cần xây các màn hình đó.
Hướng dẫn này tiếp nối cách thêm thanh toán vào ứng dụng, vốn đề cập Checkout một lần, chế độ thử nghiệm và đưa lên chính thức. Ở đây chúng ta tập trung vào điểm khác biệt của thanh toán định kỳ. Chi tiết về Stripe lấy từ tài liệu của Stripe, tính đến tháng 9 năm 2026.
Vì sao gói đăng ký khó hơn thanh toán một lần
Một khoản thanh toán một lần xảy ra khi khách hàng đang ở trong ứng dụng của bạn. Gói đăng ký tiếp tục diễn ra sau khi họ rời đi: gia hạn mỗi tháng, thẻ hết hạn, nâng cấp giữa chu kỳ, hủy có hiệu lực vào cuối kỳ. Ứng dụng của bạn chỉ biết về những điều này qua webhook. Phần lớn lỗi gói đăng ký xuất phát từ việc coi khoản thanh toán đầu tiên là toàn bộ câu chuyện.
Các lựa chọn của bạn
| Cách làm | Bạn xây dựng gì | Phù hợp nhất với |
|---|---|---|
| Payment Link với giá định kỳ | Chỉ một liên kết | Kiểm tra nhu cầu; xử lý quyền truy cập bằng tay |
| Checkout (chế độ đăng ký) + customer portal | Một route trên server, một webhook, một nút thanh toán | Đa số ứng dụng SaaS và thành viên |
| API đăng ký với biểu mẫu thanh toán riêng | Nhiều hơn nhiều: giao diện thanh toán, xác thực, thử lại | Ứng dụng có luồng thanh toán bất thường |
| Nhà cung cấp merchant-of-record | Tương tự Checkout | Người bán muốn nhà cung cấp xử lý thuế bán hàng |
Với đa số ứng dụng, Checkout cộng với portal là sự đánh đổi phù hợp. Hãy so sánh các nhà cung cấp trong Stripe so với Paddle so với Lemon Squeezy nếu việc xử lý thuế khiến bạn lo lắng.
Lên kế hoạch giá trong Stripe trước
Trong Stripe, một sản phẩm là thứ bạn bán ("gói Pro"), còn giá là cách bạn tính phí cho nó ("20 USD mỗi tháng", "200 USD mỗi năm"). Một sản phẩm có thể có nhiều mức giá. Hãy quyết định:
- Bạn cung cấp những hạng nào và mỗi hạng mở khóa gì trong ứng dụng
- Hàng tháng, hàng năm, hay cả hai
- Bạn có cung cấp bản dùng thử không, và nó có cần thẻ ngay từ đầu không
Tạo các sản phẩm và mức giá ở chế độ thử nghiệm, và giữ lại ID của các mức giá đó; ứng dụng của bạn sẽ tham chiếu đến chúng. Nếu bạn chưa chốt giá, hãy đọc cách định giá sản phẩm SaaS và freemium so với dùng thử miễn phí.
Vòng đời gói đăng ký bạn phải xử lý
Tổng quan về gói đăng ký của Stripe liệt kê các trạng thái mà một gói có thể có. Những trạng thái quan trọng nhất đối với quyền truy cập:
| Trạng thái | Nghĩa là gì | Cấp quyền truy cập? |
|---|---|---|
trialing | Đang trong thời gian dùng thử | Có |
active | Đang trong tình trạng tốt | Có |
incomplete | Khoản thanh toán đầu tiên chưa hoàn tất; Stripe cho khách hàng 23 giờ | Không |
incomplete_expired | Khoản thanh toán đầu tiên chưa bao giờ thành công | Không |
past_due | Lần thanh toán gần nhất thất bại hoặc chưa được thử; có thể sẽ có lần thử lại | Tùy bạn; nhiều ứng dụng cho một khoảng ân hạn ngắn |
unpaid | Các lần thử lại đã kết thúc, hóa đơn vẫn mở, không thử thêm | Không; tài liệu Stripe khuyến nghị thu hồi quyền truy cập |
canceled | Đã kết thúc; không thể khởi động lại | Không |
paused | Bản dùng thử đã kết thúc mà không có phương thức thanh toán và được đặt ở chế độ tạm dừng | Không |
Lưu trạng thái trên bản ghi người dùng hoặc tài khoản của bạn và dựa quyền truy cập trên đó. Cập nhật nó mỗi khi một webhook liên quan đến đến nơi.
Các sự kiện webhook cần xử lý
| Sự kiện | Ứng dụng của bạn làm gì |
|---|---|
checkout.session.completed | Liên kết ID khách hàng và gói đăng ký mới của Stripe với người dùng của bạn |
invoice.paid | Đánh dấu gói đăng ký là active và ghi lại ngày kết thúc kỳ mới |
invoice.payment_failed | Báo cho người dùng biết thẻ của họ bị lỗi và liên kết đến portal |
invoice.payment_action_required | Yêu cầu người dùng hoàn tất xác thực thẻ |
customer.subscription.updated | Cập nhật gói, trạng thái và mọi việc hủy đã được lên lịch |
customer.subscription.deleted | Gỡ quyền truy cập trả phí |
customer.subscription.trial_will_end | Nhắc người dùng trước khi bản dùng thử kết thúc; Stripe gửi trước ba ngày |
Các quy tắc xử lý từ tài liệu webhook của Stripe vẫn áp dụng: xác minh chữ ký bằng nội dung thô, trả về 2xx nhanh chóng, xử lý sự kiện trùng lặp và đến không theo thứ tự, và chuẩn bị cho việc thử lại. Hướng dẫn webhook là gì giải thích ý tưởng này, còn hướng dẫn thanh toán liệt kê chi tiết các quy tắc.
Từng bước
- Tạo sản phẩm và mức giá trong chế độ thử nghiệm của Stripe.
- Thêm secrets: secret key, các price ID, và sau này là signing secret của webhook. Không bao giờ đưa chúng vào code frontend.
- Trang giá: nút của mỗi gói gọi máy chủ của bạn, nơi tạo một Checkout Session ở chế độ đăng ký cho người dùng đã đăng nhập. Truyền user ID của bạn để webhook có thể khớp khoản thanh toán với tài khoản.
- Endpoint webhook: xác minh chữ ký và xử lý các sự kiện trên, cập nhật một bản ghi gói đăng ký cho mỗi người dùng.
- Khóa tính năng dựa trên trạng thái và gói đã lưu, được kiểm tra trên server cho mọi hành động được bảo vệ, không chỉ các nút bị ẩn.
- Customer portal: cấu hình trong Stripe Dashboard (người dùng có thể chuyển sang những gói nào, họ có thể hủy hay không), rồi thêm nút Quản lý thanh toán tạo một portal session trên server và chuyển hướng đến đó.
- Thanh toán thất bại: trong cài đặt thanh toán của Stripe, chọn hành vi thử lại và điều gì xảy ra sau lần thử cuối. Hiển thị một banner trong ứng dụng khi trạng thái là
past_due. - Kiểm thử toàn bộ vòng đời, rồi publish và đăng ký webhook chính thức.
Prompt để đưa cho công cụ xây dựng bằng AI
Thêm thanh toán gói đăng ký với Stripe. Các gói: Starter (price ID trong STRIPE_PRICE_STARTER) và Pro (STRIPE_PRICE_PRO). Trang giá tạo một Checkout Session ở chế độ đăng ký trên server bằng STRIPE_SECRET_KEY, đính kèm user ID của người dùng đã đăng nhập. Lưu Stripe customer ID, subscription ID, gói và trạng thái lên người dùng. Không bao giờ để lộ secret key cho trình duyệt.
Thêm POST /api/stripe/webhook. Xác minh chữ ký bằng STRIPE_WEBHOOK_SECRET trên nội dung thô của request. Xử lý checkout.session.completed, invoice.paid, invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted và customer.subscription.trial_will_end. Ghi lại các ID sự kiện đã xử lý để bỏ qua các sự kiện trùng lặp. Cập nhật trạng thái người dùng từ đối tượng subscription, không phải từ thứ tự các sự kiện đến.
Khóa các tính năng Pro trên server: chỉ cho phép khi trạng thái là active hoặc trialing và gói là Pro. Hiển thị banner "thanh toán thất bại, hãy cập nhật thẻ" khi trạng thái là past_due. Thêm nút Quản lý thanh toán mở customer portal của Stripe cho người dùng này.
Nâng cấp, hạ cấp và tính tiền theo tỷ lệ
Khi khách hàng chuyển gói, tài liệu của Stripe cho biết thay đổi thường tạo ra một proration (tính tiền theo tỷ lệ): mức giá mới được áp dụng cho phần còn lại của kỳ thanh toán hiện tại. Bạn có thể xem trước khoản tính theo tỷ lệ hoặc tắt nó đi. Nếu các gói có cùng chu kỳ, ngày thanh toán vẫn giữ nguyên; chuyển từ hàng tháng sang hàng năm sẽ đưa ngày thanh toán về ngày chuyển đổi. Để khách hàng tự đổi gói trong portal giúp bạn khỏi phải viết logic này, nhưng hãy quyết định trong cài đặt portal những việc chuyển đổi nào được phép.
Danh sách kiểm thử
Hãy dùng chế độ thử nghiệm của Stripe và thẻ thử, và đi qua mọi trạng thái:
- Gói đăng ký mới cấp đúng gói cho đúng người dùng
- Đóng tab ngay sau khi thanh toán vẫn được cấp quyền truy cập (chứng minh webhook hoạt động)
- Thẻ bị từ chối khi đăng ký không cấp gì cả
- Gia hạn thất bại đưa tài khoản vào past_due và hiển thị banner
- Cập nhật thẻ trong portal khôi phục trạng thái active
- Nâng cấp và hạ cấp thay đổi các tính năng đúng cách
- Hủy vào cuối kỳ giữ quyền truy cập đến ngày kết thúc, rồi gỡ bỏ
- Người dùng dùng thử được truy cập, nhận nhắc nhở, và chuyển đổi hoặc mất quyền đúng cách
- Gửi cùng một sự kiện webhook hai lần không thay đổi gì
Các test clock của Stripe cho phép bạn mô phỏng thời gian trôi qua, nhờ đó bạn có thể kiểm thử gia hạn mà không cần chờ một tháng. Hãy xem tài liệu kiểm thử hiện tại của Stripe để biết cách dùng chúng.
Những lỗi thường gặp
- Cấp quyền truy cập ở trang thành công. Khách hàng có thể đã thanh toán mà không bao giờ đến được trang đó. Hãy dùng webhook.
- Chỉ xử lý khoản thanh toán đầu tiên. Gia hạn, thất bại và hủy đến sau, dưới dạng sự kiện.
- Chỉ kiểm tra trạng thái gói ở frontend. Bất kỳ ai cũng có thể gọi trực tiếp API của bạn.
- Một người dùng vô tình có nhiều gói đăng ký. Kiểm tra gói đăng ký hiện có trước khi tạo Checkout Session mới, hoặc gửi khách hàng hiện tại đến portal.
- Thu hồi quyền truy cập ngay lần gia hạn thất bại đầu tiên. Thẻ thất bại vì những lý do rất thông thường. Một khoảng ân hạn ngắn kèm banner rõ ràng sẽ giữ chân khách hàng.
- Quên webhook chính thức. Endpoint thử nghiệm và chính thức có signing secret khác nhau.
- Bỏ qua thuế và hoàn tiền. Hãy quyết định chính sách hoàn tiền và kiểm tra nghĩa vụ thuế với kế toán viên trước khi ra mắt.
Gói đăng ký trong Mythex
Các ứng dụng bạn xây trên Mythex dùng tài khoản Stripe và khóa của chính bạn; không có kết nối Stripe một chạm cho ứng dụng của bạn. Thêm khóa thử nghiệm và price ID vào secrets của dự án, dùng các prompt tương tự như trên, và kiểm thử bằng thẻ thử của Stripe trong Preview. Webhook cần URL công khai của ứng dụng, nên hãy publish trước khi đăng ký endpoint trong Stripe, và publish lại sau khi chuyển sang khóa chính thức để ứng dụng chính thức nhận được chúng. Xem Nhận thanh toán với Stripe và Xử lý webhook trong backend. Về sản phẩm rộng hơn, cách xây MVP SaaS đề cập những gì cần xây xung quanh phần thanh toán.
Câu hỏi thường gặp
Cách đơn giản nhất để thêm gói đăng ký với Stripe là gì?
Tạo một sản phẩm với giá định kỳ trong Stripe Dashboard, gửi khách hàng đến Stripe Checkout ở chế độ đăng ký, xử lý các sự kiện đăng ký bằng webhook, và liên kết nút Quản lý thanh toán với customer portal được Stripe lưu trữ. Cách này giúp bạn không phải tự xây biểu mẫu thẻ và màn hình đổi gói.
Tôi cần những sự kiện webhook nào của Stripe cho gói đăng ký?
Tối thiểu là checkout.session.completed để liên kết gói đăng ký mới với người dùng, invoice.paid để xác nhận mỗi lần thanh toán thành công, invoice.payment_failed để cảnh báo khi gia hạn thất bại, và customer.subscription.updated cùng customer.subscription.deleted để theo dõi thay đổi gói và việc hủy.
Điều gì xảy ra khi thanh toán gói đăng ký thất bại?
Theo tài liệu của Stripe tính đến tháng 9 năm 2026, gói đăng ký thường chuyển sang past_due và Stripe có thể thử thanh toán lại tùy theo cài đặt của bạn. Sau lần thử lại cuối cùng, cài đặt Dashboard của bạn quyết định nó bị hủy, được đánh dấu là unpaid, hay vẫn ở trạng thái past_due.
Tôi có thể cung cấp bản dùng thử miễn phí mà không yêu cầu thẻ không?
Có. Stripe hỗ trợ dùng thử miễn phí, và tài liệu của Stripe mô tả trạng thái paused cho các bản dùng thử kết thúc mà không có phương thức thanh toán, nếu bạn cấu hình như vậy. Yêu cầu thẻ ngay từ đầu thường có nghĩa là ít người đăng ký hơn nhưng nhiều khách trả tiền hơn; hãy thử nghiệm xem cách nào phù hợp với sản phẩm của bạn.