सभी लेख
Deep diveपढ़ने में 8 मिनट

रिफंड के बाद एक्सेस रद्द करना: वह कदम जो Apple और Google आपके लिए नहीं उठाते

Apple और Google दोनों ही ऐसा रिफंड जारी कर सकते हैं जिसमें ग्राहक अपनी खरीदारी बरकरार रखता है। यहाँ बताया गया है कि एक्सेस कब अपने आप हटाया जाता है, कब यह आपके सर्वर को करना पड़ता है, और वह एक नोटिफिकेशन जिसे ज़्यादातर इंटीग्रेशन कभी संभालते ही नहीं।

रात में डेस्क पर चमकता एक स्मार्टफोन, यह दर्शाता हुआ कि रिफंड की गई खरीदारी का एक्सेस अभी तक रद्द नहीं किया गया है

मुख्य बातें

  • Google Play पर, किसी ऑर्डर को रिफंड करने से अपने आप उपयोगकर्ता का एक्सेस नहीं हटता। एक्सेस तभी हटाया जाता है जब Play Console में "Remove entitlement" चेक किया जाए, या API कॉल में revoke=true पास किया जाए, जो कि डिफ़ॉल्ट नहीं है।
  • revoke विकल्प के बिना जारी किया गया रिफंड Voided Purchases API में कभी दिखाई ही नहीं देता। जो रिवोकेशन जॉब केवल उसी फ़ीड को पढ़ता है, वह ऐसे हर ऑर्डर को चुपचाप छोड़ देगा।
  • Apple, non-consumable और सब्सक्रिप्शन के लिए लाइसेंस अपने आप रद्द कर सकता है, लेकिन एक consumable इस्तेमाल होते ही डिलीवर मान लिया जाता है, इसलिए StoreKit के पास इसे रद्द करने का कोई तरीका नहीं होता। आपके सर्वर को खुद ही बैलेंस घटाना पड़ता है।
  • Apple अपने ही रिफंड को पलट भी सकता है। REFUND_REVERSED नोटिफिकेशन का मतलब है कि पहले दिया गया रिफंड वापस ले लिया गया है, revocationDate फिर से null हो जाता है, और आपने जो भी एक्सेस या बैलेंस हटाया था उसे बहाल करना होगा।
  • Google के voidedReason फ़ील्ड में नौ वैल्यू होती हैं, remorse और accidental_purchase से लेकर fraud, friendly_fraud और unacknowledged_purchase तक, इसलिए जो फ़ीड आपको एक्सेस रद्द करने के लिए कहती है, वही यह भी बताती है कि कौन-सा रिवोकेशन दुरुपयोग है और कौन-सा आपके अपने इंटीग्रेशन की गलती।
  • जिस भी दिन कोई रिफंड की गई खरीदारी अपना एक्सेस बनाए रखती है, उस दिन आप उसके पीछे के कंप्यूट, स्टोरेज और API कॉल्स के लिए उस पैसे से भुगतान कर रहे होते हैं जो आप पहले ही वापस कर चुके हैं।

रिफंड को स्वीकृत करना और एक्सेस हटाना दो अलग-अलग कदम हैं, और इनमें से केवल एक ही अपने आप होता है। जो डेवलपर यह मान लेते हैं कि प्रोसेस हो चुका रिफंड यानी खरीदारी रद्द हो चुकी है, वे दोनों प्रमुख स्टोर्स पर गलत हैं, अलग-अलग तरीकों और अलग-अलग वजहों से। यह ठीक-ठीक जानना कि ऑटोमेशन कहाँ रुकता है, ही रिफंड के बाद एक्सेस रद्द करने के लिए ज़रूरी है, बजाय इसके कि महीनों बाद पता चले कि यह हुआ ही नहीं।

रिफंड डिफ़ॉल्ट रूप से एक्सेस रद्द क्यों नहीं करता

Apple और Google दोनों ही पैसे और एक्सेस को दो अलग-अलग कार्रवाइयाँ मानते हैं। पैसे वाला हिस्सा स्टोर की नीति है: एक बार रिफंड स्वीकृत होने पर वह अंतिम होता है और लेजर अपडेट हो जाता है। एक्सेस वाला हिस्सा वैकल्पिक होता है, या डेवलपर की ज़िम्मेदारी होती है, या दोनों, यह प्लेटफ़ॉर्म और खरीदारी के प्रकार पर निर्भर करता है। दोनों में से कोई भी स्टोर यह नहीं मान लेता कि रिफंड पूरा होते ही आप एक्सेस को गायब कर देना चाहते हैं, क्योंकि कई वैध मामलों (आंशिक रिफंड, गुडविल क्रेडिट, सपोर्ट का इशारा) में उत्पाद को बिल्कुल भी नहीं छीना जाना चाहिए।

