Google Play Refund Management

Bỏ qua một lệnh acknowledgement trên Google Play, giao dịch sẽ tự hủy trong ba ngày

Một giao dịch mua được xử lý thành công, rồi ba ngày sau Google Play hoàn tiền và thu hồi nó — không khách hàng nào yêu cầu, không nhân viên hỗ trợ nào can thiệp, chỉ là một callback mà server của bạn chưa từng gửi. Đây là quy tắc chính xác, lý do các giao dịch mua bị lọt qua nó, và cái giá thực sự của mỗi giao dịch biến mất.

5 min read
Bỏ qua một lệnh acknowledgement trên Google Play, giao dịch sẽ tự hủy trong ba ngày

Một giao dịch mua được xử lý thành công, khách hàng nhận được sản phẩm, rồi ba ngày sau nó biến mất. Google Play đảo ngược khoản thanh toán và thu hồi quyền sở hữu, mà khách hàng không hề yêu cầu và không ai trong nhóm hỗ trợ của bạn chạm vào một nút nào cả. Giao dịch mua đã thất bại một cách âm thầm vì server của bạn chưa từng báo cho Google biết rằng nó đã được xử lý.

Đó là toàn bộ quy tắc: acknowledge một giao dịch mua trên Google Play trong vòng ba ngày, nếu không Google sẽ tự động hoàn tiền và thu hồi nó. Lần nào cũng vậy, không có ngoại lệ. Trong tất cả những cách một giao dịch có thể bị đảo ngược trên cả hai store, đây là một trong rất ít trường hợp nằm hoàn toàn trong tầm kiểm soát của chính hệ thống tích hợp của bạn, điều này cũng khiến nó trở thành một trong những cách âm thầm nhất để làm hao hụt doanh thu.

Đây không phải là một kiểm tra gian lận và cũng không phải là một quyết định chính sách. Đó là một callback chưa từng được gọi. Cách khắc phục rất nhỏ, nhưng số tiền bạn mất khi bỏ qua nó thì không nhỏ, vì vậy việc hiểu chính xác quy tắc này nói gì, vì sao các giao dịch mua bị lọt qua nó, và những gì mỗi giao dịch biến mất kéo theo là điều đáng để biết.

Tài liệu của Google thực sự yêu cầu điều gì

Play Billing yêu cầu ứng dụng của bạn phải thông báo cho Google ngay khi đã cấp quyền sở hữu và báo cho người dùng biết giao dịch mua đã thành công. Tài liệu của Google đưa ra một con số cụ thể: thông báo này "phải được thực hiện trong vòng ba ngày để giao dịch mua không bị tự động hoàn tiền và quyền sở hữu không bị thu hồi." Tài liệu về sản phẩm mua một lần cũng lặp lại chính xác điều này, không có ngoại lệ. Bỏ qua acknowledgement và "người dùng sẽ tự động nhận được tiền hoàn lại, và Google Play sẽ thu hồi giao dịch mua." Giao dịch mua đăng ký ban đầu cũng tuân theo quy tắc tương tự.

Acknowledgement không phải là thủ tục hành chính. Đó là tín hiệu duy nhất mà Google có để biết rằng việc giao hàng đã thực sự xảy ra. Không có tín hiệu nghĩa là Google cho rằng không có gì được giao, nên nó sẽ đảo ngược toàn bộ giao dịch cho người mua mà không hỏi ý kiến bạn. Người mua thấy một khoản hoàn tiền không ngờ tới. Bạn thấy một giao dịch biến mất khỏi sổ sách của mình.

