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

Khi một giao dịch mua trên Google Play được hoàn tiền hoặc bị chargeback, Voided Purchases API là cách bạn biết được điều đó

Google Play âm thầm hủy một giao dịch mua khi nó được hoàn tiền hoặc bị chargeback. Voided Purchases API là danh sách những đơn hàng đó, để bạn có thể thu hồi quyền truy cập. Đây là từng field, cửa sổ 30 ngày, tùy chọn revoke làm ẩn đơn hàng, và chi phí của nó.

Một chiếc smartphone bên cạnh một cuốn sổ cái bằng giấy, một ổ khóa đồng đã khóa, và một đồng xu đang trượt ra xa, minh họa Google Play Voided Purchases API báo cáo các đơn hàng được hoàn tiền và bị chargeback

Điểm chính

  • Voided Purchases API, tức phương thức purchases.voidedpurchases.list, trả về các đơn hàng mà Google Play đã hủy, hoàn tiền hoặc chargeback, để bạn có thể xây dựng một hệ thống thu hồi cắt quyền truy cập vào thứ mà khách hàng không còn sở hữu.
  • Chỉ những đơn hàng đã revoke mới xuất hiện. Một khoản hoàn tiền do nhà phát triển thực hiện mà không có tùy chọn revoke sẽ vô hình với API này, vì vậy nếu bạn muốn rút quyền truy cập, bạn phải hoàn tiền với revoke được bật.
  • Cửa sổ là 30 ngày. startTime không thể cũ hơn 30 ngày trước, vì vậy một máy chủ ngừng hoạt động lâu hơn một tháng sẽ mất vĩnh viễn những đơn hàng bị hủy đó. Hãy poll theo lịch.
  • voidedSource cho bạn biết ai đã hủy đơn hàng: 0 là người dùng, 1 là nhà phát triển, 2 là Google. voidedReason cho bạn biết lý do, từ 0 Other đến 7 Chargeback và 8 Unacknowledged_purchase.
  • Real-time developer notifications đẩy một VoidedPurchaseNotification ngay khi một giao dịch mua bị hủy, nhưng hãy xem nó như một tín hiệu. Hãy gọi Voided Purchases API để lấy danh sách chính thức trước khi bạn thu hồi.
  • Xác định các lần gia hạn đăng ký bằng orderId, không phải purchaseToken. Một purchaseToken bao trùm mọi lần gia hạn của một đăng ký, vì vậy chỉ riêng token không thể phân biệt hai lần gia hạn.
  • Hạn mức là 6,000 truy vấn mỗi ngày và 30 truy vấn trong bất kỳ cửa sổ 30 giây nào, vì vậy hãy phân trang qua kết quả bằng continuation token và truy vấn theo cửa sổ thời gian, đừng bao giờ gọi một lần cho mỗi đơn hàng.

Một khoản hoàn tiền trên Google Play không gõ cửa nhà bạn. Tiền được chuyển đi, khách hàng vẫn mở ứng dụng, và trừ khi bạn đi tìm, không có gì thay đổi ở phía bạn. Voided Purchases API là nơi bạn đi tìm. Nó đưa cho bạn một danh sách các đơn hàng đã bị hủy, hoàn tiền hoặc chargeback, để bạn có thể thu hồi quyền truy cập vào thứ mà khách hàng không còn trả tiền nữa. Hướng một scheduled job vào đó, đọc danh sách, cắt entitlement. Đó là toàn bộ vòng lặp.

Có một cái bẫy khiến hầu hết các nhóm vấp phải, và nó không nằm trong code. Chỉ những đơn hàng đã được revoke mới hiển thị ở đây. Nếu bạn hoàn tiền một giao dịch mua trong Play Console mà không tích tùy chọn revoke, đơn hàng đó sẽ không bao giờ đến được API này, và job của bạn chạy trơn tru trong khi một khách hàng đã được hoàn tiền vẫn giữ mọi thứ bạn đã bán cho họ. Bài viết này đi qua API theo từng field, những con số giới hạn nó, và nơi tiền rò rỉ khi bạn bỏ qua nó.

Voided Purchases API thực sự trả về những gì

API trả lời một câu hỏi: những đơn hàng nào của ứng dụng này gần đây đã bị hủy. Một lần hủy (void) bao gồm ba kết cục đều kết thúc bằng việc khách hàng lấy lại tiền. Một lần hủy, một lần hoàn tiền, hoặc một lần chargeback. Nó áp dụng cho các sản phẩm trong ứng dụng mua một lần và cho các đăng ký, và bạn chọn phạm vi bằng một tham số duy nhất. Đặt type bằng 0 và bạn chỉ nhận được các giao dịch mua sản phẩm trong ứng dụng bị hủy, đây là mặc định. Đặt bằng 1 và bạn nhận được cả các giao dịch mua trong ứng dụng bị hủy lẫn các giao dịch mua đăng ký bị hủy.

