मेरा ज़्यादातर दिन Undelivered Consumption Percentage Non Zero Error में निकल गया। इसलिए नहीं कि यह कोई मुश्किल एरर है। बल्कि इसलिए कि मैंने delivery Status को DELIVERED के अलावा कुछ और सेट कर दिया था और consumption Percentage को 60000 पर छोड़ दिया था, और Apple इस कॉम्बिनेशन को बिना किसी नरमी के रिजेक्ट कर देता है।
यह असली सवाल तक पहुँचने का एक घुमावदार रास्ता है।
अगर आप यह समझने की कोशिश कर रहे हैं कि Apple रिफंड रिक्वेस्ट का ऑटोमैटिक जवाब कैसे दिया जाए, तो यह काम ज़्यादातर टीमों की सोच से कहीं छोटा है। मुश्किल हिस्सा कभी API कॉल नहीं था। मुश्किल है उसमें डालने के लिए डेटा का होना। फ़ील्ड-दर-फ़ील्ड पूरी जानकारी Apple CONSUMPTION_REQUEST समझाया गया में मौजूद है। यह पोस्ट इसे इस तरह सेट करने के बारे में है कि किसी को जागकर इंतज़ार न करना पड़े।
मुख्य बातें
Apple रिफंड ऑटोमेशन एक webhook और एक PUT का मामला है। Apple एक CONSUMPTION_REQUEST भेजता है, और आपको 12 घंटे के भीतर जवाब देना होता है।
आपको एक In-App Purchase key चाहिए, न कि आपकी App Store Connect API key। ये दोनों एक-दूसरे की जगह इस्तेमाल नहीं हो सकतीं।
पाँच में से तीन फ़ील्ड ज़रूरी हैं, और इनमें से दो पर ऐसी शर्तें हैं जो सिर्फ एरर के रूप में सामने आती हैं।
कस्टमर के कंसेंट के बिना आपको बिल्कुल जवाब नहीं देना चाहिए, और कंसेंट को बाद में पीछे जाकर नहीं लिया जा सकता।
यह एंडपॉइंट v2 पर शिफ्ट हो गया है। प्रोडक्शन api.storekit.apple.com है, और सैंडबॉक्स आपको 12 घंटे के बजाय सिर्फ 5 मिनट देता है।
डेवलपर्स के लिए Apple रिफंड रिक्वेस्ट असल में कैसे काम करती हैं
कोई कस्टमर reportaproblem.apple.com खोलता है, पिछले 90 दिनों में हुई खरीदारी को ढूँढता है, एक वजह चुनता है, और सबमिट कर देता है। अगर आपने App Store Server Notifications V2 कॉन्फिगर किया है, तो आपके एंडपॉइंट को एक CONSUMPTION_REQUEST एक साइन्ड JWS पेलोड के रूप में मिलता है, जिसमें ट्रांज़ैक्शन, प्रोडक्ट, और उनकी चुनी गई वजह शामिल होती है।
यह webhook ही Apple Store इन-ऐप परचेज़ रिफंड का पूरा ट्रिगर है। आपके dashboard में कुछ भी नहीं दिखता, और चेक करने के लिए कोई क्यू नहीं होता। यही वजह है कि टीमों को यह सिस्टम होने का पता हफ़्तों बाद चलता है, जब तक वे इसकी वजह से पैसे खोना शुरू नहीं कर देती हैं।
इसके बाद आपके पास 12 घंटे होते हैं।
एक बात को सुधारना ज़रूरी है, क्योंकि यह हर जगह दोहराई जाती है। यह सिर्फ consumables तक सीमित नहीं है। Apple के मौजूदा डॉक्यूमेंटेशन के मुताबिक, CONSUMPTION_REQUEST किसी भी प्रोडक्ट टाइप के लिए आ सकता है: consumable, non-consumable, non-renewing subscription, auto-renewable subscription। पुरानी गाइड्स अब भी कहती हैं कि यह सिर्फ consumables और subs के लिए है। वे आउटडेटेड हैं।
"जवाब देना" का असल में क्या मतलब है
आप एक JSON बॉडी को एक एंडपॉइंट पर PUT करते हैं।
PUT https://api.storekit.apple.com/inApps/v2/transactions/consumption/{transactionId}
सैंडबॉक्स api.storekit-sandbox.apple.com है, वही पाथ। सफल होने पर 202 Accepted मिलता है, 200 नहीं, जो लोगों को तब उलझा देता है जब वे स्टेटस कोड पर असर्शन कर रहे होते हैं।
ऑथेंटिकेशन एक शॉर्ट-लिव्ड ES256 JWT होता है, जिसे In-App Purchase key से साइन किया जाता है, जो App Store Connect में Users and Access के अंदर, Integrations में जनरेट होती है। यह आपकी App Store Connect API key नहीं है। .p8 फ़ाइल सिर्फ एक बार, बनने के समय ही डाउनलोड होती है, इसलिए अगर आप इसे खो देते हैं तो आपको नई फ़ाइल जनरेट करनी होगी और इसे इस्तेमाल करने वाली हर चीज़ को रोटेट करना होगा। मुझसे पूछिए कि मुझे यह कैसे पता है।
पाँच फ़ील्ड, और वे दो जो परेशान करती हैं
बॉडी एक Consumption Request होती है। तीन ज़रूरी, दो ऑप्शनल।
Customer Consented (ज़रूरी)। यह तभी True होगा जब आपके पास असली कंसेंट हो। इस पर आगे और बात होगी, क्योंकि यही पूरी प्रक्रिया का गेट है।
Delivery Status (ज़रूरी)। यह बताता है कि खरीदारी पहुँची और काम कर रही थी या नहीं।
Sample Content Provided (ज़रूरी)। यह बताता है कि खरीदारी से पहले कोई फ्री सैंपल, ट्रायल, या फंक्शनैलिटी की जानकारी मौजूद थी या नहीं।
Consumption Percentage (ऑप्शनल)। यह बताता है कि उपयोगकर्ता ने कितना इस्तेमाल किया, milliunits में। 0 से 100000 तक, यानी 45.2 प्रतिशत का मतलब है 45200।
Refund Preference (ऑप्शनल)। GRANT_FULL, GRANT_PRORATED, DECLINE, या NO_PREFERENCE।
दो ऐसी शर्तें हैं, जिनमें से हर एक पर आपका एक दोपहर का समय लग सकता है।
अगर Delivery Status DELIVERED नहीं है, तो Consumption Percentage 0 होना चाहिए। इसके अलावा कुछ भी Undelivered Consumption Percentage Non Zero Error के साथ फेल हो जाता है। यही मेरे दिन की कहानी थी।
और अगर आप किसी auto-renewable subscription के लिए GRANT_PRORATED भेजते हैं, तो consumptionPercentage को बिल्कुल शामिल न करें। Apple इसे बिलिंग पीरियड में बीते समय से खुद कैलकुलेट करता है और आपकी वैल्यू को Consumption Percentage Auto Renewable Subscription Error के साथ रिजेक्ट कर देता है। कम से कम एरर के नाम जानकारी देने वाले हैं, जो मेरी उम्मीद से बेहतर है।
कंसेंट एक गेट है, महज़ एक फॉर्मेलिटी नहीं
इस मामले में Apple का डॉक्यूमेंटेशन असामान्य रूप से सीधा है: अगर कस्टमर ने कंसेंट नहीं दिया है, तो नोटिफिकेशन का जवाब न दें। Customer Consented को false सेट करके जवाब न दें। बस जवाब ही न दें।
आप रिक्वेस्ट आने के बाद जाकर कंसेंट भी नहीं ले सकते। यह पहले से मौजूद होना चाहिए, यानी यह भाषा आपके टर्म्स या प्राइवेसी पॉलिसी में इसे शिप करने से पहले ही होनी चाहिए, न कि आपके पहले विवादित रिफंड के बाद। Apple का गाइडेंस यह है कि कंसेंट स्वतंत्र रूप से दिया गया, स्पष्ट, सुविज्ञ और असंदिग्ध होना चाहिए, कि opt-in, opt-out से बेहतर है, और इसे हासिल करना पूरी तरह आपकी ज़िम्मेदारी है।
व्यावहारिक रूप से: एक लाइन जिसमें कहा गया हो कि आप रिफंड रिक्वेस्ट की समीक्षा में मदद के लिए खरीदारी और इस्तेमाल का डेटा Apple के साथ शेयर कर सकते हैं, साथ ही इसे वापस लेने का कोई तरीका। इस पर अपनी अलग से लीगल रिव्यू ज़रूर करवाएँ। मैं वकील नहीं हूँ और यह कोई सलाह नहीं है।
डेवलपर्स App Store रिफंड रिक्वेस्ट को कैसे ऑटोमेट करते हैं
पाइपलाइन, उस क्रम में जिसमें मैं इसे बनाऊँगा:
App Store Connect में App Information के अंदर एक Production Server URL कॉन्फिगर करें, और Version 2 चुनें। Consumption requests V1 पर नहीं आती हैं।
किसी भी फ़ील्ड पर भरोसा करने से पहले JWS सिग्नेचर चेन को Apple के रूट सर्टिफिकेट्स के साथ वेरिफाई करें।
ट्रांज़ैक्शन को किसी यूज़र से मैच करें। खरीदारी के समय अटैच किया गया appAccountToken सबसे तेज़ रास्ता है। इसके बिना आपको प्रोडक्ट और डेट के आधार पर रिवर्स-लुकअप करना पड़ेगा, जो जल्दी ही उलझा हुआ हो जाता है।
कंसेंट चेक करें। कंसेंट नहीं है, तो जवाब नहीं। इसे लॉग करें और आगे बढ़ें।
असली इस्तेमाल का डेटा निकालें। कोई अनुमान नहीं, कोई डिफ़ॉल्ट वैल्यू नहीं। अगर आप "उन्होंने इसमें से कितना इस्तेमाल किया" इस सवाल का जवाब नहीं दे सकते, तो यही आपके ऑटोमेशन में असली कमी है।
ऊपर बताई गई दोनों शर्तों को लागू करते हुए ConsumptionRequest बनाएँ।
इसे PUT करें, 202 कन्फर्म करें, और जो पेलोड आपने भेजा है उसे स्टोर करें।
स्टेप 5 वह जगह है जहाँ ज़्यादातर कोशिशें अटक जाती हैं। API कॉल तो बस कुछ घंटों का काम है। लेकिन सात हफ़्ते पहले हुई किसी खरीदारी के लिए हर यूज़र का सही कंज़म्प्शन डेटा होना एक डेटा मॉडलिंग समस्या है, जिसे आपने शायद अभी तक हल नहीं किया होगा।
इसे प्रोडक्शन में टिकाऊ बनाने के लिए क्या ज़रूरी है
हैप्पी पाथ से आगे, चार बातें:
Idempotency। Apple नोटिफिकेशन्स को रीट्राय करता है, और आपके हैंडलर को एक ही नोटिफिकेशन दो बार दिखेगी। notification UUID पर dedupe करें, वरना आप डुप्लिकेट जवाब फ़ाइल कर देंगे।
ऐसा अलर्टिंग सिस्टम जो वाकई किसी को पेज करे। चुपचाप फेल होने वाला हैंडलर, हैंडलर न होने से भी बुरा है, क्योंकि आप यह मानते रहेंगे कि आप सुरक्षित हैं।
एक सैंडबॉक्स टेस्ट हार्नेस। सैंडबॉक्स आपको 12 घंटे के बजाय सिर्फ 5 मिनट देता है, इसलिए आप इसे हाथ से कैज़ुअली टेस्ट नहीं कर सकते। इसे स्क्रिप्ट करें।
और हर केस के लिए यह रिकॉर्ड कि आपने क्या भेजा था। जब कोई रिफंड अस्वीकार होता है, तो आप यह जानना चाहेंगे कि आपने कौन सी preference फ़ाइल की थी और क्यों। जब आपके DECLINE के बावजूद कोई रिफंड मंज़ूर हो जाता है, तो आप यह भी जानना चाहेंगे।
ईमानदार अनुमान: अगर आपने पहले कभी webhook plumbing बनाई है, तो इसे जोड़ने में कुछ दिन लगेंगे, और इस पर भरोसा करने में कुछ हफ़्ते। इसमें से ज़्यादातर हिस्सा Apple की तरफ़ का नहीं है।
या फिर यह काम किसी और चीज़ से करवाएँ
अगर आप पहले से RevenueCat या Adapty इस्तेमाल करते हैं, तो पहले देख लें कि वे क्या ऑफर करते हैं। दोनों ने रिफंड रिस्पॉन्स फीचर्स जोड़े हैं, और आपकी रिसीट्स और कस्टमर मैपिंग वहाँ पहले से मौजूद है।
खास तौर पर इसी काम के लिए बना Apple रिफंड मैनेजमेंट सॉफ़्टवेयर और आगे जाता है। Refund Sensor आपके पास मौजूद App Store Connect keys पर काम करता है, विंडो के भीतर हर रिक्वेस्ट का जवाब ट्रांज़ैक्शन एविडेंस के साथ देता है, और हर केस का नतीजा लॉग करता है। कोई SDK नहीं, कोई कोड बदलाव नहीं, कनेक्ट करने में बस करीब पाँच मिनट। अगर आप तुलना करना चाहते हैं, तो हमने Apple रिफंड मैनेजमेंट टूल्स में बाकी विकल्पों की तुलना की है।
कस्टमर की तरफ़ की बात, क्योंकि लोग पूछते हैं
App Store खरीदारी के लिए रिफंड रिक्वेस्ट कैसे करें, यूज़र की नज़र से: reportaproblem.apple.com पर जाएँ, साइन इन करें, पिछले 90 दिनों में हुई खरीदारी ढूँढें, एक वजह चुनें। Apple कस्टमर्स को बताता है कि उन्हें 48 घंटों के भीतर जवाब मिल जाएगा।
आप StoreKit की refund request sheet का इस्तेमाल करके यही फ़ॉर्म अपने ऐप के अंदर भी दिखा सकते हैं, जो नाराज़ यूज़र्स को वन-स्टार रिव्यू के बजाय Apple की तरफ़ भेज देता है। यह रिफंड मंज़ूर नहीं कर सकता। ऐसा केवल Apple ही कर सकता है। Apple Support की रिफंड रिक्वेस्ट करने की गाइड में बताया गया है कि कस्टमर को क्या दिखता है।
इसे साफ़ तौर पर कहना ज़रूरी है, क्योंकि App Store रिफंड मैनेजमेंट को ज़रूरत से ज़्यादा बढ़ा-चढ़ाकर पेश किया जाता है: आप किसी रिफंड को मंज़ूर या अस्वीकार नहीं कर सकते। आप सिर्फ़ Apple के फ़ैसला लेने के दौरान उनके सामने सबूत रख सकते हैं, और अपनी preference बता सकते हैं। यही वह पूरा असर है जो आपको मिलता है, और यह उससे ज़्यादा है जितना ज़्यादातर डेवलपर्स इस्तेमाल करते हैं।
स्रोत
अक्सर पूछे जाने वाले प्रश्न
हाँ। इसी काम के लिए CONSUMPTION_REQUEST नोटिफिकेशन और Send Consumption Information एंडपॉइंट मौजूद हैं। आप रिफंड को मंज़ूर या अस्वीकार नहीं कर सकते, लेकिन Apple आपके भेजे गए डेटा को फ़ैसले के इनपुट के रूप में इस्तेमाल करता है।
App Store Server Notifications V2 रिसीव करें, JWS सिग्नेचर वेरिफाई करें, ट्रांज़ैक्शन को किसी यूज़र से मैच करें, कंसेंट चेक करें, असली कंज़म्प्शन डेटा तैयार करें, और 12 घंटे के भीतर इसे v2 कंज़म्प्शन एंडपॉइंट पर PUT करें। बाकी सब idempotency, अलर्टिंग और रिकॉर्ड-कीपिंग है।
Users and Access के अंदर Integrations में मिलने वाली In-App Purchase key। आपकी App Store Connect API key इन कॉल्स को ऑथेंटिकेट नहीं करेगी, और आपको ऐसा 401 एरर मिलेगा जो क्लॉक प्रॉब्लम जैसा दिखता है।
नहीं, यह ऑप्शनल है। लेकिन अगर deliveryStatus DELIVERED नहीं है तो यह 0 होना चाहिए, और प्रोरेटेड ऑटो-रिन्यूएबल सब्सक्रिप्शन के लिए इसे पूरी तरह छोड़ देना चाहिए।
जवाब न दें। इस बारे में Apple का गाइडेंस बिल्कुल साफ है, और रिक्वेस्ट आने के बाद कंसेंट नहीं लिया जा सकता।
यह एक इनपुट है, गारंटी नहीं। Apple का कहना है कि वह कई तरह के फैक्टर्स का इस्तेमाल करता है। प्रैक्टिस में, भारी इस्तेमाल हो चुकी खरीदारी पर अच्छे सबूत वाला DECLINE, बिना जवाब वाली रिक्वेस्ट की तुलना में कहीं ज़्यादा बार खारिज कर दिया जाता है।