Những điểm chính cần nhớ

  • Để một giao dịch không được xác nhận trong 72 giờ và việc đảo ngược sẽ tự xảy ra: người mua được hoàn tiền, quyền sở hữu bị rút lại, không ai được hỏi ý kiến. Không có khách hàng nào kích hoạt điều đó. Backend của bạn đã làm điều đó, bằng cách im lặng, và backend của bạn cũng hoàn toàn có khả năng ngăn chặn nó.

  • Việc đạt trạng thái PURCHASED mới là thứ khởi động đồng hồ đếm ngược, điều này khiến thời điểm checkout không liên quan đến hạn chót của bạn. Bất cứ thứ gì còn ở trạng thái PENDING đều không gắn đồng hồ nào và chưa cần đến lệnh xác nhận.

  • Một trong hai lệnh gọi sẽ hoàn thành nghĩa vụ này. Chạy purchases.products.consume đối với một sản phẩm tiêu hao, hoặc dùng purchases.products.acknowledge và purchases.subscriptions.acknowledge cho bất cứ thứ gì không phải sản phẩm tiêu hao. Google coi mỗi lệnh gọi này là bằng chứng rằng hàng hóa đã đến tay người mua.

  • Các gói đăng ký chỉ yêu cầu bạn làm điều này đúng một lần, ngay lúc đăng ký. Mọi chu kỳ thanh toán sau đó đều do chính Google xử lý, được đánh dấu là đã xác nhận mà mã nguồn của bạn không cần động đến.

  • Bán một gói trả trước kéo dài dưới bảy ngày? Thời hạn cho phép của bạn sẽ co lại thành năm mươi phần trăm thời gian của gói đó. Sản phẩm ngắn hơn, cầu chì ngắn hơn.

  • Chỉ có giá niêm yết là quay trở lại với người mua. Thời gian compute, chi phí gọi API model, phí lưu trữ, các khoản trả cho creator: số tiền đó đã rời khỏi tài khoản của bạn trong quá trình xử lý và không có đồng nào trở lại. Thiệt hại thực tế vượt xa bất cứ con số nào mà dòng hoàn tiền thể hiện.

  • Không có gì trên iOS hoạt động theo cách này. StoreKit hiển thị lại một giao dịch chưa hoàn tất ở mỗi lần khởi chạy và kiên nhẫn chờ đợi, không bao giờ đảo ngược khoản thanh toán. Rủi ro đặc thù này chỉ tồn tại trên Google Play và không ở nơi nào khác.

Checkout không phải là lúc hạn chót bắt đầu

Nhấn "mua" không khởi động đồng hồ. Trạng thái giao dịch mua phải chuyển sang PURCHASED trước, và một số giao dịch mua nằm ở trạng thái PENDING trước khi điều đó xảy ra. Các phương thức thanh toán bằng tiền mặt làm vậy, chuyển khoản ngân hàng chậm và yêu cầu phê duyệt từ phụ huynh cũng vậy. Google nói rõ điều này: "Khung thời gian acknowledgement ba ngày chỉ bắt đầu khi trạng thái giao dịch mua chuyển từ PENDING sang PURCHASED."

Điều đó có nghĩa là hai điều cho cách bạn xây dựng luồng xử lý. Chỉ cấp quyền sở hữu khi trạng thái là PURCHASED, không bao giờ cấp khi còn ở PENDING, nếu không bạn sẽ giao sản phẩm cho số tiền có thể không thực sự đến. Và cũng không acknowledge một giao dịch mua đang PENDING, vì chưa có gì để acknowledge cả. Gọi enablePendingPurchases() khi thiết lập BillingClient, chờ đến khi trạng thái chuyển đổi, và chỉ bắt đầu tính giờ khi nó chuyển sang PURCHASED.

Hiểu PENDING là "chưa bán được." Không có gì phải thực hiện: không quyền sở hữu, không acknowledgement, không có gì được ghi nhận là doanh thu. Ngay khi nó chuyển sang PURCHASED, cả quyền sở hữu và đồng hồ đếm ngược ba ngày đều trở thành vấn đề của bạn.

Chọn đúng lệnh gọi API cho thứ bạn đã bán

Có hai hướng API giúp xóa hạn chót, và hướng nào áp dụng còn tùy vào thứ bạn đã bán. Cả hai đều đáp ứng yêu cầu như nhau. Điều khác biệt giữa chúng là chúng còn làm gì khác với sản phẩm sau đó.

