Trong bài viết cuối cùng của loạt bài về thiết kế hệ thống thanh toán, tôi sẽ đề cập đến các ‘kiểu mẫu giao tiếp’ được sử dụng để thanh toán, các chiến lược xử lý lỗi thanh toán và cả cách đạt được tính nhất quán trong hệ thống thanh toán.
Giao tiếp giữa các dịch vụ nội bộ
Có 2 loại mẫu giao tiếp được các ‘dịch vụ nội bộ’ sử dụng để liên lạc:
(i) Đồng bộ (Synchronous).
(ii) Không đồng bộ (Asynchronous).

Giao tiếp đồng bộ
Đó là một loại giao tiếp tạo ra một chu kỳ yêu cầu và phản hồi dài phụ thuộc vào nhiều dịch vụ. Nó được xem là một cách đáng tin cậy và hiệu quả để gửi số lượng lớn dữ liệu. Nhưng nhược điểm của phương pháp này là:
(i) Hiệu suất thấp: Nếu một trong các thành phần bị ảnh hưởng thì các thành phần khác cũng bị ảnh hưởng.
(ii) Cách ly lỗi kém: Nếu PSP (nhà cung cấp dịch vụ thanh toán) hoặc dịch vụ khác bị lỗi thì máy khách không thể nhận được phản hồi nữa.
(iii) Khớp nối chặt chẽ: Người khởi tạo yêu cầu cần biết người nhận.
(iv) Khó mở rộng quy mô: Nếu không sử dụng ‘hàng chờ đợi’ để hoạt động như một bộ đệm thì việc mở rộng quy mô là không dễ dàng.
Giao tiếp không đồng bộ
Giao tiếp không đồng bộ được chia thành hai loại. Chúng là người tiếp nhận ‘đơn’ (một người tiếp nhận) và người tiếp nhận ‘đa’ (nhiều người tiếp nhận).
Một người tiếp nhận:
Mỗi yêu cầu được xử lý bởi một người nhận hoặc dịch vụ được triển khai thông qua ‘hàng chờ đợi tin nhắn’ được chia sẻ. Hàng chờ đợi có thể có nhiều người đăng ký, nhưng sau khi tin nhắn được xử lý; nó sẽ bị xóa khỏi hàng chờ đợi.
Nhiều người tiếp nhận:
Mỗi yêu cầu được xử lý bởi nhiều người nhận hoặc dịch vụ. Hãy xem xét ‘Apache Kafka’.
Khi người tiêu dùng nhận được tin nhắn, họ sẽ không bị xóa khỏi Kafka. Cùng một tin nhắn có thể được xử lý bởi các dịch vụ khác nhau.
Mô hình này ánh xạ tốt tới hệ thống thanh toán, vì cùng một yêu cầu có thể gây ra nhiều tác dụng phụ như gửi thông báo đẩy, cập nhật báo cáo tài chính, …. Yêu cầu thanh toán được xuất bản tới Kafka và được sử dụng bởi các dịch vụ khác nhau – như hệ thống thanh toán, dịch vụ phân tích và dịch vụ thanh toán.
Kết luận:
Giao tiếp đồng bộ có thiết kế đơn giản hơn nhưng nó không cho phép các dịch vụ được tự chủ, do hiệu suất tổng thể bị ảnh hưởng khi mức độ phụ thuộc tăng lên.
Giao dịch không đồng bộ thường đơn giản và nhất quán cho khả năng mở rộng và khả năng phục hồi khi xảy ra lỗi. Đối với hệ thống thanh toán quy mô lớn có ‘logic’ kinh doanh phức tạp và số lượng lớn phụ thuộc của bên thứ ba, giao tiếp không đồng bộ là lựa chọn tốt hơn.
Xem thêm: Thiết kế hệ thống thanh toán (Phần 1)
Xử lý các khoản thanh toán không thành công
Mọi hệ thống thanh toán đều phải xử lý các giao dịch thất bại. Độ tin cậy và khả năng chịu lỗi là những yêu cầu chính. Chúng ta hãy xem xét một số kỹ thuật để giải quyết những thách thức đó.
Theo dõi trạng thái thanh toán
Bất cứ khi nào xảy ra lỗi, chúng ta có thể xác định trạng thái hiện tại của giao dịch thanh toán và quyết định xem có cần thử lại hoặc hoàn lại tiền hay không. Trạng thái thanh toán có thể được duy trì trong bảng cơ sở dữ liệu bổ sung.
Hàng đợi thử lại và hàng đợi thông điệp ‘chết’ (Retry queue and dead letter queue)
Hàng đợi thử lại: Các lỗi có thể thử lại như lỗi tạm thời được chuyển đến hàng đợi thử lại.
Hàng đợi thông điệp ‘chết’: Nếu thông điệp bị lỗi liên tục, cuối cùng nó sẽ rơi vào hàng đợi thông điệp ‘’chết, điều này rất hữu ích cho việc gỡ lỗi và cách ly các thông điệp có vấn đề để kiểm tra nhằm xác định lý do tại sao chúng không được xử lý thành công.

