Semua artikel
Deep diveWaktu baca 8 menit

Mencabut Akses Setelah Refund: Langkah yang Tidak Diambil Apple dan Google untuk Anda

Apple dan Google bisa saja memproses refund sementara pelanggan tetap memiliki pembelian tersebut. Berikut kapan tepatnya akses dicabut secara otomatis, kapan server Anda yang harus melakukannya, dan satu notifikasi yang hampir tidak pernah ditangani oleh kebanyakan integrasi.

Sebuah smartphone menyala di atas meja pada malam hari, menggambarkan momen ketika akses ke pembelian yang di-refund belum dicabut

Poin utama

  • Di Google Play, me-refund pesanan tidak dengan sendirinya mencabut akses pengguna. Akses hanya dicabut jika opsi "Remove entitlement" dicentang di Play Console, atau jika panggilan API menyertakan revoke=true, yang bukan merupakan default.
  • Refund yang diproses tanpa opsi revoke tidak akan pernah muncul di Voided Purchases API. Proses revocation yang hanya membaca feed tersebut akan diam-diam melewatkan setiap pesanan semacam itu.
  • Apple dapat mencabut lisensi untuk non-consumable dan subscription secara otomatis, tetapi consumable dianggap sudah terkirim begitu ia digunakan, sehingga StoreKit tidak memiliki mekanisme untuk mencabutnya. Server Anda sendiri yang harus mengurangi saldonya.
  • Apple juga dapat membatalkan refund yang sudah diberikannya sendiri. Notifikasi REFUND_REVERSED berarti sebuah refund yang sebelumnya disetujui dibatalkan, revocationDate kembali menjadi null, dan akses atau saldo apa pun yang telah Anda hapus harus dipulihkan.
  • Field voidedReason milik Google membawa sembilan nilai, mulai dari remorse dan accidental_purchase hingga fraud, friendly_fraud, dan unacknowledged_purchase, sehingga feed yang sama yang memberi tahu Anda untuk mencabut akses juga memberi tahu Anda mana pencabutan yang merupakan penyalahgunaan dan mana yang merupakan bug pada integrasi Anda sendiri.
  • Setiap hari sebuah pembelian yang di-refund tetap memiliki akses adalah hari di mana Anda membayar biaya compute, storage, dan panggilan API di baliknya, atas uang yang sudah Anda kembalikan.

Menyetujui refund dan mencabut akses adalah dua langkah yang terpisah, dan hanya salah satunya yang terjadi secara otomatis. Developer yang menganggap refund yang sudah diproses berarti pembelian sudah dicabut aksesnya keliru di kedua toko besar, dengan cara dan alasan yang berbeda. Mengetahui persis di mana otomatisasi berhenti adalah yang dibutuhkan untuk mencabut akses setelah refund, alih-alih baru menyadarinya berbulan-bulan kemudian bahwa hal itu tidak pernah terjadi.

Mengapa refund tidak secara default mencabut akses

Apple dan Google sama-sama memperlakukan uang dan akses sebagai dua operasi yang berbeda. Sisi uang adalah kebijakan toko, begitu refund disetujui, statusnya final dan buku besar diperbarui. Sisi akses bersifat opsional, atau menjadi tugas developer, atau keduanya, tergantung pada platform dan jenis pembelian. Tidak ada toko yang berasumsi Anda ingin akses langsung hilang begitu refund selesai diproses, karena banyak kasus yang sah (refund sebagian, kredit itikad baik, gestur dukungan pelanggan) yang memang tidak dimaksudkan untuk mengambil kembali produknya sama sekali.

Google Play: kotak centang yang tidak disadari siapa pun

Di Play Console, me-refund sebuah pesanan adalah formulir dua bagian, pilih jumlah refund, lalu tentukan secara terpisah apakah akan mencentang "Remove entitlement". Membiarkannya tidak tercentang adalah jalur default yang diambil sebagian besar agen support, karena kebanyakan permintaan refund adalah gestur itikad baik di mana pelanggan tetap memiliki apa yang mereka beli. Masalahnya, tidak ada apa pun di layar konfirmasi refund yang memberi tahu Anda bahwa ini juga cara pelanggan tetap memiliki subscription atau item in-app setelah refund yang seharusnya Anda maksudkan sebagai final.

Jalur API memiliki jebakan yang sama

Pilihan yang sama ada di dalam kode. Endpoint orders.refund menerima parameter query revoke yang bersifat opsional, ketika nilainya true, akses ke item tersebut langsung dihentikan, dan untuk subscription berulang, setiap pembayaran di masa depan juga ikut dibatalkan. Jika parameter ini dihilangkan, nilainya default ke false, refund tetap diproses, catatan pembelian diperbarui, dan pengguna tetap dapat menggunakan produk persis seperti sebelumnya.