Các sản phẩm tiêu hao sẽ được consume. Đó là purchases.products.consume trên một backend an toàn, hoặc consumeAsync() từ Play Billing Library trên client. Consume làm nhiệm vụ kép, vì nó vừa đáp ứng yêu cầu acknowledgement vừa khiến sản phẩm có thể mua lại được. Đó là thứ bạn cần cho xu, credit, hoặc bất kỳ lần tạo nội dung một lần nào mà khách hàng có thể mua lặp lại nhiều lần.

Các sản phẩm không tiêu hao và các gói đăng ký thì được acknowledge, thông qua purchases.products.acknowledge, purchases.subscriptions.acknowledge, hoặc acknowledgePurchase() trên client. Điều này xóa hạn chót nhưng không mở lại sản phẩm để mua tiếp.

Dưới đây là cách mỗi loại sản phẩm tương ứng với một lệnh gọi và một hạn chót:

  • Đối với một sản phẩm tiêu hao (consumable), gọi purchases.products.consume hoặc consumeAsync(). Điều này cũng khiến sản phẩm có thể mua lại được, và hạn chót của bạn là ba ngày kể từ PURCHASED.

  • Đối với một sản phẩm không tiêu hao (non-consumable), gọi purchases.products.acknowledge hoặc acknowledgePurchase(). Quyền sở hữu được xác nhận mà không thể mua lại, và hạn chót của bạn là ba ngày kể từ PURCHASED.

  • Đối với giao dịch mua đăng ký ban đầu, gọi purchases.subscriptions.acknowledge hoặc acknowledgePurchase(). Điều này xác nhận gói đăng ký mới, và hạn chót của bạn là ba ngày kể từ PURCHASED.

  • Đối với lần gia hạn đăng ký, bạn không cần gọi gì cả. Google tự đánh dấu nó là đã acknowledge, nên không có hạn chót nào.

  • Đối với gói trả trước ngắn hơn một tuần, dùng cùng các lệnh acknowledgement, nhưng hạn chót của bạn sẽ co lại thành một nửa thời gian của gói.

Nghĩa vụ này chỉ dừng ở lần thanh toán đầu tiên

Các lần gia hạn đăng ký hoàn toàn không cần acknowledgement. Google nói rõ điều này: "bạn không cần acknowledge các lần gia hạn đăng ký," và nó tự đánh dấu mỗi lần gia hạn là đã acknowledge. Chỉ có giao dịch mua đăng ký đầu tiên mới đến ở trạng thái đang chờ acknowledgement và nằm trên bàn của bạn cho đến khi bạn xử lý xong. Hãy kiểm tra trạng thái acknowledgement ở phía server, hoặc dùng isAcknowledged() trên client, trước khi hành động. Như vậy bạn sẽ không bao giờ gửi một lệnh acknowledgement trùng lặp.

Một đồng hồ chặt hơn cho các gói trả trước ngắn hạn

Các gói trả trước có thời hạn một tuần hoặc dài hơn tuân theo quy tắc ba ngày thông thường. Các gói ngắn hơn thì không. Tài liệu của Google cho biết các gói "có thời hạn ngắn hơn một tuần phải được acknowledge trong vòng một nửa thời hạn của gói." Một gói trả trước ba ngày chỉ cho bạn một ngày rưỡi, không phải toàn bộ khoảng thời gian. Nếu các gói nạp trả trước ngắn hạn là một phần trong danh mục của bạn, luồng acknowledgement phải nhanh và do server điều khiển, không bao giờ phụ thuộc vào việc khách hàng mở lại ứng dụng.

Lệnh gọi acknowledgement thường bị thất lạc ở đâu

Không ai cố ý bỏ qua bước này. Nó bị thất lạc vì logic acknowledgement được gắn vào sai tác nhân kích hoạt. Mẫu sai lầm phổ biến là một client chỉ acknowledge khi luồng mua hàng quay trở lại foreground. Điều đó chỉ bao quát khách hàng hoàn tất việc mua và tiếp tục dùng ứng dụng ngay sau đó. Nó không bao quát bất kỳ ai khác.

