Tất cả bài viết
Deep diveĐọc trong 9 phút

Một giao dịch mua đang chờ trông giống một lượt bán, nhưng tiền chưa về, và cấp quyền sớm là cho không sản phẩm

Cả App Store lẫn Google Play đều có trạng thái mua đang chờ, một đơn hàng cửa hàng đã chấp nhận nhưng chưa tính phí. Mở khóa nó trước khi khoản thanh toán hoàn tất và mỗi đơn thất bại là chi phí thuần túy. Đây là cách các giao dịch mua đang chờ hoạt động trên từng cửa hàng, một đơn cấp nhầm tốn kém ra sao, và cách xử lý chúng mà không rò rỉ.

Một nhân viên thu ngân đưa một gói hàng nhỏ đã bọc qua quầy cửa hàng dưới ánh sáng ấm, minh họa một giao dịch mua đang chờ nơi hàng hóa chuyển đi trước khi khoản thanh toán hoàn tất

Điểm chính

  • Một giao dịch mua đang chờ là một đơn hàng thực mà cửa hàng đã chấp nhận nhưng chưa tính phí. Mã của bạn thấy một lượt mua mới, nhưng tiền chưa về, và có thể không bao giờ về.
  • Chỉ cấp quyền khi trạng thái là PURCHASED trên Google Play, hoặc khi giao dịch đã hoàn tất trên Apple. Đừng bao giờ mở khóa ở trạng thái PENDING hay ở kết quả pending của Apple.
  • Trên Google Play, thanh toán tiền mặt tại cửa hàng, chuyển khoản ngân hàng và một số hình thức tính phí qua nhà mạng được xử lý ngoài luồng chính, nên lượt mua trả về ở trạng thái PENDING, không phải PURCHASED, cho đến khi khách hàng thực sự trả tiền.
  • Trên Apple, một giao dịch mua đang chờ thường là Ask to Buy, nơi người tổ chức gia đình phải phê duyệt. Việc phê duyệt có thể mất hàng giờ hoặc hàng ngày, và giao dịch đã hoàn tất đến sau qua Transaction.updates.
  • Mở khóa trên một đơn đang chờ mà sau đó bị hủy và bạn đã tiêu tốn tài nguyên tính toán, lệnh gọi API, dung lượng lưu trữ, hoặc một khoản chi cho hàng tiêu hao cho một khoản phí chưa bao giờ hoàn tất. Khác với hoàn tiền, không có tiền để đòi lại, vì chưa bao giờ thu được đồng nào.
  • Cửa hàng báo cho bạn khi một đơn đang chờ thất bại. Google gửi ONE_TIME_PRODUCT_CANCELED, loại 2, hoặc SUBSCRIPTION_PENDING_PURCHASE_CANCELED, loại 20. Apple thì đơn giản là không bao giờ gửi một giao dịch đã hoàn tất.
  • Một đơn đang chờ có thể trở thành một lượt bán thực khi ứng dụng của bạn đã đóng, nên hãy kiểm tra lại khi quay lại: gọi queryPurchasesAsync() trong onResume() trên Google Play, và tiếp tục lắng nghe Transaction.updates trên Apple.

Ai đó chạm nút mua trong ứng dụng của bạn. Nhật ký hiển thị một đơn hàng mới, trình lắng nghe thanh toán của bạn kích hoạt, và bạn giao hàng. Với hầu hết các lượt mua thì điều đó hoàn toàn đúng. Với một giao dịch mua đang chờ thì đó là sai lầm, vì đơn hàng tồn tại nhưng tiền thì không. Khách hàng đã chọn một phương thức thanh toán hoàn tất muộn hơn, cửa hàng vẫn đang chờ được trả tiền, và bạn vừa giao một tính năng trả phí cho một khoản phí có thể không bao giờ hoàn tất. Đây là người anh em lặng lẽ của hoàn tiền. Ở đây không có gì bị đảo ngược, vì chưa bao giờ thu được gì. Bạn chỉ đơn giản là cho không sản phẩm.

