Cổng thanh toán Stripe (Phần 5): Payment Intent

9 min read

Stripe Checkout là lựa chọn nhanh và an toàn cho phần lớn nhu cầu thanh toán online. Tuy nhiên, không phải sản phẩm nào cũng muốn chuyển khách hàng sang một trang thanh toán dựng sẵn. Nhiều website cần giữ khách hàng trong cùng giao diện, tùy chỉnh layout, kiểm soát trải nghiệm checkout hoặc xử lý logic thanh toán phức tạp hơn.

Đó là lúc Payment Intent và Stripe Elements trở nên quan trọng.

Nếu Stripe Checkout giống như một trang thanh toán hoàn chỉnh do Stripe chuẩn bị sẵn, thì Stripe Elements là bộ thành phần giao diện giúp developer xây dựng form thanh toán tùy chỉnh. Còn Payment Intent là đối tượng cốt lõi theo dõi toàn bộ vòng đời của một giao dịch.

Bài viết này sẽ giải thích Payment Intent là gì, Stripe Elements hoạt động ra sao, khi nào nên dùng thay vì Checkout và những lưu ý quan trọng khi triển khai. Nếu bạn chưa đọc các phần trước, có thể xem lại phần 3 về cách Stripe hoạt độngphần 4 về tích hợp Stripe Checkout để nắm rõ luồng thanh toán tổng thể.

Payment Intent là gì?

Theo tài liệu Payment Intents API của Stripe, Payment Intent là một đối tượng trong Stripe đại diện cho ý định thanh toán của khách hàng. Nó không chỉ lưu số tiền cần thanh toán, mà còn theo dõi toàn bộ trạng thái của giao dịch từ lúc bắt đầu đến khi thành công, thất bại hoặc bị hủy.

Trong các hệ thống thanh toán hiện đại, một giao dịch không phải lúc nào cũng chỉ có hai trạng thái “thành công” hoặc “thất bại”. Một số giao dịch cần xác thực 3D Secure, một số phương thức thanh toán xử lý bất đồng bộ, một số giao dịch có thể đang chờ ngân hàng phản hồi.

Payment Intent giúp Stripe quản lý các trường hợp đó rõ ràng hơn.

Một Payment Intent thường chứa:

  • số tiền cần thanh toán;
  • loại tiền tệ;
  • phương thức thanh toán;
  • trạng thái hiện tại;
  • thông tin xác thực bổ sung nếu có;
  • metadata liên kết với hệ thống nội bộ;
  • thông tin charge sau khi thanh toán thành công.

Vì sao Stripe cần Payment Intent?

Trước đây, nhiều hệ thống thanh toán chỉ xử lý theo kiểu tạo một charge trực tiếp. Cách này đơn giản nhưng không còn phù hợp với các yêu cầu thanh toán hiện đại như xác thực mạnh, nhiều phương thức thanh toán, thanh toán bất đồng bộ hoặc retry khi thất bại.

Payment Intent giải quyết vấn đề này bằng cách tách giao dịch thành một vòng đời rõ ràng.

Ví dụ:

Tạo Payment Intent -> Gắn payment method -> Cần xác thực -> Khách hàng xác thực -> Xử lý -> Thành công

Nhờ đó, backend và frontend có thể phối hợp tốt hơn. Frontend xử lý phần tương tác với khách hàng, backend tạo Payment Intent và webhook xác nhận trạng thái cuối cùng.

Các trạng thái thường gặp của Payment Intent

Khi làm việc với Stripe, developer nên hiểu các trạng thái phổ biến sau:

  • requires_payment_method: Payment Intent cần một phương thức thanh toán hợp lệ.
  • requires_confirmation: Payment Intent đã có thông tin cần thiết và cần được confirm.
  • requires_action: Khách hàng cần thực hiện thêm hành động, ví dụ xác thực 3D Secure.
  • processing: Stripe đang xử lý thanh toán.
  • requires_capture: Thanh toán đã được authorize nhưng chưa capture, thường dùng trong mô hình giữ tiền trước.
  • succeeded: Thanh toán thành công.
  • canceled: Payment Intent đã bị hủy.