Google Play: वह चेकबॉक्स जिस पर किसी का ध्यान नहीं जाता

Play Console में, किसी ऑर्डर को रिफंड करना दो हिस्सों वाला फ़ॉर्म है: पहले रिफंड राशि चुनें, फिर अलग से यह तय करें कि "Remove entitlement" चेक करना है या नहीं। इसे अनचेक छोड़ देना वह डिफ़ॉल्ट रास्ता है जिसे ज़्यादातर सपोर्ट एजेंट अपनाते हैं, क्योंकि ज़्यादातर रिफंड रिक्वेस्ट गुडविल जेस्चर होती हैं जहाँ ग्राहक अपनी खरीदी हुई चीज़ बनाए रखता है। समस्या यह है कि रिफंड कन्फर्मेशन स्क्रीन पर कहीं भी यह नहीं बताया जाता कि इसी तरह ग्राहक ऐसे रिफंड के बाद भी सब्सक्रिप्शन या इन-ऐप आइटम बनाए रख सकता है जिसे आप अंतिम मानना चाहते थे।

API वाले रास्ते में भी वही जाल है

यही विकल्प कोड में भी मौजूद है। orders.refund एंडपॉइंट एक वैकल्पिक क्वेरी पैरामीटर revoke लेता है; जब यह true होता है, तो आइटम का एक्सेस तुरंत खत्म हो जाता है, और रिकरिंग सब्सक्रिप्शन के मामले में आगे के सभी भुगतान भी इसके साथ रद्द हो जाते हैं। पैरामीटर को छोड़ देने पर यह डिफ़ॉल्ट रूप से false हो जाता है: रिफंड दर्ज हो जाता है, खरीदारी का रिकॉर्ड अपडेट होता है, और उपयोगकर्ता उत्पाद का इस्तेमाल बिल्कुल पहले जैसा ही जारी रखता है।

कार्रवाईक्या एक्सेस हटाया गया?क्या Voided Purchases API में दिखाई देता है?
Play Console रिफंड, "Remove entitlement" अनचेकनहींनहीं
Play Console रिफंड, "Remove entitlement" चेक किया गयाहाँ, तुरंतहाँ
orders.refund API, revoke पैरामीटर छोड़ा गया (डिफ़ॉल्ट false)नहींनहीं
orders.refund API, revoke=trueहाँ, तुरंतहाँ

वही आखिरी पंक्ति है जो इंटीग्रेशन को फँसाती है। Voided Purchases API इसलिए मौजूद है ताकि आप बिना हाथ से Play Console देखे रिवोकेशन पाइपलाइन बना सकें, लेकिन यह केवल उन ऑर्डर्स को सूचीबद्ध करता है जो वाकई रिवोकेशन के साथ वॉइड किए गए थे। बिना चेकबॉक्स के, या बिना revoke=true के प्रोसेस किया गया रिफंड, उस फ़ीड के लिए डिज़ाइन के हिसाब से अदृश्य है, किसी बग की वजह से नहीं।

हरी बत्तियों की पंक्तियों के बीच एक अकेली लाल स्टेटस लाइट वाला सर्वर रैक, जो एक खामोश रिवोकेशन फैसले को दर्शाता है

voidedReason फ़ील्ड को समझना जब कोई खरीदारी वाकई वॉइड के रूप में दिखती है, तो voidedReason फ़ील्ड आपको उसकी वजह बताता है, नौ वैल्यू के पैमाने पर: other, remorse, not_received, defective, accidental_purchase, fraud, friendly_fraud, chargeback, और unacknowledged_purchase। यह सिर्फ हिसाब-किताब नहीं है। remorse और accidental_purchase सामान्य रिफंड हैं। fraud, friendly_fraud, और chargeback ऐसे मामले हैं जिन्हें सीधे रद्द करके भूल जाने के बजाय फ्रॉड रिव्यू क्यू में भेजना बेहतर है, क्योंकि आँकड़ों के हिसाब से वही खाता दोबारा ऐसा करने की ज़्यादा संभावना रखता है।

अगर आप सीधे इस फ़ीड पर आधारित सिस्टम बनाते हैं तो एक ऑपरेशनल बात मायने रखती है: purchases.voidedpurchases.list पर एक दिन में 6,000 क्वेरी और किसी भी 30 सेकंड की विंडो में 30 क्वेरी की सीमा है। अगर डिज़ाइन टाइट लूप में पोलिंग करता है, तो यह सीमा जल्दी आ जाती है; तय पैटर्न पेजिनेशन टोकन के साथ शेड्यूल्ड पुल का है, लगातार पोलिंग का नहीं।

Apple: App Store लाइसेंस रद्द कर सकता है, लेकिन consumable नहीं