Mỗi mục trong danh sách là một đối tượng voided purchase. Các field thì ít và mỗi field đều quan trọng.

Các field trên một voided purchase

FieldNó chứa gì
orderIdOrder id nhận diện duy nhất một giao dịch mua một lần, một giao dịch mua đăng ký, hoặc một lần gia hạn đăng ký đơn lẻ. Đây là khóa join của bạn
purchaseTokenToken nhận diện một giao dịch mua một lần hoặc một đăng ký. Nó không phân biệt các lần gia hạn, vì vậy hãy dùng orderId cho việc đó
purchaseTimeMillisThời điểm giao dịch mua được thực hiện, tính bằng mili giây kể từ epoch
voidedTimeMillisThời điểm giao dịch mua bị hủy, hoàn tiền hoặc chargeback, tính bằng mili giây kể từ epoch
voidedSourceAi khởi tạo việc hủy: 0 người dùng, 1 nhà phát triển, 2 Google
voidedReasonLý do giao dịch mua bị hủy, một số nguyên từ 0 đến 8
voidedQuantitySố lượng bị hủy từ một khoản hoàn tiền một phần dựa trên số lượng, chỉ được trả về khi includeQuantityBasedPartialRefund là true

Đọc voidedReason trước khi bạn hành động

voidedReason là field biến một danh sách thô thành một quyết định. Một khoản hoàn tiền do hối tiếc của người mua và một khoản chargeback từ ngân hàng đều rơi vào cùng một danh sách, nhưng chúng không phải là cùng một sự kiện, và bảng giá tháng Tám khiến một trong số chúng trở nên đắt đỏ. Đây là bộ đầy đủ.

voidedReasonLabelNó có ý nghĩa gì với bạn
0OtherKhông có danh mục nào được gán. Thu hồi và tiếp tục
1RemorseNgười mua đổi ý. Một khoản hoàn tiền thông thường
2Not_receivedKhách hàng nói rằng họ chưa bao giờ nhận được sản phẩm. Đáng để kiểm tra khâu giao hàng của bạn
3DefectiveSản phẩm không hoạt động. Một tín hiệu chất lượng, hãy ghi lại
4Accidental_purchaseMột giao dịch mua ngoài ý muốn, thường trên thiết bị dùng chung
5FraudGoogle đánh dấu giao dịch là gian lận
6Friendly_fraudMột khoản chargeback trong đó chủ thẻ hợp pháp tranh chấp một khoản phí do chính họ thực hiện
7ChargebackNgân hàng của khách hàng đảo ngược khoản thanh toán. Chung cuộc với ngân hàng, và giờ bị tính vào bạn
8Unacknowledged_purchaseGoogle tự động hoàn tiền một giao dịch mua mà ứng dụng của bạn chưa bao giờ acknowledge

Cửa sổ 30 ngày là cái bẫy làm rỗng danh sách của bạn

Voided Purchases API chỉ có thể hiển thị các giao dịch mua bị hủy trong 30 ngày qua. Tham số startTime mặc định bằng thời điểm hiện tại trừ đi 30 ngày, và không thể đặt cũ hơn thế. endTime mặc định là bây giờ. Vì vậy endpoint là một cửa sổ một tháng cuộn, không phải một kho lưu trữ.

Hệ quả rất thẳng thừng. Nếu polling job của bạn hỏng và không ai nhận ra trong năm tuần, các lần hủy từ tuần đầu tiên đã hết hạn khỏi API. Không có lệnh gọi nào mang chúng trở lại. Bạn sẽ không thu hồi những đơn hàng đó, và bạn thậm chí sẽ không biết chúng từng tồn tại trừ khi bạn đã ghi lại chúng bằng cách khác. API là một tấm lưới an toàn với một cái lỗ có kích cỡ bằng lần gián đoạn tồi tệ nhất của bạn.

Tùy chọn revoke quyết định liệu một đơn hàng có xuất hiện hay không

Đây là lý do phổ biến nhất khiến một nhóm báo cáo API bị hỏng. Chỉ những đơn hàng đã revoke mới được trả về. Các khoản hoàn tiền do người dùng khởi tạo, các lần hủy, các lần chargeback, và các khoản hoàn tiền do Google khởi tạo luôn được revoke, vì vậy chúng luôn xuất hiện. Một khoản hoàn tiền do nhà phát triển khởi tạo thì khác. Khi bạn tự hoàn tiền một đơn hàng, qua Play Console hoặc Orders API, bạn chọn có revoke nó hay không. Hoàn tiền mà không revoke, và đơn hàng được giải quyết với khách hàng nhưng không bao giờ hiện ra trong Voided Purchases API.