Một giao dịch mua đang chờ là một đơn hàng thực ở trạng thái chưa được thanh toán, và cả App Store lẫn Google Play đều có nó. Cả hai cửa hàng đều nói rõ ràng là hãy chờ. Cái bẫy là một đơn đang chờ trông gần như giống hệt một đơn đã hoàn tất trong mã của bạn, nên một tích hợp coi mỗi lượt mua mới là một lượt bán sẽ giao hàng cho những đơn mà cửa hàng vẫn đang cố thu tiền. Xử lý đúng thì bạn không mất gì. Xử lý sai thì mỗi khoản thanh toán chậm thất bại là chi phí thuần túy, giao đi bằng túi tiền của bạn.

Giao dịch mua đang chờ thực sự là gì

Một giao dịch mua đang chờ là một đơn hàng mà cửa hàng đã ghi nhận nhưng chưa tính phí. Người mua đã bắt đầu quy trình, cửa hàng đã chấp nhận, và việc xử lý thanh toán đang diễn ra ở một nơi ứng dụng của bạn không thấy được. Google Play gọi đây là trạng thái PENDING. Apple gọi đây là một giao dịch đang chờ, hay bị hoãn. Tên khác nhau, sự thật giống nhau: cửa hàng giữ đơn hàng ở trạng thái mở trong khi chờ được trả tiền, và nó đã nói với bạn đừng coi đơn hàng đó là tiền.

Google Play: khoản thanh toán đang diễn ra ở nơi khác

Một số phương thức thanh toán được xử lý ngoài luồng chính. Tiền mặt tại cửa hàng vật lý, chuyển khoản ngân hàng, và một số hình thức tính phí qua nhà mạng đều cần thêm bước giữa lúc chạm và lúc tính phí. Khi khách hàng chọn một trong số đó, Google trả lượt mua về ở trạng thái PENDING thay vì PURCHASED. Với thanh toán tiền mặt, khách hàng nhận một mã qua thông báo và email, mang nó đến một cửa hàng tham gia, và trả cho thu ngân. Cho đến khi việc đó xảy ra, Google chưa thu được gì, và bạn cũng vậy. Quy tắc của Google chỉ gói gọn trong một dòng: dùng getPurchaseState() và chỉ cấp quyền khi trạng thái là PURCHASED. Nó cũng bảo bạn đừng xác nhận một lượt mua khi nó còn là PENDING, vì việc xác nhận thuộc về một đơn đã trả tiền, không phải một đơn được hứa hẹn.

Apple: lượt mua đang chờ một cú chạm của người khác

Trạng thái đang chờ của Apple nghĩa là giao dịch cần một hành động bên ngoài trước khi có thể hoàn tất. Phổ biến nhất là Ask to Buy, nơi một đứa trẻ bắt đầu một lượt mua và một người tổ chức gia đình phải phê duyệt. Trong StoreKit 2 lệnh gọi mua trả về Product.PurchaseResult.pending. Trong StoreKit cũ giao dịch được báo cáo là bị hoãn (deferred). Dù thế nào, Apple cũng chưa tính phí ai cả, và giao dịch đã hoàn tất, nếu đến, sẽ đến bất đồng bộ qua Transaction.updates. Hãy hiển thị cho khách hàng một trạng thái chờ và đừng mở khóa gì cho đến khi giao dịch đã hoàn tất về đến.

Vì sao cấp một giao dịch mua đang chờ khiến bạn mất tiền thật

Khoản mất mát ở đây không phải kiểu hoàn tiền, nơi số tiền bạn đã ghi nhận bị kéo lại. Nó tệ hơn ở một điểm cụ thể: không có tiền để kéo lại, vì chưa bao giờ thu được đồng nào.

Bạn giao hàng, và cửa hàng không bao giờ thu được tiền

Khi bạn mở khóa trên một đơn đang chờ mà sau đó bị hủy, bạn đã tiêu tiền để phục vụ nó rồi. Tài nguyên tính toán đã chạy tính năng đó, các lệnh gọi API bên thứ ba bạn đã trả tiền, dung lượng lưu trữ bạn đã cấp phát, và với một mặt hàng tiêu hao là khoản chi thực tế cho thứ bạn đã bán. Tất cả những cái đó ra đi cho một đơn hàng không tạo ra doanh thu nào. Một khoản hoàn tiền ít nhất còn bắt đầu từ một khoản phí đã xảy ra. Một giao dịch mua đang chờ bị cấp nhầm thì chưa từng có một khoản phí nào, nên nó thậm chí không hiện lên như tiền đi ra. Nó hiện lên như con số không, mà đó chính là lý do nó dễ bị bỏ sót và dễ lặp lại.