Phân phối một lần chính xác (exactly once delivery)
Một trong những vấn đề nghiêm trọng nhất mà hệ thống thanh toán có thể gặp phải là tính phí gấp đôi cho khách hàng. Hệ thống thanh toán cần thực hiện lệnh thanh toán chính xác một lần. Một thao tác được thực hiện chính xác một lần nếu:
(i) Nó được thực hiện ít nhất một lần (Retry – thử lại)
(ii) Đồng thời, nó được thực thi tối đa một lần (Idempotence)
Thử lại (Retry)
Khi chúng ta thử lại một giao dịch thanh toán do lỗi mạng hoặc hết thời gian chờ, nó sẽ cung cấp sự đảm bảo ít nhất một lần. Ví dụ, một khách hàng cố gắng thanh toán 10 USD nhưng yêu cầu thanh toán liên tục không thành công do kết nối mạng kém. Mạng cuối cùng đã phục hồi và yêu cầu đã thành công ở lần thử thứ tư.
Việc quyết định khoảng thời gian thích hợp giữa các lần thử lại là rất quan trọng. Dưới đây là một số chiến lược thử lại phổ biến.
(i) Thử lại ngay lập tức: Máy khách ngay lập tức gửi lại yêu cầu.
(ii) Khoảng thời gian cố định: Đợi một khoảng thời gian cố định giữa thời điểm thanh toán không thành công và lần thử lại mới.
(iii) Khoảng thời gian tăng dần: Khách hàng đợi một khoảng thời gian ngắn cho lần thử lại đầu tiên và sau đó tăng dần thời gian cho những lần thử lại tiếp theo.
(iv) Thời gian chờ theo cấp số nhân: Nhân đôi thời gian chờ đợi giữa các lần thử lại sau mỗi lần thử lại thất bại. Ví dụ, khi yêu cầu không thành công lần đầu tiên, chúng ta sẽ thử lại sau 1 giây; nếu thất bại lần thứ hai, chúng ta đợi 2 giây trước khi thử lại lần tiếp theo; nếu thất bại lần thứ ba, chúng ta đợi 4 giây trước khi thử lại lần nữa.
(v) Hủy: Khách hàng có thể hủy yêu cầu. Đây là một thực tế phổ biến khi lỗi xảy ra vĩnh viễn hoặc các yêu cầu ‘lặp đi lặp lại’ khó có thể thành công.
Việc xác định chiến lược thử lại thích hợp là rất khó. Không có giải pháp “một kích thước phù hợp cho tất cả”. Lý tưởng nhất là thời gian chờ theo cấp số nhân được sử dụng, nếu sự cố mạng gặp phải trong một khoảng thời gian ngắn. Một cách thực hành tốt là cung cấp mã lỗi có tiêu đề ‘thử lại sau’.
Thực thi tối đa một lần (Idempotence)
Để tránh thanh toán hai lần không thành công, chúng ta thực hiện thanh toán tối đa một lần được gọi là idempotency.
Tính tạm thời có nghĩa là khách hàng có thể thực hiện cùng một yêu cầu (liên lạc) nhiều lần và tạo ra kết quả tương tự.
Để liên lạc giữa máy khách và máy chủ, khóa tạm thời thường là một giá trị duy nhất được máy khách tạo ra và hết hạn sau một khoảng thời gian nhất định.
UUID thường được sử dụng làm khóa tạm thời và được nhiều công ty công nghệ khuyên dùng. Để thực hiện một yêu cầu thanh toán tạm thời, một khóa tạm thời sẽ được thêm vào tiêu đề HTTP:
<idempotency-key: key_value>
Kịch bản 1: Nếu khách hàng nhấp nhanh vào nút “thanh toán” hai lần thì sao?
Khi người dùng nhấp vào “thanh toán”, khóa tạm thời sẽ được gửi đến hệ thống thanh toán như một phần của yêu cầu HTTP. Trong một trang web thương mại điện tử, khóa bình thường thường là ID của giỏ hàng ngay trước khi thanh toán.
Đối với yêu cầu thứ hai, yêu cầu này được xem là ‘thử lại’ vì hệ thống thanh toán đã thấy khóa tạm thời. Khi chúng ta đưa khóa bình thường được chỉ định trước đó vào tiêu đề yêu cầu, hệ thống thanh toán sẽ trả về trạng thái mới nhất của yêu cầu trước đó.

