Skip to main content
पर्दे के पीछे

हमारे अपने पेमेंट सिस्टम के ऑडिट में क्या निकला

दो गंभीर खामियाँ, और दोनों में से कोई भी वहाँ नहीं थी जहाँ हम ढूंढते। उन गलतियों पर एक खुली रिपोर्ट, जो अब ठीक की जा चुकी हैं।

प्रकाशित लेखक AlphaCloud5 मिनट में पढ़ें

अगस्त 2026 में हमने अपने पेमेंट सिस्टम की शुरू से जांच की। इसलिए नहीं कि कुछ हुआ था, बल्कि इसलिए कि वह कुछ ही समय पहले लाइव हुआ था और हम जानना चाहते थे कि हमसे क्या छूट गया।

हमसे काफ़ी कुछ छूट गया था। यह लेख उन खामियों के नाम बताता है। यहाँ बताई गई हर चीज़ ठीक की जा चुकी है, और इसमें से ज़्यादातर पहले से ही सार्वजनिक चेंजलॉग में थी।

वह मॉडलिंग गलती, जिससे दोनों गंभीर खामियाँ निकलीं

जब कोई हमसे Premium खरीदता है, तो हमारे पेमेंट प्रोवाइडर के पास एक कस्टमर रिकॉर्ड बनता है। हमने वह रिकॉर्ड सर्वर ID के तहत सेव किया, क्योंकि आखिरकार Premium एक सर्वर पर ही लगता है।

यही बिल्कुल गलत मॉडल है, और यही दो सबसे गंभीर खामियों की जड़ था।

एक कस्टमर रिकॉर्ड एक खरीदार का होता है। उसमें उसका पेमेंट तरीका, उसकी इनवॉइस हिस्ट्री और, जैसे ही कोई दो सर्वरों के लिए भुगतान करता है, कई सब्सक्रिप्शन होते हैं। इसे सर्वर ID से जोड़ने का मतलब है कि कई लोग एक ही पंक्ति की ओर इशारा करते हैं।

खामी 1: किसी और का कस्टमर डेटा, बिना भुगतान के बदला जा सकता था

किसी सर्वर का कोई भी सदस्य खरीदारी कर सकता है, और जो फ़ंक्शन कस्टमर रिकॉर्ड ढूंढता या बनाता है, वह भुगतान से पहले चलता है। वह सर्वर ID से पंक्ति ढूंढता था और चुपचाप उसे नए संभावित खरीदार के नाम पर लिख देता था, पेमेंट प्रोवाइडर के पास उसके ईमेल पते समेत।

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

इसे ऐसे ठीक किया गया कि किसी भी दोबारा इस्तेमाल से पहले जांचा जाता है कि रिकॉर्ड सच में इसी अकाउंट का है या नहीं। मेल न खाने पर एक अलग रिकॉर्ड बनाया जाता है और दूसरे को छुआ नहीं जाता।

खामी 2: बिलिंग पोर्टल दूसरों के सब्सक्रिप्शन दिखा देता था

वही मॉडलिंग गलती, एक स्तर आगे। मालिकाना हक़ की जांच सर्वर के सब्सक्रिप्शन पर होती थी, लेकिन पोर्टल सेशन कस्टमर रिकॉर्ड के लिए खोला जाता था, जिसमें डिज़ाइन के हिसाब से कई सर्वर हो सकते हैं।

तो जिसने भी किसी एक सर्वर पर वैध रूप से खरीदारी की, वह उन सब्सक्रिप्शन को देख और मैनेज कर सकता था जो उसी रिकॉर्ड में दूसरे सर्वरों के लिए थे।

इसे ऐसे ठीक किया गया कि कस्टमर रिकॉर्ड अब सर्वर की पंक्ति से नहीं, बल्कि उस खास खरीदारी से निकाला जाता है।

दो खामियाँ, जहाँ कोड हमारी अपनी शर्तों के उलट था

यह वह श्रेणी है जिसने हमें सबसे ज़्यादा परेशान किया, क्योंकि यह तकनीकी नहीं है।

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

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

दूसरा दौर पहले से भी बुरा था

पहली 21 खामियों के बाद हमने कुछ हफ़्तों बाद फिर से देखा। यह सही फ़ैसला था, क्योंकि सबसे गंभीर खामी तभी सामने आई।