Các nhà phát triển gặp phải điều này liên tục. Các chủ đề trong chính cộng đồng nhà phát triển của Google đều lặp lại kịch bản tương tự mỗi lần, một phiên bản nào đó của việc người dùng tự động được hoàn tiền ba ngày sau khi mua, hoặc một lời cầu xin được hiểu vì sao các khoản thanh toán cứ tự đảo ngược vào ngày thứ ba. Nguyên nhân chẩn đoán hiếm khi thay đổi: lệnh gọi acknowledgement được gắn vào một hành động của người dùng mà hành động đó chưa từng xảy ra, nên nó đơn giản là chưa từng được gọi.

Các bản thử nghiệm miễn phí và khách hàng không bao giờ mở lại ứng dụng

Phiên bản tệ nhất của điều này là một bản thử nghiệm miễn phí, hoặc một giao dịch mua được thực hiện ở những thời điểm cuối cùng trước khi ai đó đóng ứng dụng của bạn lần cuối. Khi acknowledgement chờ đến lần mở ứng dụng kế tiếp mà lần mở ứng dụng đó chẳng bao giờ xảy ra, giao dịch mua đơn giản là hết hạn. Google hoàn tiền và thu hồi nó vào ngày thứ ba. Đối với một bản thử nghiệm đang trên đà chuyển đổi thành công, bạn mất khoản thanh toán đầu tiên mà bạn chưa từng tính phí được, và quyền sở hữu của khách hàng cũng âm thầm biến mất theo. Không điều nào trong hai điều đó xuất hiện trong hàng chờ hỗ trợ của bạn. Cả hai đều xuất hiện dưới dạng một giao dịch bị vô hiệu (voided purchase), nếu bạn chịu tìm kiếm.

Acknowledgement phụ thuộc vào lần mở ứng dụng kế tiếp là một giao dịch bạn mất đi mỗi khi khách hàng không quay lại trong ba ngày. Các bản thử nghiệm và các giao dịch mua một chạm chính là những thời điểm khách hàng không quay lại.

Cái giá thực sự đằng sau một khoản hoàn tiền do thiếu acknowledgement

Dòng hoàn tiền đánh giá thấp những gì bạn thực sự mất. Google đảo ngược giao dịch, bạn trả lại giá tiền, và đó là con số mọi người nhìn thấy. Nhưng đó không phải là toàn bộ hóa đơn.

Hãy xem xét một sản phẩm tiêu hao khởi động công việc thực sự ngay khi ai đó mua nó. Một loạt lần tạo ảnh, một chuỗi lệnh gọi đến API của nhà cung cấp model, một lần xuất video, một khoản trả cho creator. Bạn đã trả tiền cho khoản compute đó, những lệnh gọi API đó, khoản lưu trữ đó, và những khoản trả đó ngay tại thời điểm sử dụng. Khoản hoàn tiền trả lại giá bán cho khách hàng. Nó không trả lại hóa đơn của nhà cung cấp cho bạn. Chi phí thực sự đã rời khỏi túi bạn và không có gì quay trở lại cho nó.

Đối với các gói đăng ký và bản thử nghiệm, tổn thất là khoản thanh toán đầu tiên mà bạn chẳng bao giờ thu được, cộng thêm một quan hệ khách hàng kết thúc trước khi nó bắt đầu. Và mức rủi ro sắp tăng lên. Bắt đầu từ ngày 3 tháng 8 năm 2026, Google Play sẽ chuyển giá mua hàng chargeback và phí ngân hàng sang cho nhà phát triển đối với các đơn hàng được đặt sau ngày đó, điều này khiến bất kỳ lỗ hổng có thể tránh được nào cũng đáng được bịt kín ngay từ bây giờ chứ không phải để sau. Một khoản hoàn tiền do thiếu acknowledgement không phải là một chargeback, nhưng nó dạy cho bạn cùng một bài học. Tiền bạn đã chi ra không tự động là tiền bạn giữ được.

Xây dựng bước acknowledgement sao cho không thể bị bỏ sót