Tín hiệu hủy, và ý nghĩa của nó

Cửa hàng có báo cho bạn khi một đơn đang chờ chết. Trên Google Play, một sản phẩm mua một lần bị thất bại sẽ gửi thông báo ONE_TIME_PRODUCT_CANCELED, loại 2, và một gói đăng ký đang chờ sẽ gửi SUBSCRIPTION_PENDING_PURCHASE_CANCELED, loại 20. Khi chính những đơn đó lại thành công, bạn nhận ONE_TIME_PRODUCT_PURCHASED, loại 1, hoặc SUBSCRIPTION_PURCHASED, loại 4. Trên Apple không có sự kiện hủy nào để bắt, vì một giao dịch bị hoãn mà bị từ chối thì đơn giản là không bao giờ trở thành một giao dịch đã hoàn tất. Nếu bạn đã mở khóa sớm, sự im lặng đó chính là hóa đơn.

Câu hỏiGoogle PlayApple
Điều gì kích hoạtTiền mặt, chuyển khoản ngân hàng, một số hình thức tính phí qua nhà mạngPhê duyệt Ask to Buy, hoặc hành động bắt buộc khác
Trạng thái bạn thấyPurchaseState PENDINGkết quả pending, hoặc một giao dịch bị hoãn
Cấp quyền khiTrạng thái là PURCHASEDGiao dịch đã hoàn tất
Đã thành côngONE_TIME_PRODUCT_PURCHASED (1), SUBSCRIPTION_PURCHASED (4)Giao dịch đã hoàn tất qua Transaction.updates
Đã thất bạiONE_TIME_PRODUCT_CANCELED (2), SUBSCRIPTION_PENDING_PURCHASE_CANCELED (20)Không bao giờ có giao dịch đã hoàn tất về đến
Đã thu được tiền chưaChưa, cho đến khi PURCHASEDChưa, cho đến khi giao dịch hoàn tất

Khoảng thời gian, và ai đang chờ ai

Một giao dịch mua đang chờ không phải là một chiếc đồng hồ bạn đang chạy đua. Đó là một chiếc đồng hồ mà, về phía bạn, còn chưa bắt đầu chạy.

Google Play cho khách hàng nhiều ngày, không phải vài phút

Một khoản thanh toán tiền mặt hay chuyển khoản ngân hàng hoàn tất theo lịch của khách hàng, không phải của bạn. Đơn hàng nằm ở PENDING cho đến khi khách hàng trả tiền hoặc hết thời hạn và Google hủy nó. Cửa sổ xác nhận ba ngày của chính bạn, cái tự động hoàn tiền cho một lượt mua bạn không xác nhận, thậm chí còn chưa bắt đầu cho đến khi lượt mua chuyển từ PENDING sang PURCHASED. Vậy nên không có gì phải vội phục vụ một đơn đang chờ. Chỉ có kỷ luật chờ trạng thái thay đổi.

Sự phê duyệt của Apple nằm trên điện thoại của người tổ chức gia đình

Một yêu cầu Ask to Buy đến thiết bị của người tổ chức dưới dạng một lời nhắc mà họ phê duyệt hoặc từ chối bất cứ khi nào họ rảnh. Điều đó có thể là vài phút, vài giờ, hoặc một ngày sau, và ứng dụng của bạn không thể thúc giục nó. Hành vi đúng duy nhất là phản ánh trạng thái chờ và để StoreKit trao cho bạn giao dịch đã hoàn tất nếu và khi sự phê duyệt đến.

Một hộp các tông đã dán kín bên cạnh một chiếc đồng hồ cát trên bàn, minh họa một giao dịch mua đang chờ nơi hàng hóa đã sẵn sàng nhưng khoản thanh toán chưa hoàn tất

Cách xử lý các giao dịch mua đang chờ mà không rò rỉ

Toàn bộ công việc quy về bốn thói quen. Không cái nào khó, và bỏ sót bất kỳ cái nào là nơi tiền ra đi.