Nếu phát hiện thấy nhiều yêu cầu đồng thời có cùng khóa tạm thời, thì chỉ một yêu cầu được xử lý và yêu cầu còn lại nhận được mã trạng thái ‘429’ – ‘Quá nhiều yêu cầu’. Để hỗ trợ idempotency, chúng ta có thể sử dụng ràng buộc khóa duy nhất của cơ sở dữ liệu.
Xem thêm: Thiết kế hệ thống thanh toán (Phần 2)
Tính nhất quán
Một số dịch vụ có trạng thái được gọi trong quá trình thực hiện thanh toán.
(1). Dịch vụ thanh toán lưu giữ các dữ liệu liên quan đến thanh toán như nonce, nhiệm vụ, lệnh thanh toán, trạng thái thực hiện, …
(2) Sổ cái lưu giữ tất cả dữ liệu kế toán.
(3) Ví giữ số dư của người bán.
(4) PSP giữ trạng thái thực hiện thanh toán.
(5) Dữ liệu có thể được sao chép giữa các bản sao cơ sở dữ liệu khác nhau để tăng độ tin cậy.
Trong môi trường phân tán, việc giao tiếp giữa hai dịch vụ bất kỳ có thể bị lỗi, gây ra sự không nhất quán về dữ liệu. Để duy trì tính nhất quán của dữ liệu giữa hai dịch vụ nội bộ, việc đảm bảo xử lý chính xác một lần là rất quan trọng.
Để duy trì tính nhất quán của dữ liệu giữa các dịch vụ nội bộ và bên ngoài, chúng ta dựa vào idempotency và sự đối chiếu. Nếu dữ liệu được sao chép, độ trễ sao chép có thể khiến dữ liệu không nhất quán giữa cơ sở dữ liệu chính và bản sao. Chúng ta có hai lựa chọn để giải quyết điều này:
(1). Chỉ phục vụ cả việc đọc và ghi từ cơ sở dữ liệu chính. Cách tiếp cận này dễ cài đặt nhưng nhược điểm rõ ràng là khả năng mở rộng. Bản sao được sử dụng để đảm bảo độ tin cậy của dữ liệu nhưng chúng không phục vụ bất kỳ lưu lượng truy cập nào, gây lãng phí tài nguyên.
(2). Đảm bảo tất cả các bản sao luôn được đồng bộ hóa. Chúng ta có thể sử dụng các thuật toán đồng thuận như Paxos và Raft hoặc cơ sở dữ liệu phân tán dựa trên sự đồng thuận như Yugabyte hoặc CockroachDB.
Bảo mật thanh toán
Bảo mật thanh toán rất quan trọng và các kỹ thuật khác nhau được sử dụng là:
(1). Nghe lén yêu cầu/phản hồi (Request/response eavesdropping) bằng cách sử dụng HTTPS.
(2). Giả mạo dữ liệu (Data tampering) ‘thực thi mã hóa’ và giám sát tính toàn vẹn (nguyên trạng).
(3). Tấn công trung gian (Man-in-the-middle attack): Sử dụng SSL để ghim sự xác nhận.
(4) Mất dữ liệu: Sao chép cơ sở dữ liệu trên nhiều vùng và chụp ảnh nhanh dữ liệu.
5) Tấn công từ chối dịch vụ phân tán (DDoS): Giới hạn tỷ lệ (giới hạn tốc độ là một chiến lược để hạn chế lưu lượng mạng. Nó đặt ra giới hạn về tần suất có thể lặp lại một hành động trong một khung thời gian nhất định – ví dụ, cố gắng đăng nhập vào một tài khoản. Giới hạn tỷ lệ có thể giúp ngăn chặn một số loại hoạt động của bot phá hoại) và tường lửa.
6) Token hóa ‘trộm cắp thẻ’: Thay vì sử dụng số thẻ thật, token sẽ được lưu trữ và sử dụng để thanh toán.
(7). Tuân thủ PCI: PCI DDS là tiêu chuẩn bảo mật thông tin dành cho các tổ chức xử lý thẻ tín dụng có thương hiệu.
(8). Gian lận: Xác minh địa chỉ, giá trị xác minh thẻ (CVV) và phân tích hành vi người dùng.
Trong loạt bài viết này, chúng tôi đã cố gắng đề cập đến luồng thanh toán. Hiểu về việc thử lại, tính bình thường, tính nhất quán, xử lý lỗi thanh toán, đối chiếu và bảo mật.
Một hệ thống thanh toán thường phức tạp. Mặc dù chúng tôi đã đề cập đến nhiều chủ đề, nhưng còn nhiều điều cần phân tích sâu hơn mà chúng tôi sẽ cố gắng đề cập trong các bài viết sau của mình.
Tôi hy vọng nó có thể cung cấp cho bạn một số kiến thức về hệ thống thanh toán.
Hình minh họa: Thanh toán kỹ thuật số. Ảnh Freepik
Tác giả: Sourav Mansingh
Nguồn: Sourav Mansingh – medium.com – Ấn Độ
Tài liệu tham khảo
https://en.wikipedia.org/wiki/Payment_system
https://en.wikipedia.org/wiki/Card_scheme
https://developer.paypal.com/api/rest/
https://blog.bytebytego.com/ p/hệ thống thanh toán
https://en.wikipedia.org/wiki/Payment_Card_Industry_Data_Security_Standard