Không nên chỉ xử lý một trạng thái succeeded. Trong thực tế, hệ thống cần xử lý cả trạng thái cần xác thực, đang xử lý, thất bại hoặc bị hủy để trải nghiệm người dùng rõ ràng hơn.

Stripe Elements là gì?

Stripe Elements là bộ UI component do Stripe cung cấp để developer xây dựng form thanh toán tùy chỉnh. Các component này giúp thu thập thông tin thanh toán một cách an toàn mà không để dữ liệu thẻ đi qua server của doanh nghiệp.

Thay vì tự tạo input số thẻ, ngày hết hạn, CVC và tự validate, developer dùng component của Stripe. Dữ liệu nhạy cảm được Stripe xử lý trực tiếp, giúp giảm rủi ro bảo mật và giảm phạm vi tuân thủ PCI.

Hiện nay, Payment Element là lựa chọn phổ biến vì nó có thể hiển thị nhiều phương thức thanh toán trong cùng một component, thay vì chỉ tập trung vào thẻ. Một điểm cần lưu ý là Stripe hiện khuyến nghị nhiều tích hợp nên ưu tiên Checkout Sessions API kết hợp Payment Element trước, còn Payment Intents API phù hợp hơn khi dự án cần kiểm soát sâu hoặc có flow thanh toán phức tạp.

Stripe Elements phù hợp khi:

  • cần giữ khách hàng trong giao diện website;
  • cần checkout tùy chỉnh theo brand;
  • cần kiểm soát layout nhiều hơn Stripe Checkout;
  • cần luồng thanh toán đặc thù;
  • cần kết hợp thanh toán với form nghiệp vụ khác.

Stripe Checkout và Stripe Elements khác nhau thế nào?

Stripe Checkout:

  • triển khai nhanh;
  • giao diện do Stripe quản lý;
  • ít code hơn;
  • phù hợp phần lớn use case phổ thông;
  • ít rủi ro sai flow;
  • tùy chỉnh giao diện có giới hạn.

Stripe Elements:

  • tùy chỉnh giao diện sâu hơn;
  • nhiều code hơn;
  • cần hiểu Payment Intent rõ hơn;
  • phù hợp sản phẩm cần trải nghiệm checkout riêng;
  • developer chịu trách nhiệm nhiều hơn trong flow;
  • cần kiểm thử kỹ hơn.

Nếu dự án cần ra mắt nhanh, Checkout thường là lựa chọn tốt. Nếu sản phẩm đã có yêu cầu UX cụ thể hoặc checkout là một phần quan trọng của trải nghiệm sản phẩm, Elements đáng để cân nhắc.

Luồng tích hợp Payment Intent với Elements

Một flow cơ bản có thể như sau:

  1. Khách hàng mở trang thanh toán.
  2. Frontend gọi backend để tạo Payment Intent.
  3. Backend tạo Payment Intent với số tiền, currency và metadata.
  4. Backend trả client_secret về frontend.
  5. Frontend dùng Stripe Elements để thu thập thông tin thanh toán.
  6. Frontend confirm payment bằng client_secret.
  7. Stripe xử lý xác thực nếu cần.
  8. Backend nhận webhook để xác nhận thanh toán cuối cùng.

Điểm cần nhớ: client_secret được dùng ở frontend để hoàn tất giao dịch, nhưng secret key của Stripe vẫn chỉ được dùng ở backend.

Có nên fulfill order ngay sau khi frontend báo thành công?

Không nên chỉ dựa vào frontend.

Frontend có thể hiển thị thanh toán thành công để cải thiện trải nghiệm người dùng, nhưng backend vẫn nên chờ webhook như payment_intent.succeeded hoặc event phù hợp để cập nhật trạng thái đơn hàng chính thức. Nội dung này liên quan trực tiếp đến phần webhook trong Stripe payment flow.