Cấp quyền ở trạng thái đã trả tiền, đừng bao giờ ở trạng thái đang chờ

Trên Google Play, kiểm tra getPurchaseState() và chỉ cấp trên PURCHASED, và đừng xác nhận một lượt mua khi nó còn là PENDING. Trên Apple, chỉ mở khóa trên một giao dịch đã hoàn tất và đừng bao giờ trên kết quả pending. Chỉ một quy tắc này đóng lại toàn bộ chỗ rò rỉ. Mọi thứ còn lại là làm sao để bạn thực sự nhận ra khi trạng thái đã trả tiền đến.

Kiểm tra lại khi ứng dụng quay lại

Sự chuyển từ đang chờ sang đã trả tiền thường xảy ra khi ứng dụng của bạn không chạy. Trên Google Play, gọi queryPurchasesAsync() trong trình xử lý onResume() để nhận những đơn đã trở thành PURCHASED ở nền, và giữ trình lắng nghe Real-time Developer Notifications của bạn làm nguồn chân lý phía máy chủ. Trên Apple, lắng nghe Transaction.updates trong suốt vòng đời của ứng dụng, vì một giao dịch được phê duyệt có thể đến rất lâu sau khi lệnh gọi mua ban đầu đã trả về.

Bật hỗ trợ đang chờ và kiểm thử cả hai kết cục

Google yêu cầu bạn gọi enablePendingPurchases() khi bạn dựng BillingClient, và việc hỗ trợ các giao dịch đang chờ cho sản phẩm mua một lần là bắt buộc, không phải tùy chọn. Kiểm thử nó trước khi bạn phát hành. Người kiểm thử giấy phép được cấp hai công cụ kiểm thử bổ sung cho các hình thức thanh toán trì hoãn, nơi khoản thanh toán tự động hoàn tất hoặc tự động hủy sau vài phút, để bạn có thể theo dõi cả đường trả tiền lẫn đường thất bại từ đầu đến cuối.

Nói cho khách hàng biết đơn hàng chưa xong

Một người mua đang chờ là một khách hàng thực sự đang giữa chừng một lượt mua, không phải một thất bại. Hãy cho họ thấy đơn hàng đang chờ khoản thanh toán hoặc một sự phê duyệt của họ, và cho họ một lối rõ ràng để quay lại hoàn tất nó. Một trạng thái chờ lặng lẽ làm mất những lượt bán mà một trạng thái được gắn nhãn tốt sẽ thu lại, vì phần lớn những người mua này vẫn muốn món hàng và chỉ còn một bước nữa.

Chính những luồng Real-time Developer Notifications và App Store Server Notifications mà RefundHalt vốn đã đọc cho hoàn tiền và bồi hoàn cũng mang những tín hiệu này. Thông báo đã mua nói rằng một đơn đang chờ cuối cùng đã hoàn tất, và thông báo đã hủy nói rằng nó không, rơi vào bảng điều khiển của bạn bên cạnh phần còn lại của các sự kiện doanh thu, nên một đơn đang chờ bị thất bại là thứ bạn có thể thấy thay vì thứ bạn vô tình phải trả tiền cho.

Tóm lại

Một giao dịch mua đang chờ là một đơn hàng không có khoản thanh toán, và cả hai cửa hàng đều nói rõ rằng bạn nên chờ. Google Play trả các đơn tiền mặt, chuyển khoản ngân hàng, và một số hình thức tính phí qua nhà mạng về ở trạng thái PENDING và bảo bạn chỉ cấp quyền trên PURCHASED. Apple trả về một giao dịch đang chờ hoặc bị hoãn cho Ask to Buy và các hành động bắt buộc khác rồi giao giao dịch đã hoàn tất sau đó qua Transaction.updates. Hãy mở khóa ở trạng thái đã trả tiền, kiểm tra lại khi ứng dụng của bạn tiếp tục, bật và kiểm thử hỗ trợ đang chờ, và gắn nhãn đơn hàng đang chờ cho khách hàng. Làm được vậy thì một giao dịch mua đang chờ không tốn của bạn đồng nào. Bỏ qua nó thì bạn giao một sản phẩm trả phí cho một khoản phí không bao giờ đến, đó là khoản mất mát duy nhất không có hóa đơn nào để chỉ vào.