सर्वर B पर रिफ़ंड सर्वर A का भुगतान किया हुआ लाइफ़टाइम लाइसेंस डिलीट कर देता था। रिफ़ंड और चार्जबैक की सूचनाओं में सिर्फ़ एक भुगतान का नाम होता है, सर्वर का नहीं। हम उन्हें कस्टमर रिकॉर्ड के ज़रिए जोड़ते थे। पहले दौर में सही तरह से यह तय कर लेने के बाद कि एक रिकॉर्ड कई सर्वरों में एक खरीदार का होता है, ठीक यही जोड़ना अब साफ़-साफ़ एक ही नतीजे पर नहीं पहुँचता था। जिसने दो सर्वरों के लिए भुगतान किया हो और एक के लिए रिफ़ंड पाया हो, वह दूसरे का लाइफ़टाइम लाइसेंस खो सकता था।

अब यह चार सटीक चरणों से होता है, और जो कुछ साफ़-साफ़ तय नहीं हो पाता, उसकी रिपोर्ट की जाती है, अंदाज़ा नहीं लगाया जाता।

जीता गया चार्जबैक कुछ भी वापस नहीं लाता था। चार्जबैक होने पर कोड खरीदारी की पंक्ति डिलीट कर देता था, जो इस बात का अकेला सबूत थी कि इस सर्वर के पास कभी लाइफ़टाइम लाइसेंस था। बाद में केस जीतने पर भी इसे पलटा नहीं जा सकता था। अब यह डिलीट करने के बजाय मार्क करता है।

डेटा रिटेंशन भुगतान किए गए सर्वरों को डिलीट कर देता था। जब बॉट किसी सर्वर को छोड़ता है, तो उसका डेटा 30 दिन बाद हटा दिया जाता है। उस सफ़ाई ने कभी नहीं पूछा कि उससे कोई चालू सब्सक्रिप्शन जुड़ा है या नहीं।

हमने इससे क्या सीखा

बग पेमेंट कोड में नहीं थे। एक भी गंभीर खामी किसी रकम, करेंसी या गणना से जुड़ी नहीं थी। सब उसके पीछे के मॉडल में थीं: कस्टमर रिकॉर्ड किसका है, भुगतान किसका है, सर्वर किसका है। जो कोई पेमेंट सिस्टम का ऑडिट रकम देखकर करता है, वह खतरनाक हिस्से से नज़र हटा रहा है।

एक ऑडिट को दूसरे दौर की ज़रूरत होती है। सबसे गंभीर खामी पहले दौर के एक फ़िक्स का सीधा नतीजा थी। यह संयोग नहीं है: किसी मॉडल को सुधारने से वे धारणाएँ टूट जाती हैं जो कहीं और अब भी मानी जा रही होती हैं।

"हमारी शर्तें ऐसा कहती हैं" एक जांचने लायक वाक्य है। दो खामियाँ उस बीच का फ़र्क थीं जो हमने लिखित में वादा किया था और जो कोड करता था। इसे पढ़ा जा सकता है, और कोई ऐसा करता नहीं, क्योंकि यह इंजीनियरिंग नहीं बल्कि कानून जैसा दिखता है।

खरीदारी की पूरी शर्तें हमारी सेवा की शर्तों में हैं, वापसी का अधिकार वापसी के तहत।

और लेख

पर्दे के पीछे

AlphaCloud में लगभग सब कुछ मुफ़्त क्यों रहता है

ज़्यादातर Discord बॉट अपने सबसे काम के फ़ीचर सब्सक्रिप्शन के पीछे बंद कर देते हैं। हम जानबूझकर ऐसा नहीं करते, और इसका उदारता से उतना लेना-देना नहीं है जितना लगता है।

पर्दे के पीछे

हमारे कोड के अंदर का कानून

रद्द करने का एक बटन जिसे बिना लॉगिन के काम करना चाहिए। एक फ़ॉर्म जो आपको कभी अस्वीकार नहीं कर सकता। हमारे लिए कानूनी दायित्व खानापूर्ति वाला टेक्स्ट नहीं, बल्कि आर्किटेक्चर क्यों हैं।

पर्दे के पीछे

4,000 Discord सर्वरों पर नज़र: Grafana, PostHog और उन्होंने हमें क्या दिखाया

एक लूप जो चुपचाप हमारा आधा ट्रैफ़िक पैदा कर रहा था, रोज़ 30 लाख बेमतलब रिक्वेस्ट और एक हमला जो एक मिनट में खत्म हो गया। हमारी मॉनिटरिंग क्या देखती है, और क्या जानबूझकर नहीं देखती।