Apple के App Store Server Notifications V2 रिफंड पूरा होते ही REFUND नोटिफिकेशन भेजते हैं, चाहे वह consumable हो, non-consumable हो, auto-renewable सब्सक्रिप्शन हो, या non-renewing सब्सक्रिप्शन। उस नोटिफिकेशन के बाद क्या होता है, यह पूरी तरह खरीदारी के प्रकार पर निर्भर करता है, क्योंकि StoreKit का अपना रिवोकेशन मैकेनिज़्म चारों में से केवल दो प्रकारों को कवर करता है।

consumable अपवाद क्यों हैं

एक non-consumable या सब्सक्रिप्शन में एक स्थायी entitlement होता है जिसे Apple के सिस्टम बंद कर सकते हैं। एक consumable में ऐसा नहीं होता: एक बार इस्तेमाल हो जाने पर उसे डिलीवर मान लिया जाता है, और StoreKit में रद्द करने के लिए कुछ बचता ही नहीं। अगर किसी ग्राहक ने 500 coins खरीदे, उन्हें खर्च किया, और उसके बाद रिफंड पाया, तो Apple की नज़र में वे coins पहले ही जा चुके हैं। एकमात्र लेजर जिसमें वे अब भी मौजूद हैं, वह आपका है। REFUND नोटिफिकेशन ही आपका एकमात्र संकेत है कि आप मिलते-जुलते मूल transaction ID को ढूँढें और उस बैलेंस को अपने रिकॉर्ड से घटाएँ; Apple की तरफ से यह काम आपके लिए कोई नहीं करेगा।

नोटिफिकेशनइसका मतलबआपके सर्वर को क्या करना है
REFUNDApple ने किसी consumable, non-consumable, auto-renewable, या non-renewing खरीदारी का रिफंड कियाnon-consumable और सब्सक्रिप्शन का एक्सेस तुरंत रद्द करें; consumable बैलेंस खुद घटाएँ
REFUND_DECLINEDApple ने ग्राहक के रिफंड अनुरोध को अस्वीकार कियाकुछ नहीं। एक्सेस कभी खतरे में नहीं था
REFUND_REVERSEDApple ने पहले दिए गए रिफंड को पलट दियाREFUND नोटिफिकेशन की वजह से जो भी एक्सेस या बैलेंस हटाया गया था, उसे बहाल करें
CONSUMPTION_REQUESTApple रिफंड तय करने से पहले आपका सबूत चाहता है12 घंटे के भीतर जवाब दें; यह REFUND से पहले होता है, बाद में नहीं

वह नोटिफिकेशन जिसे लगभग कोई नहीं संभालता

REFUND_REVERSED वह नोटिफिकेशन है जिसे ज़्यादातर इंटीग्रेशन पूरी तरह छोड़ देते हैं, क्योंकि यह Apple के अपने ही फैसले को पलटने की बात करता है, ऐसा मामला जिसके लिए कम ही डेवलपर पहली बार में डिज़ाइन करते हैं। जब यह आता है, तो ट्रांज़ैक्शन पर revocationDate फिर से null हो जाता है: Apple की नज़र में, खरीदारी फिर से मान्य हो जाती है। अगर मूल REFUND आने पर आपके सर्वर ने कोई entitlement हटाया, सब्सक्रिप्शन रिकॉर्ड रद्द किया, या consumable बैलेंस शून्य कर दिया था, तो REFUND_REVERSED यह सब वापस लाने का संकेत है।

इस अंतर को चरण दर चरण कैसे पाटें

  • Google Play पर, हर रिफंड रास्ते को डिफ़ॉल्ट रूप से revoke=true पर सेट करें, चाहे वह Play Console से हो या API से, जब तक कि कोई खास सपोर्ट केस ग्राहक को एक्सेस बनाए रखने की माँग न करे।
  • purchases.voidedpurchases.list को पेजिनेशन टोकन के साथ शेड्यूल पर पोल करें, टाइट लूप में नहीं, और एक दिन में 6,000 और हर 30 सेकंड में 30 रिक्वेस्ट से नीचे रहें।
  • voidedReason की fraud, friendly_fraud, और chargeback वैल्यू को ऑटोमैटिक रिवोक-एंड-क्लोज़ के बजाय रिव्यू क्यू में भेजें।
  • Apple पर, App Store Server Notifications V2 सब्सक्राइब करें और जैसे ही कोई REFUND नोटिफिकेशन किसी non-consumable या सब्सक्रिप्शन का नाम ले, उसका एक्सेस उसी क्षण रद्द करें।
  • हर consumable खरीदारी के साथ मूल transaction ID सहेजें, ताकि REFUND नोटिफिकेशन बिना किसी मैनुअल खोज के घटाने के लिए सही बैलेंस ढूँढ सके।
  • REFUND_REVERSED को स्पष्ट रूप से हैंडल करें और हर रिवोकेशन को उसकी transaction या order ID के साथ लॉग करें, ताकि रिवर्सल ठीक वही बहाल कर सके जो हटाया गया था।