TindakanAkses dicabut?Muncul di Voided Purchases API?
Refund Play Console, "Remove entitlement" tidak dicentangTidakTidak
Refund Play Console, "Remove entitlement" dicentangYa, segeraYa
API orders.refund, parameter revoke dihilangkan (default false)TidakTidak
API orders.refund, revoke=trueYa, segeraYa

Baris terakhir itulah yang menjebak banyak integrasi. Voided Purchases API dibuat agar Anda bisa membangun pipeline revocation tanpa harus memeriksa Play Console secara manual, tetapi API ini hanya mendaftar pesanan yang benar-benar dibatalkan dengan revocation. Refund yang diproses tanpa kotak centang itu, atau tanpa revoke=true, memang sengaja tidak terlihat oleh feed tersebut, bukan karena bug.

Sebuah rak server dengan satu lampu status merah di antara barisan lampu hijau, menggambarkan keputusan revocation yang tidak disadari

Membaca field voidedReason Ketika sebuah pembelian memang muncul sebagai voided, field voidedReason memberi tahu Anda alasannya, dalam skala sembilan nilai: other, remorse, not_received, defective, accidental_purchase, fraud, friendly_fraud, chargeback, dan unacknowledged_purchase. Itu bukan sekadar pencatatan. Remorse dan accidental_purchase adalah refund biasa. Fraud, friendly_fraud, dan chargeback adalah nilai-nilai yang layak diarahkan ke antrean review fraud, bukan ke jalur revoke-and-forget biasa, karena akun yang sama secara statistik lebih mungkin mengulanginya.

Ada satu detail operasional yang penting jika Anda membangun sistem langsung di atas feed ini, purchases.voidedpurchases.list dibatasi hingga 6.000 query per hari dan 30 query dalam jendela 30 detik mana pun. Desain yang melakukan polling dalam loop ketat akan cepat mencapai batas itu. Pola yang dimaksudkan adalah pull terjadwal dengan pagination token, bukan polling terus-menerus.

Apple: App Store dapat mencabut lisensi, tetapi tidak untuk consumable

App Store Server Notifications V2 milik Apple mengirimkan notifikasi REFUND begitu sebuah refund selesai diproses, baik untuk consumable, non-consumable, subscription auto-renewable, maupun subscription non-renewing. Apa yang terjadi setelah notifikasi itu sepenuhnya bergantung pada jenis pembeliannya, karena mekanisme revocation milik StoreKit sendiri hanya mencakup dua dari empat jenis tersebut.

Mengapa consumable menjadi pengecualian

Non-consumable atau subscription memiliki entitlement yang tetap ada dan bisa dimatikan oleh sistem Apple. Consumable tidak demikian, begitu digunakan, ia dianggap sudah terkirim, dan tidak ada lagi yang tersisa di StoreKit untuk dicabut. Jika seorang pelanggan membeli 500 coins, menghabiskannya, lalu mendapatkan refund, coins tersebut sudah dianggap tuntas dari sudut pandang Apple. Satu-satunya buku besar yang masih mencatatnya adalah milik Anda. Notifikasi REFUND adalah satu-satunya sinyal bagi Anda untuk menemukan original transaction ID yang cocok dan mengurangi saldo tersebut dari catatan Anda sendiri, tidak ada apa pun di sisi Apple yang akan melakukannya untuk Anda.

NotifikasiArtinyaYang harus dilakukan server Anda
REFUNDApple me-refund pembelian consumable, non-consumable, auto-renewable, atau non-renewingCabut akses non-consumable dan subscription segera; kurangi saldo consumable sendiri
REFUND_DECLINEDApple menolak permintaan refund pelangganTidak ada tindakan. Akses tidak pernah berisiko
REFUND_REVERSEDApple membatalkan refund yang sebelumnya sudah diberikanPulihkan akses atau saldo apa pun yang tadinya Anda hapus akibat notifikasi REFUND
CONSUMPTION_REQUESTApple meminta bukti Anda sebelum memutuskan sebuah refundTanggapi dalam 12 jam; ini muncul sebelum REFUND, bukan sesudahnya

Notifikasi yang hampir tidak pernah ditangani siapa pun

REFUND_REVERSED adalah notifikasi yang paling sering diabaikan sepenuhnya oleh kebanyakan integrasi, karena ia menggambarkan Apple membatalkan keputusannya sendiri, sebuah kasus yang jarang dirancang oleh developer sejak awal. Ketika notifikasi ini tiba, revocationDate pada transaksi tersebut kembali menjadi null, sejauh yang Apple ketahui, pembelian itu kembali sah. Jika server Anda menghapus entitlement, membatalkan catatan subscription, atau menolkan saldo consumable saat REFUND awal masuk, REFUND_REVERSED adalah sinyal untuk mengembalikan semuanya.

Cara menutup celah ini, langkah demi langkah

  • Di Google Play, jadikan revoke=true sebagai default untuk setiap jalur refund, baik melalui Play Console maupun API, kecuali ada kasus support tertentu yang memang mengharuskan pelanggan tetap memiliki akses.
  • Lakukan polling purchases.voidedpurchases.list secara terjadwal dengan pagination token, bukan dalam loop ketat, dan tetap di bawah 6.000 request per hari serta 30 per 30 detik.
  • Arahkan nilai voidedReason berupa fraud, friendly_fraud, dan chargeback ke antrean review, bukan ke revoke-and-close otomatis.
  • Di Apple, berlangganan App Store Server Notifications V2 dan cabut akses non-consumable serta subscription begitu notifikasi REFUND menyebutkannya.
  • Simpan original transaction ID untuk setiap pembelian consumable agar notifikasi REFUND bisa menemukan saldo yang tepat untuk dikurangi tanpa pencarian manual.
  • Tangani REFUND_REVERSED secara eksplisit dan catat setiap revocation berdasarkan transaction atau order ID-nya, agar pembatalan dapat memulihkan persis apa yang telah dihapus.

Apa sebenarnya biaya dari celah ini

Semua ini bukan soal keadilan bagi pelanggan, ini soal apa yang tetap berjalan setelah uangnya sudah hilang. Subscription yang sudah di-refund tetapi masih memanggil API Anda, menyimpan data, dan menghabiskan compute bukanlah masalah moral, itu adalah pos biaya, Anda membayar tagihan infrastruktur untuk pembelian yang pendapatannya sudah Anda kembalikan. Toko tidak melacak biaya itu untuk Anda, karena dari sisi mereka, refund sudah selesai begitu uangnya berpindah. Sisi akses sepenuhnya menjadi tagihan yang harus Anda kendalikan sendiri.

Inilah alasan yang sama mengapa RefundHalt memperlakukan revocation sebagai bagian dari pipeline refund, bukan sebagai renungan belakangan, sebuah event REFUND dan perubahan akses dicatat sebagai satu tindakan, dan sebuah event REFUND_REVERSED memulihkan state secara otomatis, alih-alih menunggu seseorang menyadari bahwa pelanggan masih terkunci dari sesuatu yang sudah dikembalikan oleh Apple.

Pertanyaan yang sering diajukan

Apakah me-refund pembelian di Google Play secara otomatis mencabut akses?
Tidak. Akses hanya dicabut jika "Remove entitlement" dicentang di Play Console atau panggilan API orders.refund menyertakan revoke=true. Keduanya bukan default, sehingga sebuah refund bisa diproses sementara pelanggan tetap menggunakan produknya.
Apa yang terjadi ketika pembelian in-app consumable di-refund di App Store?
Apple mengirimkan notifikasi REFUND, tetapi StoreKit tidak memiliki cara untuk mencabut consumable karena dianggap sudah terkirim begitu dipakai. Server milik developer harus menemukan transaksi aslinya dan mengurangi saldo secara manual.
Apa itu notifikasi REFUND_REVERSED dan apakah memerlukan penanganan khusus?
REFUND_REVERSED berarti Apple membatalkan refund yang sebelumnya sudah diberikan; revocationDate kembali menjadi null dan pembelian tersebut kembali sah. Akses atau saldo apa pun yang dihapus oleh REFUND awal harus dipulihkan, dan kebanyakan integrasi bahkan tidak pernah mendengarkan notifikasi ini sama sekali.
Mengapa pesanan Google Play yang di-refund bisa tidak muncul di Voided Purchases API?
API tersebut hanya mendaftar pesanan yang dibatalkan dengan revocation. Jika sebuah refund diproses tanpa opsi revoke, pesanan itu sama sekali tidak akan dikembalikan oleh endpoint tersebut, sehingga pipeline yang hanya mengandalkannya akan sepenuhnya melewatkan refund-refund itu.
Seberapa sering saya boleh melakukan polling ke Voided Purchases API milik Google?
Endpoint tersebut dibatasi hingga 6.000 query per hari dan 30 query dalam jendela 30 detik mana pun, sehingga dirancang untuk pull terjadwal dengan pagination token, bukan untuk polling terus-menerus.

Sumber dan bacaan lanjutan

RefundHalt

Autopilot refund untuk App Store dan Google Play

Baca selanjutnya

Permintaan refund berikutnya sudah menuju kepada Anda.

Siapkan RefundHalt dalam waktu yang dibutuhkan untuk membaca satu lagi email dukungan tentang refund yang tidak sempat Anda sengketakan.