Câu hỏi thường gặp

Giao dịch mua đang chờ là gì?
Một giao dịch mua đang chờ là một đơn hàng cửa hàng đã chấp nhận nhưng chưa tính phí. Trên Google Play đó là trạng thái mua PENDING, dùng cho các phương thức thanh toán hoàn tất muộn hơn như tiền mặt, chuyển khoản ngân hàng, và một số hình thức tính phí qua nhà mạng. Trên Apple đó là một giao dịch đang chờ hoặc bị hoãn, thường là một lượt mua Ask to Buy đang chờ sự phê duyệt của người tổ chức gia đình. Trong cả hai trường hợp chưa có tiền nào được thu, nên bạn không nên cấp quyền.
Tôi có nên cấp quyền khi một lượt mua đang chờ không?
Không. Chỉ cấp quyền khi trạng thái là PURCHASED trên Google Play, hoặc khi giao dịch đã hoàn tất trên Apple. Nếu bạn mở khóa một tính năng khi đơn hàng vẫn đang chờ và khoản thanh toán không bao giờ hoàn tất, bạn đã giao sản phẩm miễn phí, và không có khoản phí nào để đảo ngược vì chưa bao giờ có khoản phí nào được tạo.
Những phương thức thanh toán nào gây ra một giao dịch mua đang chờ trên Google Play?
Các phương thức thanh toán được xử lý ngoài luồng chính. Thanh toán tiền mặt tại một cửa hàng vật lý, chuyển khoản ngân hàng, và một số tùy chọn tính phí qua nhà mạng cần thêm bước giữa lúc chạm và lúc tính phí, nên Google trả lượt mua về ở trạng thái PENDING thay vì PURCHASED. Với thanh toán tiền mặt, khách hàng nhận một mã qua thông báo và email, rồi trả tiền tại một cửa hàng tham gia.
Ask to Buy là gì, và nó liên quan thế nào đến các giao dịch mua đang chờ?
Ask to Buy là tính năng Family Sharing của Apple cho phép một đứa trẻ yêu cầu một lượt mua mà một người tổ chức gia đình phải phê duyệt. Trong khi yêu cầu đang chờ, lượt mua ở trạng thái đang chờ của Apple, được trả về là Product.PurchaseResult.pending trong StoreKit 2 hoặc một giao dịch bị hoãn trong StoreKit cũ. Giao dịch đã hoàn tất chỉ đến, qua Transaction.updates, nếu và khi người tổ chức phê duyệt nó.
Chuyện gì xảy ra nếu một giao dịch mua đang chờ không bao giờ được trả tiền?
Đơn hàng bị hủy và không có tiền nào đổi chủ. Trên Google Play bạn nhận một thông báo ONE_TIME_PRODUCT_CANCELED, loại 2, cho một sản phẩm mua một lần, hoặc SUBSCRIPTION_PENDING_PURCHASE_CANCELED, loại 20, cho một gói đăng ký. Trên Apple, giao dịch bị hoãn đơn giản là không bao giờ trở thành một giao dịch đã hoàn tất. Nếu bạn đã cấp quyền, đó chính là lúc khoản mất mát trở thành thực.
Một giao dịch mua đang chờ có giống một khoản hoàn tiền không?
Không. Một khoản hoàn tiền đảo ngược một khoản thanh toán đã thực sự được thu. Một giao dịch mua đang chờ bị thất bại thì chưa bao giờ bị tính phí ngay từ đầu, nên không có gì để đảo ngược và không có gì hiện lên trong một báo cáo hoàn tiền. Nếu bạn đã mở khóa nó sớm, chi phí là tài nguyên tính toán, lệnh gọi API, dung lượng lưu trữ, hoặc mặt hàng tiêu hao mà bạn đã tiêu để phục vụ một đơn hàng không tạo ra doanh thu.

Nguồn và tài liệu đọc thêm

RefundHalt

Chế độ tự động xử lý hoàn tiền cho App Store và Google Play

Đọc tiếp

Yêu cầu hoàn tiền tiếp theo đã đang trên đường đến.

Thiết lập RefundHalt trong khoảng thời gian bạn cần để đọc thêm một email hỗ trợ về khoản hoàn tiền mà mình chưa kịp phản biện.