Câu 1Khi nhận thanh toán qua dịch vụ trung gian thanh toán, thông tin thẻ đi qua đâu?
Cơ chế thu phí — Stripe làm thay những gì, và cấp quyền bằng Webhook
Bài viết này nằm trong khóa Kiến thức nền tảng về CNTT, xây dựng từ đầu những kiến thức CNTT thực tế tối thiểu mà bạn cần để lập trình và vibe coding.
Thông tin thẻ dừng lại ở dịch vụ trung gian thanh toán và không đi qua server của bạn. Các sơ đồ cho thấy vì sao thông báo Webhook mới là căn cứ để mở tính năng trả phí.
Bài viết này nói về cơ chế thu phí.
Nội dung chia thành 2 phần: cơ chế không để thông tin thẻ đi tới server của bạn, và cơ chế phản ánh kết quả thanh toán vào ứng dụng của bạn.
Số thẻ dừng ở nhánh trên và không tới được my-app.
Căn cứ duy nhất để mở tính năng trả phí là thông báo đến qua nhánh dưới.
Việc nhận số thẻ và việc trao đổi với công ty thẻ đều diễn ra bên phía dịch vụ trung gian thanh toán, phần còn lại ở my-app chỉ là ghi kết quả vào bản ghi.
Bên nhận thông tin thẻ là dịch vụ trung gian thanh toán, không phải server của bạn
Xử lý một khoản thanh toán cần số thẻ, ngày hết hạn và mã bảo mật.
PCI DSS (Payment Card Industry Data Security Standard) là tiêu chuẩn mà các đơn vị xử lý thông tin thẻ phải tuân thủ, còn dịch vụ trung gian thanh toán (payment service provider) là dịch vụ bên ngoài xử lý thanh toán thẻ thay bạn.
Stripe và PayPal là những dịch vụ trung gian thanh toán tiêu biểu.
Bạn đặt ô nhập ở đâu sẽ quyết định số thẻ đi qua những nơi nào.
| Nơi đặt ô nhập | Nơi số thẻ đi qua | Thuộc phạm vi PCI DSS |
|---|---|---|
| my-app nhận và lưu lại | my-app và dịch vụ trung gian | my-app cũng thuộc phạm vi |
| my-app nhận rồi chuyển tiếp | my-app và dịch vụ trung gian | my-app cũng thuộc phạm vi |
| Trang của dịch vụ trung gian | Chỉ dịch vụ trung gian | Phạm vi của my-app là nhỏ nhất |
Chỉ dòng thứ 3 là không cho số thẻ đi qua server của my-app.
Khi đó my-app chỉ giữ giá trị dùng để phân biệt khoản thanh toán, còn hai dòng trên để lại chính số thẻ hoặc bản ghi của lần truyền đó.
Dù không lưu mà chỉ chuyển tiếp thì giá trị vẫn đi qua server của bạn, và ngay khi nó đi qua, my-app cũng lọt vào phạm vi PCI DSS.
Để phạm vi mà my-app phải bảo vệ nhỏ nhất có thể, bạn giao luôn cả ô nhập cho dịch vụ trung gian thanh toán.
- Số thẻ, ngày hết hạn, mã bảo mật
- Bản ghi trao đổi với công ty thẻ
- Giá trị phân biệt khoản thanh toán pay_88
- Số tiền, và thanh toán đã xong hay chưa
- Thanh toán của người dùng nào (u_1024)
Số thẻ không đi qua my-app
Nếu bạn không tự nhận số thẻ thì trách nhiệm bảo vệ nó cũng không rơi về phía bạn.
Dù lưu lại hay chuyển tiếp ngay, chỉ cần đi qua server của bạn là trách nhiệm đó vẫn phát sinh, nên bạn giao luôn cả ô nhập cho dịch vụ trung gian thanh toán và chỉ để sự kiện thanh toán đã xong tới được my-app.
Thanh toán kết thúc trên trang thanh toán nằm ở tên miền của dịch vụ trung gian
Để thông tin thẻ không đi qua server của bạn, chính màn hình nhập được đặt ở nơi khác — đó là trang thanh toán dạng hosted (hosted checkout page, màn hình thanh toán do dịch vụ trung gian thanh toán chuẩn bị và hiển thị trên tên miền của họ).
Việc đưa người dùng sang màn hình ở một tên miền khác là chuyển hướng (redirect).
Ở bước thứ 2, bạn truyền số tiền và địa chỉ quay lại, còn dịch vụ trung gian thanh toán trả về URL của trang thanh toán dành riêng cho khoản thanh toán đó.
Việc trao đổi này thực hiện bằng cách gọi Web API, và bạn đính kèm API key.
- Trang đăng ký gói trả phí
- /thanks, mở sau khi thanh toán
- Trang thanh toán để nhập số thẻ
- my-app không nhìn thấy bên trong trang này
Trang thanh toán còn được cung cấp dưới dạng một khung đặt ngay trong trang của my-app.
Cách hiển thị khác nhau, nhưng giá trị nhập vào vẫn đi tới cùng một nơi.
Trang thanh toán do dịch vụ trung gian thanh toán chuẩn bị
Màn hình nhập số thẻ thuộc về dịch vụ trung gian thanh toán, không phải my-app.
Người dùng được chuyển hướng sang màn hình đó, thanh toán xong thì quay về /thanks; kể cả khi bạn đặt nó dưới dạng một khung trong trang của mình, giá trị nhập vào vẫn đi tới cùng một nơi.
Căn cứ để cấp quyền là thông báo Webhook, không phải trang mà người dùng quay về
Bên quyết định có được hiện tính năng trả phí hay không là my-app chứ không phải dịch vụ trung gian thanh toán, và bản ghi đó là quyền sử dụng (entitlement, bản ghi phạm vi tính năng mà người dùng đã thanh toán được phép dùng).
Việc màn hình của người dùng quay về /thanks tự nó không cho my-app biết thanh toán đã xong hay chưa.
Hãy nhìn những gì xảy ra sau khi thanh toán theo 2 đường tách biệt.
Đường trên sẽ dừng lại ở đó nếu người dùng đóng trình duyệt ngay sau khi trả tiền.
Đường dưới đi giữa các server, nên nếu thông báo không tới thì nó được gửi lại theo số lần và thời hạn đã định.
Tùy bạn lấy đường nào làm căn cứ, cùng 3 người đó sẽ có kết quả cấp quyền khác nhau.
| Điều đã xảy ra với người dùng đó | Nếu xét theo /thanks | Nếu xét theo /webhook |
|---|---|---|
| Đã trả tiền và đã mở /thanks | Được cấp quyền | Được cấp quyền |
| Trả tiền xong rồi đóng trình duyệt ngay | Không được cấp quyền | Được cấp quyền |
| Mở /thanks mà không trả tiền | Được cấp quyền | Không được cấp quyền |
Nếu xét theo /thanks, người đóng trình duyệt thì không được cấp quyền, còn người chưa trả tiền lại được cấp quyền.
Căn cứ duy nhất để cấp quyền là thông báo trên đường dưới.
Trong thanh toán, nếu phản ánh cùng một thông báo 2 lần thì thời hạn sử dụng bị kéo dài gấp đôi.
Hãy ghi lại giá trị dùng để phân biệt từng thông báo, và giữ nguyên quyền khi cùng thông báo đó tới lần thứ 2.
Thông báo có chữ ký không khớp thì bị bỏ đi mà không đổi quyền.
Khác biệt duy nhất giữa 2 trường hợp còn lại là thông báo đó đã được phản ánh hay chưa.
- Giá trị phân biệt người dùng u_1024
- Hash của mật khẩu
- Loại gói (paid)
- Ngày hết hạn
- Thuộc về người dùng nào (u_1024)
- Giá trị phân biệt thông báo evt_7
- Thời điểm đã phản ánh
Dù bạn ẩn nút trả phí trên trang, đoạn mã đó vẫn tới thiết bị của người dùng.
Kiểm tra quyền ở phía server mỗi lần chính là phân quyền đã nói trong Cách chức năng đăng nhập hoạt động.
Số thẻ dừng lại trong khung ở giữa và không vào khung bên phải.
Quyền được cấp khi thông báo ở mũi tên 2 tới nơi, không phải khi trình duyệt của người dùng quay về.
Thứ báo tin thanh toán là thông báo từ server
Thứ báo rằng thanh toán đã xong là thông báo tới từ server của dịch vụ trung gian thanh toán, không phải trang mà người dùng quay về.
Bạn đối chiếu chữ ký của thứ nhận được, giữ nguyên quyền nếu đó là thông báo đã phản ánh, ghi kết quả vào cơ sở dữ liệu của mình, rồi quyết định có hiện tính năng trả phí hay không.
Với subscription, mỗi kỳ lại có một thông báo tới và quyền thay đổi
Hợp đồng tính phí theo tháng hoặc theo năm là subscription (hợp đồng mà việc thanh toán tự động lặp lại theo từng kỳ đã định).
Một khoản thanh toán lặp lại không phải lúc nào cũng thành công.
Ở hàng thất bại, dịch vụ trung gian thanh toán để cách vài ngày rồi thử thu lại.
Trong thời gian đó có giữ trạng thái dùng được hay cắt ngay là do my-app quyết định.
Thứ cho phép bạn kiểm tra trước khi phát hành là chế độ thử nghiệm (test mode, trạng thái do dịch vụ trung gian thanh toán chuẩn bị để kiểm tra hoạt động mà không phát sinh khoản thu thật).
API key cho thử nghiệm và cho bản chính thức là khác nhau, nên bạn chuyển đổi bằng giá trị của biến môi trường.
Quyết định cả thông báo gỡ quyền trước khi phát hành
Với subscription, mỗi khi tới kỳ lại có một lần thanh toán, và mỗi lần như vậy lại có một thông báo tới.
Nếu chỉ xử lý thông báo cấp quyền thì người dùng đã ngừng trả tiền vẫn dùng được, nên hãy quyết định cấp, gia hạn và gỡ bỏ cho từng thông báo tới, và chạy thử toàn bộ ở chế độ thử nghiệm trước khi phát hành.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Bạn dựa vào điều nào làm căn cứ để kết luận thanh toán đã xong và cấp quyền cho người dùng?
Câu 3Khi có thông báo cho biết thanh toán thất bại trong hình thức thu phí định kỳ, ứng dụng của bạn làm gì?