यह अंतर असल में कितना महंगा पड़ता है

इसका ग्राहक के साथ निष्पक्षता से कोई लेना-देना नहीं है; यह इस बारे में है कि पैसा जा चुकने के बाद भी क्या चलता रहता है। एक रिफंड की गई सब्सक्रिप्शन जो अब भी आपके API को कॉल करती है, डेटा स्टोर करती है, और कंप्यूट खर्च करती है, वह कोई नैतिक समस्या नहीं है, यह एक लाइन आइटम है: आपने ऐसी खरीदारी के लिए इन्फ्रास्ट्रक्चर का बिल चुकाया जिसका राजस्व आप पहले ही वापस कर चुके हैं। स्टोर आपके लिए वह लागत ट्रैक नहीं करते, क्योंकि उनकी तरफ से रिफंड पैसा हिलते ही पूरा हो जाता है। एक्सेस वाला पक्ष पूरी तरह से आपका खुद का बिल है जिसे आपको नियंत्रित करना है।

यही वजह है कि RefundHalt रिवोकेशन को रिफंड पाइपलाइन का हिस्सा मानता है, न कि बाद में जोड़ा गया विचार: एक REFUND इवेंट और एक एक्सेस बदलाव को एक ही कार्रवाई के रूप में दर्ज किया जाता है, और एक REFUND_REVERSED इवेंट स्थिति को अपने आप बहाल कर देता है, बजाय इसके कि किसी के ध्यान देने का इंतज़ार किया जाए कि ग्राहक अब भी उस चीज़ से बाहर है जो Apple पहले ही वापस दे चुका है।

अक्सर पूछे जाने वाले सवाल

क्या Google Play पर किसी खरीदारी को रिफंड करने से एक्सेस अपने आप रद्द हो जाता है?
नहीं। एक्सेस तभी हटाया जाता है जब Play Console में "Remove entitlement" चेक किया गया हो या orders.refund API कॉल में revoke=true शामिल हो। इनमें से कोई भी डिफ़ॉल्ट नहीं है, इसलिए रिफंड दर्ज हो सकता है जबकि ग्राहक उत्पाद का इस्तेमाल जारी रखता है।
App Store पर किसी consumable इन-ऐप खरीदारी का रिफंड होने पर क्या होता है?
Apple एक REFUND नोटिफिकेशन भेजता है, लेकिन StoreKit के पास किसी consumable को रद्द करने का कोई तरीका नहीं होता क्योंकि खर्च होते ही उसे डिलीवर मान लिया जाता है। डेवलपर के सर्वर को मूल ट्रांज़ैक्शन ढूँढना होता है और बैलेंस मैन्युअल रूप से घटाना होता है।
REFUND_REVERSED नोटिफिकेशन क्या है और क्या इसे खास तरीके से हैंडल करने की ज़रूरत है?
REFUND_REVERSED का मतलब है कि Apple ने पहले दिया गया रिफंड पलट दिया है; revocationDate फिर से null हो जाता है और खरीदारी फिर से मान्य हो जाती है। मूल REFUND ने जो भी एक्सेस या बैलेंस हटाया था, उसे बहाल करना ज़रूरी है, और ज़्यादातर इंटीग्रेशन इस नोटिफिकेशन को कभी सुनते ही नहीं।
एक रिफंड की गई Google Play ऑर्डर Voided Purchases API में क्यों नहीं दिखेगी?
यह API केवल उन ऑर्डर्स को सूचीबद्ध करता है जो रिवोकेशन के साथ वॉइड किए गए थे। अगर कोई रिफंड बिना revoke विकल्प के प्रोसेस किया गया था, तो वह ऑर्डर उस एंडपॉइंट से बिल्कुल भी नहीं लौटता, इसलिए जो पाइपलाइन केवल उसी पर निर्भर करता है, वह ऐसे रिफंड को पूरी तरह छोड़ देगा।
मैं Google के Voided Purchases API को कितनी बार पोल कर सकता हूँ?
इस एंडपॉइंट की दर सीमा एक दिन में 6,000 क्वेरी और किसी भी 30 सेकंड की विंडो में 30 क्वेरी है, इसलिए यह लगातार पोलिंग के बजाय पेजिनेशन टोकन के साथ शेड्यूल्ड पुल के लिए बनाया गया है।

स्रोत और आगे की जानकारी

RefundHalt

App Store और Google Play के लिए refund ऑटोपायलट

आगे पढ़ें

अगला refund अनुरोध पहले ही रास्ते में है.

RefundHalt को उतने समय में सेटअप करें, जितने में आप उस refund के बारे में एक और सपोर्ट ईमेल पढ़ते हैं जिस पर आपत्ति नहीं कर पाए.