Lý do:

  • người dùng có thể đóng trình duyệt;
  • mạng có thể lỗi sau khi thanh toán;
  • một số phương thức thanh toán xử lý bất đồng bộ;
  • callback frontend có thể bị giả mạo nếu hệ thống thiết kế sai;
  • webhook là luồng server-to-server đáng tin cậy hơn.

Các lỗi thường gặp khi dùng Payment Intent

Lỗi 1: Tạo Payment Intent với số tiền từ frontend mà không kiểm tra lại.

Backend không nên tin giá tiền do frontend gửi lên. Giá cần được lấy từ database hoặc tính toán lại trên server.

Lỗi 2: Không xử lý requires_action.

Nếu bỏ qua trạng thái này, các giao dịch cần xác thực 3D Secure có thể thất bại hoặc trải nghiệm người dùng bị kẹt.

Lỗi 3: Không dùng webhook.

Payment Intent giúp frontend xử lý thanh toán, nhưng webhook vẫn cần để backend xác nhận nghiệp vụ.

Lỗi 4: Tạo quá nhiều Payment Intent không cần thiết.

Mỗi lần người dùng thay đổi giỏ hàng hoặc reload trang, hệ thống cần có chiến lược rõ ràng: tạo mới, cập nhật hoặc hủy Payment Intent cũ.

Lỗi 5: Không lưu payment_intent_id.

Nếu không lưu ID này, việc tra cứu, refund, đối soát và debug sau này sẽ khó khăn.

Khi nào nên chọn Checkout, khi nào nên chọn Elements?

Nên chọn Stripe Checkout nếu:

  • muốn tích hợp nhanh;
  • không cần tùy chỉnh giao diện sâu;
  • muốn giảm khối lượng code;
  • team chưa có nhiều kinh nghiệm payment;
  • cần một flow ổn định, ít bảo trì.

Nên chọn Stripe Elements nếu:

  • checkout cần nằm trong cùng website;
  • UI/UX là yếu tố quan trọng;
  • cần kết hợp thanh toán với form phức tạp;
  • cần kiểm soát nhiều bước trong flow;
  • sản phẩm đã đủ trưởng thành để đầu tư vào checkout riêng.

Một chiến lược thực tế là bắt đầu bằng Checkout để ra mắt nhanh, sau đó chuyển sang Elements khi sản phẩm có dữ liệu rõ ràng về hành vi người dùng và nhu cầu tối ưu checkout.

FAQ

Payment Intent có thay thế Checkout Session không?

Không hoàn toàn. Checkout Session thường dùng trong Stripe Checkout và bên trong có thể liên kết đến Payment Intent hoặc Subscription tùy mode. Payment Intent là đối tượng cốt lõi của một giao dịch thanh toán, còn Checkout Session là phiên thanh toán của Stripe Checkout.

Stripe Elements có an toàn không?

Có, nếu tích hợp đúng cách. Stripe Elements giúp thu thập dữ liệu thanh toán qua component của Stripe, hạn chế việc dữ liệu thẻ đi qua server của doanh nghiệp.

Có cần backend khi dùng Stripe Elements không?

Có. Backend cần tạo Payment Intent, kiểm tra giá tiền, lưu dữ liệu giao dịch và xử lý webhook.

Kết luận

Payment Intent và Stripe Elements là phần quan trọng khi doanh nghiệp muốn xây dựng trải nghiệm thanh toán tùy chỉnh hơn Stripe Checkout. Payment Intent giúp theo dõi vòng đời giao dịch, còn Elements giúp thu thập thông tin thanh toán an toàn ngay trong giao diện website.

Với dự án mới, Stripe Checkout thường là lựa chọn tốt để bắt đầu. Khi sản phẩm cần checkout riêng, Payment Intent và Elements sẽ cho developer nhiều quyền kiểm soát hơn, nhưng cũng đòi hỏi thiết kế kỹ hơn, đặc biệt ở phần webhook, trạng thái giao dịch và xử lý lỗi.

Nguồn tham khảo

Bài viết liên quan

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *