Paranın hareket etmesi ve erişimin sona ermesi her iki platformda da ayrı olaylardır ve bunlardan sadece biri otomatik gerçekleşir. Google, yetki kaldırmayı varsayılan olarak kapalı bir seçeneğin arkasında bırakır. Apple lisansları yönetebilir, ancak birinin zaten harcadığı bir consumable'a (tüketilebilir ürüne) dokunamaz. Sunucunuzun temizlemeyi atladığı her şey, artık elinizde olmayan bir gelir üzerinden çalışmaya devam eder.
Önemli noktalar
Bir Play siparişini iade etmek, siz aksini belirtmedikçe müşterinin erişimini olduğu gibi bırakır. \"Remove entitlement\" (Yetkiyi kaldır) Play Console içinde işaretlenmeli veya API çağrınız revoke=true taşımalıdır. İkisi de kendiliğinden gerçekleşmez.
Bu revoke seçeneği olmadan verilen iadeler, Voided Purchases API'de tamamen görünmez. Yalnızca bu akış üzerine kurulu herhangi bir iptal işi, bunların her birini fark edilmeden atlar.
Non-consumable ürünler ve aboneliklere ait lisansları Apple sizin için geri çekebilir. Consumable'lar harcandığı anda teslim edilmiş sayılır, bu da StoreKit'e geri çekecek hiçbir şey bırakmaz; bu nedenle bakiyeyi düşürmek kendi sunucunuza kalır.
Apple bazen kendi verdiği iadeleri geri alır. Bir REFUND_REVERSED bildirimi, verilmiş bir iadenin geri alındığını gösterir, revocationDate yeniden null değerine döner ve kaldırdığınız her şeyin geri yüklenmesi gerekir.
Google'ın voidedReason alanını remorse ve accidental_purchase'tan fraud, friendly_fraud ve unacknowledged_purchase'a kadar uzanan dokuz farklı değer doldurur; böylece tek bir akış hem erişimi kesmenizi söyler hem de hangi vakaların kötüye kullanım gibi koktuğunu işaretler.
İadesi yapılmış bir satın alım erişimini sürdürdüğü sürece, onun işlem gücünü, depolamasını ve API çağrılarını, alıcıya zaten geri ödenmiş bir gelirden finanse ediyorsunuzdur.
Erişim neden varsayılan olarak iadeden sağ çıkar
Her iki şirket de ödemeyi ve yetkiyi ayrı işlemler olarak ele alır. Ödeme, mağaza politikasını takip eder ve onaylandığında iade sonuçlanır, defter buna göre güncellenir. Yetki ise farklı bir meseledir; bazen isteğe bağlıdır, bazen sizin yönetmeniz gerekir, platforma ve ürün türüne bağlı olarak çoğunlukla ikisi de geçerlidir. Her iki mağaza da parayı geri vermenin erişimi sonlandırmak anlamına geldiğini varsaymaz, çünkü kısmi bir iade, bir jest kredisi, destek ekibinden bir nezaket göstergesi gibi birçok meşru durumda müşterinin satın aldığı şeyi elinde tutması amaçlanır.
Google Play: fark edilmeyen onay kutusu
Play Console, iade işlemini iki karara böler. Önce tutar, ardından tamamen ayrı olarak \"Remove entitlement\"ın (Yetkiyi kaldır) işaretlenip işaretlenmeyeceği. Çoğu destek temsilcisi bu kutuyu olduğu gibi bırakır; bu da mantıklıdır, çünkü iade taleplerinin büyük çoğunluğu gerçekten bir nezaket göstergesidir ve müşterinin ürünü elinde tutması amaçlanır. Onay ekranının asla belirtmediği şey, dokunulmamış bu aynı kutunun, sizin kesin olmasını istediğiniz bir iadeden sonra birinin hâlâ bir abonelik veya uygulama içi öğeye sahip olarak çekip gitmesine nasıl yol açtığıdır.
Aynı tuzak API'de de var
Kod da aynı ayrımı sunar. orders.refund uç noktasına ait isteğe bağlı bir revoke sorgu parametresi vardır ve bunu true olarak ayarlamak öğeye erişimi anında sona erdirir; bir yinelenen abonelik söz konusuysa gelecekteki her ödemeyi de bununla birlikte iptal eder. Parametreyi belirtmezseniz false değerini alırsınız: iade işlenir, satın alım kaydı değişir ve müşteri tam olarak öncesi gibi devam eder.
Refund in Play Console'da \"Remove entitlement\" işaretlenmeden, erişim devam eder. Sipariş de Voided Purchases API'ye hiç ulaşmaz.
Refund in Play Console'da \"Remove entitlement\" işaretlenerek, erişim hemen sona erer. Sipariş Voided Purchases API'de görünür.
orders.refund çağrılır ve revoke parametresi boş bırakılırsa, varsayılan olarak false değerini alır ve erişim devam eder. Burada da Voided Purchases API'ye hiçbir şey ulaşmaz.
orders.refund revoke=true ile çağrılırsa, erişim hemen sona erer ve sipariş Voided Purchases API'de görünür.
Yalnızca Voided Purchases API'yi okuyan bir iptal işi, revoke bayrağı olmadan verilen bir iadeyle asla karşılaşmaz. Kayıt gecikmiş değildir. Kayıt basitçe hiç gelmeyecektir ve sipariş, iade gerçekleşmemiş gibi bu akışın dışında kalır.
Entegrasyonları bozan şey tam da bu son satırdır. Play Console'u manuel olarak izlemeden bir iptal hattı kurmak, Voided Purchases API'nin tam olarak amacıdır; ancak kapsamı gerçekten iptal ile geçersiz kılınan siparişlerle sınırlıdır. Onay kutusu olmadan veya revoke=true olmadan işlenen iadeler, bir kusurdan değil, tasarımdan ötürü orada görünmez.
voidedReason'ı anlamlandırmak
Geçersiz kılınan satın alımlar, nedeni açıklayan ve dokuz olası değer barındıran bir voidedReason ile gelir: other, remorse, not_received, defective, accidental_purchase, fraud, friendly_fraud, chargeback ve unacknowledged_purchase. Bunu salt kayıt tutma olarak ele almak bu alanı boşa harcamaktır. Remorse ve accidental_purchase sıradan iadeleri tanımlar. Fraud, friendly_fraud ve chargeback ise otomatik bir iptal-ve-kapat işlemi yerine bir sahtecilik inceleme kuyruğunu hak eder, çünkü bu değerleri üreten hesaplar istatistiksel olarak daha yüksek bir oranda tekrarlanır.
Bu akışa karşı doğrudan geliştirme yapan herkesin bilmesi gereken bir işletimsel sayı var: purchases.voidedpurchases.list günde 6,000 sorguya ve herhangi bir 30 saniyelik aralıkta 30 sorguya izin verir. Sıkı sorgulama döngüleri bu kotayı hızla tüketir. Amaçlanan tasarım, sayfalama belirteçleri kullanan zamanlanmış çekimlerdir.
Apple: lisanslar geri çekilebilir, consumable'lar çekilemez
App Store Server Notifications V2, bir iade sonuçlandığı anda bir REFUND bildirimi gönderir ve bunu consumable, non-consumable, otomatik yenilenen abonelik ve yenilenmeyen abonelik arasında hiçbir ayrım yapmadan yapar. Sırada ne olması gerektiği ürün türüne göre tamamen farklılaşır, çünkü bu dört türden yalnızca ikisi StoreKit'in kendi iptal mekanizmasının kapsamına girer.
Consumable'ların bunun dışında kalma nedeni
Sürekli yetkiler non-consumable ürünlere ve aboneliklere aittir ve Apple'ın sistemleri bunları kapatabilir. Consumable'larda böyle bir şey yoktur. Birini harcamak onu teslim edilmiş olarak işaretler, bu da StoreKit'e geri alacak hiçbir şey bırakmaz. 500 coin satın alan, bunları harcayan ve ardından bir iade alan bir müşteri, Apple'ın bakış açısından her şeyi zaten almıştır. Bu coinlerin hâlâ var olduğu tek defter sizinkidir. O REFUND bildirimi, eşleşen orijinal işlem kimliğini (transaction ID) bulup bakiyeyi kendiniz düşürmeniz için tek uyarınızdır, çünkü Apple tarafında bunu yapacak hiçbir şey yoktur.
REFUND, bir consumable, non-consumable, otomatik yenilenen veya yenilenmeyen satın alımın iade edildiği anlamına gelir. Non-consumable ve abonelik erişimini hemen geri çekin, consumable bakiyesini ise kendiniz düşün.
REFUND_DECLINED, Apple'ın müşterinin iade talebini geri çevirdiği anlamına gelir. Yapılacak bir şey yoktur, çünkü erişim hiçbir zaman risk altında olmamıştır.
REFUND_REVERSED, Apple'ın daha önce verdiği bir iadenin geri alındığı anlamına gelir. Orijinal REFUND bildirimi nedeniyle kaldırdığınız her şeyi yeniden yükleyin.
CONSUMPTION_REQUEST, Apple'ın karar vermeden önce sizden kanıt istediği anlamına gelir. 12 saat içinde yanıt verin ve bu bildirimin bir REFUND'dan sonra değil, önce geldiğini unutmayın.
Herkesin göz ardı ettiği bildirim
REFUND_REVERSED, entegrasyonların düzenli olarak atladığı bildirimdir, çünkü Apple'ın kendi kararını geri almasını temsil eder ve bu, başlangıçta nadiren birinin tasarladığı bir şeydir. Gelişiyle revocationDate yeniden null değerine döner, bu da Apple'ın artık satın alımı tamamen geçerli saydığı anlamına gelir. Sunucunuzun orijinal REFUND üzerinde yaptığı her şey, bir yetkiyi silmek, bir abonelik kaydını iptal etmek, bir consumable bakiyesini sıfırlamak, geri alınmalıdır.
Geri alınmış bir iadenin yepyeni bir satın alıma benzemesini beklemeyin, çünkü benzemeyecektir. Uygulanan yasaklar, geri çekilen erişim, harcanmış olarak işaretlenen bakiyeler: REFUND_REVERSED, tam olarak bu şeyleri geri almanız için verilen talimattır ve size bunu hatırlatmak için daha sonra hiçbir takip bildirimi gelmeyecektir.
Boşluğu adım adım kapatmak
revoke=true'yu hem Console hem de API dahil her Play iade yolunda varsayılan yapın; alternatifi ise yalnızca müşterinin erişimi elinde tutmasını özellikle gerektiren destek vakaları için saklayın.
purchases.voidedpurchases.list'i sıkı bir döngü içinde değil, sayfalama belirteçleri kullanarak zamanlanmış şekilde çekin; günde 6,000 isteğin ve 30 saniyede 30 isteğin altında kalın.
fraud, friendly_fraud ve chargeback voidedReason değerlerini otomatik iptal-ve-kapat işlemi yerine bir inceleme kuyruğuna yönlendirin.
Apple'da App Store Server Notifications V2'ye abone olun ve bir REFUND bildirimi bunları tanımladığı anda non-consumable ve abonelik erişimini geri çekin.
Her consumable satın alımına karşılık orijinal işlem kimliğini saklayın, böylece bir REFUND bildirimi, kimse manuel olarak aramak zorunda kalmadan düşülmesi gereken tam bakiyeyi bulabilir.
REFUND_REVERSED'i bilinçli olarak uygulayın ve her iptali işlem veya sipariş kimliğine karşı kaydedin, böylece bir geri alma işlemi tam olarak neyin alındığını geri yükler.
Bu boşluğun gerçek maliyeti nedir
Burada sorun müşteri adaleti değil. Sorun, gelir gittikten sonra çalışmaya devam eden şeydir. İadesi yapılmış, hâlâ API'nize ulaşan, hâlâ veri tutan, hâlâ işlem gücü harcayan bir abonelik hiçbir ahlaki zorluk sunmaz; bir fatura sunar. Altyapı, gelirini çoktan geri ödediğiniz bir satın alım üzerinden ödenmiştir. Hiçbir mağaza bu gideri sizin adınıza takip etmez, çünkü onların bakış açısından iade, para hareket ettiği anda sona ermiştir. Erişim tarafını kontrol etmek tamamen sizin yönetmeniz gereken bir maliyettir.
Bu nedenle Refund Sensor, iptali sonradan eklenen bir şey olarak değil, iade hattının bir parçası olarak ele alır. Bir REFUND olayı ve ona karşılık gelen erişim değişikliği tek bir işlem olarak yazılır; bir REFUND_REVERSED olayı ise, Apple'ın günler önce geri verdiği bir şeyden kilitli kalan bir müşteriyi birinin fark etmesini beklemek yerine, önceki durumu kendiliğinden geri yükler.
Bu kuralların belgelendiği yerler
Sık sorulan sorular
Hayır, kaldırmaz. Erişim yalnızca Play Console'da "Remove entitlement" (Yetkiyi kaldır) işaretlendiğinde veya orders.refund çağrısında revoke=true gönderildiğinde sona erer. İkisi de varsayılan olmadığından, iadeler sık sık işlenirken müşteriler ürünü kullanmaya devam eder.
Bir REFUND bildirimi gelir, ancak StoreKit bir consumable'ı iptal edemez çünkü harcanması teslim edilmiş sayılır. Orijinal işlemi bulup bakiyeden düşmek, kendi sunucunuzun yapması gereken bir iştir.
Apple'ın daha önce verdiği bir iadenin geri alındığı anlamına gelir. revocationDate yeniden null değerine döner ve satın alım tekrar geçerli sayılır. Orijinal REFUND bildirimi nedeniyle kaldırdığınız erişim veya bakiye her ne ise yeniden yüklenmelidir; buna karşın pek çok entegrasyon bu bildirime hiç abone olmaz.
Çünkü bu uç nokta yalnızca iptal (revocation) ile birlikte geçersiz kılınan siparişleri döndürür. Revoke seçeneği olmadan işlenen iadeler buna dahil değildir, dolayısıyla yalnızca bu akışa bağlı herhangi bir süreç bunları tamamen gözden kaçırır.
Günde altı bin sorgu ve herhangi bir 30 saniyelik pencerede en fazla 30 sorgu. Bu uç nokta, sürekli sorgulama yerine sayfalama (pagination) belirteçleriyle zamanlanmış çekimler için tasarlanmıştır.