Mô hình đáng tin cậy loại bỏ hoàn toàn ứng dụng của bạn khỏi đường dẫn quan trọng. Hãy chạy nó trên backend của bạn, được điều khiển bởi các thông báo, không phải bởi việc ai đó có mở lại màn hình hay không.

  • Đăng ký nhận các thông báo nhà phát triển theo thời gian thực (real-time developer notifications). Một sự kiện mua sản phẩm một lần, hoặc một sự kiện mua đăng ký, sẽ đến server của bạn ngay khi Google ghi nhận giao dịch, bất kể ứng dụng có đang chạy hay không.

  • Xác thực purchase token thông qua Play Developer API và xác nhận trạng thái là PURCHASED chứ không phải PENDING.

  • Ghi lại quyền sở hữu trong hệ thống của riêng bạn, gắn với tài khoản của khách hàng đó.

  • Consume hoặc acknowledge ngay không chậm trễ. Sản phẩm tiêu hao được consume, sản phẩm không tiêu hao và các gói đăng ký ban đầu được acknowledge. Đọc trạng thái acknowledgement trước để lệnh gọi không bao giờ bị gửi hai lần.

  • Giữ lại một cơ chế bổ sung ở phía client. Gọi queryPurchasesAsync() trong onResume() sẽ bắt lại bất cứ thứ gì đã hoàn tất trong khi ứng dụng bị tắt. Hãy coi đó như một lớp bảo hiểm, không phải con đường chính.

Điểm mấu chốt là acknowledgement được kích hoạt bởi một sự kiện mà Google trao cho bạn, thay vì bởi một hành động của người dùng mà bạn không có cách nào đảm bảo. Một người mua một lần rồi không bao giờ quay lại vẫn hoàn toàn được bao quát, vì server của bạn đã làm xong việc ngay khi giao dịch mua được ghi nhận.

Một lớp phòng vệ cuối cho những gì vẫn lọt qua

Cho dù pipeline được xây dựng tốt đến đâu, nó vẫn đáng có một lớp lưới an toàn bên dưới. Voided Purchases API của Google liệt kê các đơn hàng kết thúc bằng một khoản hoàn tiền, một lần thu hồi, hoặc một chargeback, và nó chỉ ra cụ thể trường hợp mà nguyên nhân là một giao dịch mua "chưa từng được nhà phát triển acknowledge, và vì vậy có thể không tồn tại trong hồ sơ của nhà phát triển." Truy vấn nó theo một lịch trình đều đặn và bạn có thể gỡ quyền truy cập của bất kỳ ai mà đơn hàng của họ đã bị Google âm thầm hủy sau lưng bạn. Tuy vậy, hãy chú ý giới hạn: chỉ 30 ngày gần nhất mới có thể truy xuất được, điều này khiến đây phải là một tác vụ được lên lịch chạy định kỳ chứ không phải thứ bạn tiện thì làm mỗi quý một lần.

iOS chưa bao giờ được xây dựng để làm điều này với bạn

Chuyển sang App Store thì không điều nào trong số này áp dụng. Apple đã xây dựng một bước kết thúc riêng của mình, việc finishing a transaction (hoàn tất giao dịch), ngoại trừ việc hình phạt cho việc bỏ qua nó lại đi theo hướng ngược lại. Một giao dịch chưa hoàn tất sẽ nằm chờ trong hàng đợi của Apple. Nó xuất hiện lại ở mỗi lần khởi chạy ứng dụng, và lại xuất hiện mỗi khi observer được gắn vào, trao cho bạn những cơ hội lặp lại để giao thứ mà khách hàng đã trả tiền. Apple không hề đảo ngược giao dịch vì điều này. Không có gì giống như một tác nhân hoàn tiền 72 giờ tồn tại trên nền tảng đó.

Đó chính là lý do vì sao một mô hình tư duy duy nhất không thể áp dụng cho cả hai store. Android trao cho bạn một đồng hồ đếm ngược và một giao dịch sẽ tan biến nếu bạn bỏ lỡ nó. iOS trao cho bạn một lời nhắc dai dẳng và thời gian không giới hạn để xử lý nó. Mang thói quen của iOS vào một codebase Android và sớm hay muộn bạn sẽ nhìn vào một đống đơn hàng bị đảo ngược mà không ai trong nhóm có thể giải thích được.