Quy tắc theo sau rất đơn giản. Nếu ý định của bạn là rút quyền truy cập, hãy hoàn tiền với tùy chọn revoke được bật. Nếu không, bạn đã trả lại tiền và để ngỏ cửa, và revocation job của bạn, dù được viết tốt đến đâu, cũng không có gì để hành động.

Cách poll mà không vượt hạn mức

Endpoint bị giới hạn tốc độ, và các giới hạn đủ thấp để một vòng lặp ngây thơ sẽ chạm phải chúng. Bạn có 6,000 truy vấn mỗi ngày, tính theo Pacific Time, và không quá 30 truy vấn trong bất kỳ khoảng 30 giây nào. Ngân sách đó ổn cho việc poll theo cửa sổ và không thân thiện với các thiết kế một-yêu-cầu-cho-mỗi-đơn-hàng.

Các cửa sổ truy vấn và continuation token

maxResults mặc định là 1,000, cũng chính là giới hạn trên. Khi một cửa sổ chứa nhiều hơn một trang các lần hủy, phản hồi mang theo một đối tượng tokenPagination với một nextPageToken. Gửi lại token đó ở lệnh gọi tiếp theo để đi qua các trang. Đặt startTime và endTime để giới hạn cửa sổ bạn quan tâm, phân trang cho đến khi hết token, rồi tiến cửa sổ tới. Mẫu đó giữ bạn nằm trong cả giới hạn burst 30 giây lẫn hạn mức hằng ngày.

Real-time developer notifications lấp đầy khoảng trống

Poll mỗi ngày vẫn để lại tối đa một ngày mù, và cửa sổ 30 ngày trừng phạt những khoảng trống dài. Real-time developer notifications loại bỏ độ trễ đó. Google phát hành một VoidedPurchaseNotification tới một chủ đề Cloud Pub/Sub mà bạn sở hữu ngay khi một giao dịch mua bị hủy, và backend của bạn tiêu thụ nó trong vài giây. Thông điệp thì nhỏ.

RTDN fieldNó chứa gì
purchaseTokenToken từ giao dịch mua ban đầu
orderIdOrder id cho giao dịch bị hủy, một cái mới cho mỗi lần gia hạn đăng ký
productType1 cho một đăng ký, 2 cho một giao dịch mua một lần
refundType1 cho hoàn tiền toàn phần, 2 cho hoàn tiền một phần dựa trên số lượng
Một bàn tay đóng một ổ khóa đồng trên một chồng hóa đơn bên cạnh một chiếc smartphone, minh họa việc thu hồi quyền truy cập sau khi một giao dịch mua trên Google Play bị hủy

Điều này khiến bạn tốn bao nhiêu tiền

API là hệ thống ống dẫn, nhưng lý do để lắp đặt nó là một hóa đơn. Mỗi lần hủy trong danh sách đó ánh xạ tới một con số thật, và hai trong số chúng đang trở nên đắt hơn.

Hóa đơn chargeback rơi vào bạn từ ngày 3 tháng 8 năm 2026

Bắt đầu từ ngày 3 tháng 8 năm 2026, Google chuyển chi phí của một khoản chargeback sang nhà phát triển. Bạn mất giá mua và bạn phải trả thêm phí chargeback của ngân hàng. Một voidedReason bằng 7 không còn chỉ là một giao dịch bán bị mất, nó là một mục có kèm phí. Bạn không thể đảo ngược một khoản chargeback, nó chung cuộc với ngân hàng, nhưng bạn có thể cầm máu sau đó. Bắt kịp lần hủy nhanh cho phép bạn thu hồi entitlement và, đối với bất cứ thứ gì bạn vẫn đang cung cấp, ngừng chi tiêu cho một khách hàng đã được hoàn tiền rồi bị đảo ngược.

Bạn vẫn tiếp tục trả tiền để phục vụ một khách hàng đã được hoàn tiền

Giá mua biến mất ngay khi một lần hủy xuất hiện. Thứ bạn vẫn kiểm soát là chi phí tiếp tục cung cấp. Mỗi giờ một entitlement đã được hoàn tiền còn hoạt động, bạn vẫn trả tiền cho những thứ mà khách hàng không còn tài trợ nữa: điện toán, các lệnh gọi API mô hình, lưu trữ, và bất kỳ khoản chi trả cho nhà sáng tạo hay đối tác nào gắn với việc sử dụng của họ. Một hệ thống thu hồi được vận hành bởi API này là cách bạn tắt cái đồng hồ đo đó. Bỏ qua nó, và bạn tài trợ cho sản phẩm dành cho những người mà cửa hàng đã bồi hoàn xong.

Friendly fraud là một mẫu hình đáng để theo dõi xu hướng

Một voidedReason bằng 5 hoặc 6 không phải là chuyện xảy ra một lần. Fraud và friendly fraud kết cụm theo tài khoản, theo thiết bị, và đôi khi theo chương trình khuyến mãi. API cung cấp cho bạn voidedSource và voidedReason trên mỗi lần hủy, đủ để theo dõi xu hướng lạm dụng theo tài khoản thay vì coi mỗi lần đảo ngược như một chi phí riêng lẻ. Một khách hàng chargeback hai lần đang nói với bạn điều gì đó mà lần hoàn tiền đầu tiên không nói.

Lắp đặt theo cách của RefundHalt

Mô hình rất nhỏ một khi bạn nắm được tất cả các mảnh ghép. Lắng nghe VoidedPurchaseNotification theo thời gian thực để không có gì phải chờ trọn một ngày. Gọi Voided Purchases API làm nguồn sự thật, khóa theo orderId để các lần gia hạn đăng ký không bao giờ bị nhầm lẫn. Đọc voidedSource và voidedReason để một khoản chargeback được xử lý khác với một khoản hoàn tiền do hối tiếc. Poll theo một lịch đủ chặt để cửa sổ 30 ngày không bao giờ cắn, và hoàn tiền với tùy chọn revoke được bật bất cứ khi nào ý định của bạn là cắt quyền truy cập.

Đây là phần RefundHalt chạy thay cho bạn. Nó tiêu thụ các thông báo thời gian thực, đối soát mọi lần hủy với API, thu hồi đúng đơn hàng thay vì toàn bộ sản phẩm, và tách một khoản chargeback ngân hàng khỏi một khoản hoàn tiền thông thường để những khoản đắt đỏ được đánh dấu, không bị chôn vùi. Bạn được thu hồi quyền truy cập trong vài giây và một bản ghi về việc ai đã hủy cái gì và tại sao, mà không phải tự dựng lên một pipeline Pub/Sub và một polling job.

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

Tại sao các đơn hàng đã hoàn tiền của tôi không hiển thị trong Voided Purchases API?
Bởi vì chỉ những đơn hàng đã revoke mới được trả về. Các khoản hoàn tiền của người dùng, các lần hủy, các lần chargeback, và các khoản hoàn tiền do Google khởi tạo luôn được revoke và luôn xuất hiện. Một khoản hoàn tiền do nhà phát triển khởi tạo chỉ xuất hiện nếu bạn cũng đã chọn tùy chọn revoke. Nếu bạn hoàn tiền một đơn hàng mà không revoke nó, đơn hàng được giải quyết nhưng vô hình với API này, vì vậy hãy hoàn tiền với revoke được bật bất cứ khi nào bạn có ý định rút quyền truy cập.
Voided Purchases API truy ngược lại được bao xa?
Ba mươi ngày. Tham số startTime mặc định bằng thời điểm hiện tại trừ đi 30 ngày và không thể đặt cũ hơn thế, vì vậy endpoint là một cửa sổ một tháng cuộn thay vì một kho lưu trữ. Một đơn hàng bị hủy đã quá 30 ngày sẽ biến mất khỏi API mà không có cách nào lấy lại, đó là lý do bạn poll theo lịch và bổ trợ nó bằng các thông báo thời gian thực.
Tôi nên dùng real-time developer notifications hay Voided Purchases API để thu hồi quyền truy cập?
Hãy dùng cả hai. VoidedPurchaseNotification đến trong vài giây và bảo bạn hãy nhìn, nhưng chính hướng dẫn của Google là xem nó như một tín hiệu, không phải một nguồn sự thật. Hãy gọi Voided Purchases API để xác nhận trạng thái hiện tại, rồi thu hồi. Thông báo loại bỏ độ trễ, và API cung cấp cho bạn voidedSource và voidedReason chính thức để hành động.
Làm thế nào để phân biệt một khoản chargeback với một khoản hoàn tiền thông thường trong API?
Hãy đọc field voidedReason. Giá trị 7 là một khoản chargeback, nghĩa là ngân hàng của khách hàng đã đảo ngược khoản thanh toán, và 6 là friendly fraud. Giá trị 1 là một khoản hoàn tiền do hối tiếc. Điều này quan trọng vì từ ngày 3 tháng 8 năm 2026 Google chuyển giá mua chargeback và phí ngân hàng sang nhà phát triển, nên một voidedReason bằng 7 khiến bạn tốn kém hơn một khoản hoàn tiền thông thường.
Voided Purchases API có bao gồm các đăng ký không?
Có. Đặt tham số type bằng 1 để nhận được cả các giao dịch mua trong ứng dụng bị hủy lẫn các giao dịch mua đăng ký bị hủy. Mặc định, type 0, chỉ trả về các giao dịch mua sản phẩm trong ứng dụng. Đối với các đăng ký, hãy xác định đúng kỳ bị hủy bằng orderId, vì một purchaseToken bao trùm mọi lần gia hạn và một orderId mới được tạo cho mỗi giao dịch gia hạn.

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.