Bịt kín lỗ hổng đó chính xác là công việc mà Refund Sensor thực hiện. Xác nhận được kích hoạt trực tiếp từ thông báo của store, chỉ trong khoảnh khắc nó vừa đến, trong khi một lượt đối soát thường trực với Voided Purchases API sẽ gỡ quyền truy cập ở mọi nơi Google đã âm thầm hủy một đơn hàng sau lưng bạn. Một đồng hồ đếm ngược mà bạn có thể bỏ lỡ trở thành một ô đã được tích sẵn trước cả khi bạn kịp nghĩ đến việc kiểm tra.

Những quy tắc này được ghi trong tài liệu nào

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

Server của bạn chưa gửi lệnh acknowledgement đúng hạn. Khi một giao dịch mua không được acknowledge quá ba ngày kể từ khi đạt trạng thái PURCHASED, Google sẽ tự động hoàn tiền và thu hồi quyền sở hữu. Đây không phải do khách hàng gây ra, và cũng không phải hình phạt do Google áp đặt. Đó là do thiếu lệnh gọi acknowledgement, và việc acknowledge ở phía server dựa trên thông báo từ store sẽ loại bỏ hoàn toàn vấn đề này.

Cả hai đều dừng đồng hồ ba ngày, nhưng lại để sản phẩm ở hai trạng thái khác nhau. Consume, thông qua purchases.products.consume hoặc consumeAsync(), dành cho các sản phẩm tiêu hao (consumable). Nó xóa hạn chót và đưa sản phẩm trở lại trạng thái có thể mua tiếp. Acknowledge, thông qua purchases.products.acknowledge, purchases.subscriptions.acknowledge, hoặc acknowledgePurchase(), dành cho sản phẩm không tiêu hao (non-consumable) và các gói đăng ký. Nó xác nhận quyền sở hữu mà không mở lại sản phẩm để mua thêm lần nữa.

Không, chỉ giao dịch mua đầu tiên mới cần. Google không yêu cầu acknowledge các lần gia hạn và tự động đánh dấu chúng là đã acknowledge. Chỉ có giao dịch mua đăng ký ban đầu, vốn đến ở trạng thái đang chờ acknowledgement, mới là trách nhiệm của bạn cho đến khi bạn xử lý xong.

Không, hãy chờ đến khi đạt trạng thái PURCHASED. Một giao dịch mua ở trạng thái PENDING, như thanh toán bằng tiền mặt hoặc yêu cầu phê duyệt từ phụ huynh, hoàn toàn chưa bắt đầu đồng hồ ba ngày. Chỉ cấp quyền sở hữu và gửi acknowledgement sau khi trạng thái thực sự chuyển từ PENDING sang PURCHASED.

Không, và điều này đáng để biết trước khi bạn mặc định rằng Android và iOS hoạt động giống nhau. Một giao dịch StoreKit chưa hoàn tất chỉ đơn giản được gửi lại ở mỗi lần mở ứng dụng cho đến khi bạn hoàn tất nó. Apple không bao giờ tự động hoàn tiền. Cơ chế tự động hoàn tiền sau ba ngày chỉ có ở Google Play, nên hai nền tảng này cần được xử lý khác nhau.

Bản thân việc hoàn tiền không thể đảo ngược. Điều bạn có thể làm là đối soát. Voided Purchases API hiển thị các giao dịch mua đã hoàn tiền và bị thu hồi trong 30 ngày qua, và đánh dấu riêng những giao dịch bị vô hiệu chính vì chưa từng được acknowledge, nhờ đó bạn có thể thu hồi quyền sở hữu mà bạn đã cấp. Về sau, việc acknowledge dựa trên thông báo từ store là cách giúp giao dịch tiếp theo không bị lọt qua.

#billing#acknowledgement#refunds#in-app purchases#android
Refund SensorRefund Sensor TeamRefund defense for App Store and Google Play developers