सुरक्षित बिलिंग अपडेट के लिए इडेम्पोटेंट पेमेंट वेबहुक चेकलिस्ट
इडेम्पोटेंट पेमेंट वेबहुक चेकलिस्ट — इवेंट्स को डेडुप करने, retries संभालने, और invoices, subscriptions, entitlements को सुरक्षित रूप से अपडेट करने के तरीके।

क्यों पेमेंट वेबहुक डुप्लिकेट अपडेट बनाते हैं
एक पेमेंट वेबहुक वह संदेश है जो आपका पेमेंट प्रोवाइडर आपके बैकएंड को तब भेजता है जब कुछ महत्वपूर्ण होता है—जैसे कि चार्ज सफल हो गया, एक invoice पे किया गया, सब्सक्रिप्शन रिन्यू हुआ, या रिफंड जारी हुआ। यह बेसिकली प्रोवाइडर कह रहा है, “यह हुआ। अपने रिकॉर्ड अपडेट करो।”
डुप्लिकेट इसलिए आते हैं क्योंकि वेबहुक डिलीवरी विश्वसनीय होने के लिए डिज़ाइन की गई है, "एक ही बार" सुनिश्चित करने के लिए नहीं। अगर आपका सर्वर धीमा है, टाइमआउट हो रहा है, एरर लौटा रहा है, या थोड़ी देर के लिए अनउपलब्ध है, तो प्रोवाइडर आमतौर पर वही इवेंट retries करेगा। आप अक्सर दो अलग इवेंट भी देख सकते हैं जो एक ही वास्तविक कार्रवाई से संबंधित हों (उदाहरण के लिए, एक invoice इवेंट और उसी भुगतान से जुड़ा payment इवेंट)। इवेंट्स आउट ऑफ़ ऑर्डर भी आ सकते हैं, खासकर रिफंड जैसे त्वरित फॉलो-अप्स के साथ।
अगर आपका हैंडलर idempotent नहीं है, तो वह एक ही इवेंट को दो बार लागू कर सकता है, और इसका असर ग्राहक और फाइनेंस टीम तुरंत देख लेंगी:
- एक invoice को दो बार paid चिह्नित करना, जिससे duplicate अकाउंटिंग एंट्रीज़ बनती हैं
- रिन्यूअल दो बार लागू होना, जिससे एक्सेस बहुत आगे बढ़ जाना
- Entitlements दो बार देना (अतिरिक्त क्रेडिट्स, सीट्स, या फीचर्स)
- रिफंड या चार्जबैक सही तरीके से एक्सेस को वापस न कर पाना
यह सिर्फ़ "best practice" नहीं है। यह भरोसेमंद बिलिंग और सपोर्ट टिकट पैदा करने वाली बिलिंग के बीच का फर्क है।
इस चेकलिस्ट का लक्ष्य सरल है: हर आने वाले इवेंट को "अधिकतम एक बार लागू करें" की तरह हाथ में रखें। आप हर इवेंट के लिए एक स्थिर पहचान संग्रहीत करेंगे, retries को सुरक्षित रूप से हैंडल करेंगे, और invoices, subscriptions, और entitlements को नियंत्रित तरीके से अपडेट करेंगे। अगर आप बैकएंड को कोई-कोड टूल जैसे AppMaster में बना रहे हैं, तो वही नियम लागू होते हैं: आपको एक साफ़ डेटा मॉडल और एक दोहराने योग्य हैंडलर फ़्लो चाहिए जो retries के दौरान भी सही रहे।
वेबहुक्स पर लागू करने योग्य idempotency के बेसिक्स
Idempotency का मतलब है कि एक ही इनपुट को एक से ज्यादा बार प्रोसेस करने पर अंतिम स्थिति समान रहती है। बिलिंग भाषा में: एक invoice एक बार ही paid होना चाहिए, एक subscription एक बार अपडेट होना चाहिए, और एक्सेस एक बार ही granted होना चाहिए, भले ही वेबहुक दो बार डिलीवर हो।
प्रोवाइडर तब retries करते हैं जब आपके endpoint को एक तेज़ सफल response नहीं मिलता—टाइमआउट, 5xx, या नेटवर्क ड्रॉप। वे एक ही इवेंट रिपीट करेंगे। यह अलग है उन अलग इवेंट्स से जो वास्तविक बदलाव का प्रतिनिधित्व करते हैं, जैसे कुछ दिनों बाद आया refund। नए इवेंट्स के अलग IDs होते हैं。
इसे काम करने लायक बनाने के लिए आपको दो चीज़ों की ज़रूरत है: स्थिर पहचानकर्ता (stable identifiers) और जिस चीज़ का आपने पहले ही सामना किया है उसका छोटा “मेमोरी”।
कौन से IDs मायने रखते हैं (और क्या स्टोर करें)
ज़्यादातर पेमेंट प्लेटफ़ॉर्म एक इवेंट ID प्रदान करते हैं जो वेबहुक इवेंट के लिए यूनिक होता है। कुछ payload के अंदर request ID, idempotency key, या कोई यूनिक payment object ID (जैसे charge या payment intent) भी देते हैं।
स्टोर करें जो आपको एक सवाल का जवाब देने में मदद करे: “क्या मैंने पहले ही यही ठीक वही इवेंट लागू किया है?”
एक व्यावहारिक न्यूनतम सूची:
- Event ID (unique key)
- Event type (डिबगिंग में मदद के लिए)
- Received timestamp
- Processing status (processed/failed)
- प्रभावित customer, invoice, या subscription का संदर्भ
मुख्य कदम यह है कि इवेंट ID को एक तालिका में यूनिक constraint के साथ स्टोर करें। फिर आपका हैंडलर सुरक्षित रूप से यह कर सकता है: पहले event ID insert करें; अगर वह पहले से मौजूद है, तो रुक जाएँ और 200 लौटाएँ।
डेडुप रिकॉर्ड कितने समय तक रखें
लेट retries और सपोर्ट जांच के लिए डेडुप रिकॉर्ड पर्याप्त समय तक रखें। एक सामान्य विंडो 30 से 90 दिन है। अगर आप chargebacks, disputes, या लंबी subscription cycles से निपटते हैं, तो इन्हें लंबा रखें (6 से 12 महीने), और पुरानी rows purge करते रहें ताकि तालिका तेज़ रहे।
Generated बैकएंड जैसे AppMaster में यह साफ़ तौर पर WebhookEvents मॉडल बन कर आता है जिसमें event ID पर unique फील्ड हो, और एक बिजनेस प्रोसेस जो duplicate मिलने पर जल्दी बाहर निकल जाए।
इवेंट्स के डेडुप के लिए एक सरल डेटा मॉडल डिज़ाइन करें
एक अच्छा वेबहुक हैंडलर ज्यादातर डेटा की समस्या है। अगर आप प्रत्येक प्रोवाइडर इवेंट को सटीक रूप से एक बार रिकॉर्ड कर सकते हैं, तो आगे की हर चीज़ सुरक्षित हो जाती है।
एक रिसीट-लॉग की तरह एक तालिका से शुरू करें। PostgreSQL में (और AppMaster के Data Designer में modeled करते समय), इसे छोटा और सख्त रखें ताकि duplicates जल्दी फेल हों।
न्यूनतम जरूरत
webhook_events टेबल के लिए एक व्यावहारिक बेसलाइन:
provider(text, जैसे "stripe")provider_event_id(text, required)status(text, जैसे "received", "processed", "failed")processed_at(timestamp, nullable)raw_payload(jsonb या text)
(provider, provider_event_id) पर एक unique constraint जोड़ें। यही आपका मुख्य डेडुप गार्डरेल है।
आपको उन बिजनेस IDs की भी ज़रूरत होगी जिनका आप उपयोग रिकॉर्ड्स को locate करने के लिए करते हैं। ये webhook event ID से अलग होते हैं।
आम उदाहरण: customer_id, invoice_id, और subscription_id। इन्हें text रखें क्योंकि प्रदाता अक्सर non-numeric IDs इस्तेमाल करते हैं।
raw payload vs parsed fields
डिबग और बाद में reprocess करने के लिए raw payload स्टोर करें। parsed fields क्वेरी और रिपोर्टिंग को आसान बनाते हैं, लेकिन केवल वही स्टोर करें जिनका आप वास्तव में उपयोग करते हैं।
सरल दृष्टिकोण:
- हमेशा
raw_payloadस्टोर करें - साथ में कुछ parsed IDs रखें जिनकी आप अक्सर क्वेरी करते हैं (customer, invoice, subscription)
- फ़िल्टरिंग के लिए normalized
event_type(text) रखें
अगर एक invoice.paid इवेंट दो बार आता है, तो आपका unique constraint दूसरी insert को ब्लॉक कर देगा। आपके पास ऑडिट के लिए raw payload रहेगा, और parsed invoice ID से आसानी से पहला अपडेट किया गया invoice रिकॉर्ड मिल जाएगा।
चरण-दर-चरण: एक सुरक्षित वेबहुक हैंडलर फ्लो
एक सुरक्षित हैंडलर जानबूझकर नीरस होता है। यह हर बार एक जैसा व्यवहार करता है, भले ही प्रोवाइडर वही इवेंट retries करे या इवेंट्स आउट ऑफ़ ऑर्डर आएं।
हर बार पालन करने के लिए 5-स्टेप फ्लो
-
सिग्नेचर वेरिफ़ाई करें और payload पार्स करें। जो रिक्वेस्ट सिग्नेचर चेक फेल करती हैं, अप्रत्याशित इवेंट टाइप होती हैं, या पार्स नहीं हो पा रहीं, उन्हें reject करें।
-
बिलिंग डेटा छूने से पहले इवेंट रिकॉर्ड लिखें। provider event ID, type, created time, और raw payload (या उसका hash) सेव करें। अगर event ID पहले से मौजूद है, तो इसे duplicate मानें और रुकें।
-
इवेंट को एक single “owner” रिकॉर्ड से मैप करें। तय करें आप क्या अपडेट कर रहे हैं: invoice, subscription, या customer। अपने रिकॉर्ड्स पर external IDs स्टोर रखें ताकि आप उन्हें सीधे lookup कर सकें।
-
एक सुरक्षित state change लागू करें। केवल स्टेट को आगे बढ़ाएँ। एक late "invoice.updated" के आने पर paid हुए invoice को undo न करें। जो आप लागू करते हैं उसका रिकॉर्ड रखें (old state, new state, timestamp, event ID) ताकि audit हो सके।
-
तेज़ी से जवाब दें और outcome लॉग करें। इवेंट को सुरक्षित रूप से स्टोर कर लेने और या तो प्रोसेस या इग्नोर कर देने के बाद success लौटाएँ। लॉग करें क्या प्रोसेस हुआ, deduped हुआ, या reject हुआ, और क्यों।
AppMaster में यह अक्सर webhook events के लिए एक DB टेबल और एक Business Process बनकर आता है जो “seen event ID?” चेक करता है और फिर न्यूनतम अपडेट स्टेप्स चलाता है।
Retries, timeouts, और out of order delivery को हैंडल करना
प्रोवाइडर वेबहुक तब retries करते हैं जब उन्हें तेज़ success response नहीं मिलता। वे इवेंट्स को आउट ऑफ़ ऑर्डर भी भेज सकते हैं। आपका हैंडलर तब भी सुरक्षित रहना चाहिए जब वही अपडेट दो बार आए, या बाद वाला अपडेट पहले आ जाए।
एक व्यावहारिक नियम: तेज़ी से जवाब दें, भारी काम बाद में करें। वेबहुक रिक्वेस्ट को एक रिसीट की तरह ट्रीट करें, न कि भारी लॉजिक चलाने की जगह। यदि आप तीसरे-पक्ष APIs कॉल करते हैं, PDFs जनरेट करते हैं, या अनुरोध के भीतर अकाउंट्स फिर से कैल्कुलेट करते हैं, तो आप टाइमआउट बढ़ाते हैं और और retries ट्रिगर करते हैं।
Out of order: सबसे नया सच रखें
आउट-ऑफ-ऑर्डर डिलीवरी सामान्य है। कोई भी बदलाव लागू करने से पहले दो चेक करें:
- टाइमस्टैम्प की तुलना करें: केवल वही इवेंट लागू करें जो उस ऑब्जेक्ट (invoice, subscription, entitlement) के लिए आपने पहले स्टोर किए गए से नया हो।
- जब टाइमस्टैम्प क्लोज़ या अस्पष्ट हों तो स्थिति प्रायोरिटी का उपयोग करें: paid, open के ऊपर है; canceled active से ऊपर हो सकता है; refunded का प्राथमिकता paid पर हो।
अगर आपने पहले ही किसी invoice को paid रिकॉर्ड किया है और एक लेट "open" इवेंट आता है, तो उसे इग्नोर करें। अगर आपने "canceled" रिकॉर्ड किया और बाद में कोई पुराना "active" अपडेट आता है, तो canceled रखें।
Ignore बनाम queue
जब आप साबित कर सकें कि इवेंट stale है या पहले से लागू है (same event ID, older timestamp, lower status priority) तो इग्नोर करें। जब इवेंट ऐसे डेटा पर निर्भर हो जो अभी मौजूद नहीं है (जैसे subscription अपडेट उस से पहले आना जब customer रिकॉर्ड मौजूद नहीं है) तो queue करें।
एक व्यावहारिक पैटर्न:
- इवेंट को तुरंत processing state के साथ स्टोर करें (received, processing, done, failed)
- अगर dependencies गायब हैं, तो उसे waiting के रूप में मार्क करें और बैकग्राउंड में retry करें
- retry लिमिट सेट करें और बार-बार विफलताओं के बाद अलर्ट करें
AppMaster में, यह एक webhook events तालिका और एक Business Process के लिए अच्छा फिट है जो रिक्वेस्ट को जल्दी acknowledge करता है और queued इवेंट्स को असिंक्रोनस तरीके से प्रोसेस करता है।
सुरक्षित रूप से invoices, subscriptions, और entitlements अपडेट करना
डेडुप्लिकेशन संभालने के बाद अगला जोखिम split billing state है: invoice कहता है paid, पर subscription अभी भी past due है, या एक्सेस दो बार दिया गया और कभी revoke नहीं हुआ। हर वेबहुक को एक state transition के रूप में ट्रीट करें और उसे एक atomic अपडेट में लागू करें।
Invoices: status changes को monotonic बनाएं
Invoices paid, voided, और refunded जैसी स्टेट्स से गुजरते हैं। आप partial payments भी देख सकते हैं। किसी भी इवेंट के आधार पर invoice को बदलते वक्त उसे toggle न करें। current status और key totals (amount_paid, amount_refunded) स्टोर करें और केवल आगे-सुरक्षित transitions की अनुमति दें।
व्यावहारिक नियम:
- एक invoice को केवल पहली बार paid के रूप में मार्क करें जब आप paid इवेंट पहली बार देखें।
- refunds के लिए,
amount_refundedको invoice कुल तक बढ़ाएँ; कभी घटाएँ नहीं। - अगर कोई invoice voided है, तो fulfillment actions बंद कर दें, पर रिकॉर्ड audit के लिए रखें।
- partial payments के लिए, amounts अपडेट करें पर “fully paid” लाभ न दें जब तक वास्तविक पूर्ण भुगतान न हो।
Subscriptions और entitlements: एक बार देना, एक बार हटाना
Subscriptions में renewals, cancellations, और grace periods आते हैं। subscription status और अवधि की सीमाएँ (current_period_start/end) रखें, और उन डेटा से entitlement windows निकालें। Entitlements स्पष्ट रिकॉर्ड होने चाहिए, न कि सिर्फ़ एक boolean।
एक्सेस कंट्रोल के लिए:
- प्रति user प्रति product प्रति period एक entitlement grant
- जब एक्सेस ख़त्म हो (cancellation, refund, chargeback) तो एक revocation रिकॉर्ड
- एक audit trail जो रिकॉर्ड करे कि किस webhook इवेंट ने हर बदलाव किया
विभाजन स्थिति से बचने के लिए एक ट्रांज़ैक्शन का उपयोग करें
Invoice, subscription, और entitlement अपडेट एक ही डेटाबेस ट्रांज़ैक्शन में लागू करें। वर्तमान rows पढ़ें, चेक करें कि क्या यह इवेंट पहले से लागू है, फिर सभी चेंजेज़ एक साथ लिखें। अगर कुछ फेल होता है, तो roll back करें ताकि आप “paid invoice पर कोई एक्सेस नहीं” या उल्टा स्थिति में न आएँ।
AppMaster में, यह अक्सर एक Business Process फ्लो के रूप में मॉडल होता है जो PostgreSQL को एक नियंत्रित पथ में अपडेट करता है और बिजनेस चेंज के साथ एक audit entry लिखता है।
वेबहुक endpoints के लिए सुरक्षा और डेटा सुरक्षा चेक
वेबहुक सुरक्षा correctness का हिस्सा है। अगर कोई attacker आपके endpoint को हिट कर सकता है, तो वह नकली “paid” स्टेट्स बनाने की कोशिश कर सकता है। भले ही डेडुप्लिकेशन हो, आपको इवेंट असली है यह साबित करना और ग्राहक डेटा सुरक्षित रखना ज़रूरी है।
बिलिंग डेटा छूने से पहले भेजने वाले की सत्यता जाँचें
हर रिक्वेस्ट पर सिग्नेचर वेरिफ़ाई करें। Stripe के लिए यह आम तौर पर Stripe-Signature हेडर चेक करने, raw request body (न कि फिर से बनायी गई JSON) का उपयोग करने, और पुराने timestamp वाले इवेंट्स को reject करने को शामिल करता है। मिसिंग हेडर्स को हार्ड फ़ेल मानें।
शुरू में बेसिक्स वैरिफ़ाई करें: सही HTTP method, Content-Type, और required fields (event id, type, और वह object id जिसका आप invoice या subscription ढूँढने में उपयोग करेंगे)। अगर आप इसे AppMaster में बना रहे हैं, तो signing secret को environment variables या secure config में रखें, कभी DB या client code में न रखें।
एक त्वरित सुरक्षा चेकलिस्ट:
- वैध सिग्नेचर और ताज़ा timestamp के बिना रिक्वेस्ट reject करें
- अपेक्षित हेडर्स और content type की माँग करें
- webhook handler के लिए least-privilege DB access का उपयोग करें
- secrets को टेबल्स के बाहर स्टोर करें (env/config), और आवश्यकतानुसार rotate करें
- केवल तब 2xx लौटाएँ जब आपने इवेंट सुरक्षित रूप से persist कर लिया हो
संवेदनशील जानकारी लीक किए बिना लॉग्स उपयोगी रखें
Retries और विवादों को डिबग करने के लिए पर्याप्त लॉग रखें, पर संवेदनशील मान न रखें। PII का एक सुरक्षित subset स्टोर करें: provider customer ID, internal user ID, और शायद एक masked email (जैसे a***@domain.com)। कभी भी पूरा कार्ड डेटा, पूरा पता, या raw authorization headers स्टोर न करें।
लॉग रखें जो आपको घटना reconstruct करने में मदद करे:
- Provider event id, type, created time
- Verification result (signature ok/failed) बिना signature स्टोर किए
- Dedupe निर्णय (नया बनाम पहले से प्रोसेस किया हुआ)
- प्रभावित internal record IDs (invoice/subscription/entitlement)
- Error reason और retry count (अगर आप retries queue करते हैं)
बुनियादी abuse protection जोड़ें: IP द्वारा rate limit करें और (जब संभव हो) customer ID के आधार पर भी rate limit पर विचार करें, और अगर आपकी सेटअप अनुमति देती है तो केवल ज्ञात provider IP ranges की अनुमति देना सोचें।
डबल चार्ज या डबल एक्सेस का कारण बनने वाली आम गलतियाँ
ज़्यादातर बिलिंग बग्स गणित की वजह से नहीं होते। वे तब होते हैं जब आप वेबहुक डिलीवरी को एक एकल, विश्वसनीय संदेश की तरह मान लेते हैं।
डुप्लिकेट अपडेट्स का सबसे आम कारण बनने वाली गलतियाँ:
- टाइमस्टैम्प या राशि से डेडुप करना बजाय event ID के। अलग इवेंट्स में वही राशि हो सकती है, और retries मिनट बाद आ सकते हैं। प्रदाता के unique event ID का उपयोग करें।
- सिग्नेचर वेरिफ़ाई करने से पहले DB अपडेट करना। पहले वेरिफ़ाई करें, फिर पार्स करें, फिर एक्ट करें।
- हर इवेंट को सत्य के स्रोत के रूप में मानना बिना current state चेक किए। अगर invoice पहले से paid, refunded, या void है तो उसे अंधाधुंध paid न मार्क करें।
- एक ही खरीद के लिए कई entitlements बनाना। Retries duplicate rows बना सकते हैं। एक upsert अपनाएँ जैसे “ensure entitlement exists for subscription_id”, फिर dates/limits अपडेट करें।
- वेबहुक fail कर जाना क्योंकि notification service डाउन है। ईमेल, SMS, Slack, या Telegram बिलिंग को ब्लॉक नहीं करना चाहिए। नोटिफ़िकेशंस को queue करें और core billing changes सुरक्षित रूप से स्टोर होने के बाद success लौटाएँ।
एक साधारण उदाहरण: एक renewal इवेंट दो बार आता है। पहली डिलीवरी entitlement row बनाती है। retry दूसरी row बना देता है, और आपकी ऐप “दो active entitlements” देखकर अतिरिक्त सीट्स या क्रेडिट दे देती है।
AppMaster में, फिक्स अधिकतर फ्लो से जुड़ा होता है: पहले verify करें, unique constraint के साथ event रिकॉर्ड insert करें, state checks के साथ billing updates लागू करें, और side effects (इमेल, रसीदें) को async स्टेप्स में डालें ताकि वे retry storm न ट्रिगर कर सकें।
वास्तविक उदाहरण: duplicate renewal + बाद में refund
यह पैटर्न डरावना लग सकता है, पर अगर आपका हैंडलर दोबारा चलाने के लिए सुरक्षित है तो यह संभाला जा सकता है।
एक ग्राहक मासिक प्लान पर है। Stripe एक renewal इवेंट भेजता है (उदाहरण के लिए, invoice.paid)। आपका सर्वर इसे प्राप्त करता है, डेटाबेस अपडेट करता है, पर 200 लौटाने में बहुत समय लेता है (cold start, व्यस्त DB)। Stripe इसे फेल मानकर वही इवेंट retry कर देता है।
पहली डिलीवरी पर आप एक्सेस दे देते हैं। retry पर आप पहचान लेते हैं कि यह वही इवेंट है और कुछ नहीं करते। बाद में, एक refund इवेंट आता है (उदाहरण के लिए, charge.refunded) और आप एक बार एक्सेस revoke कर देते हैं।
डेटाबेस में स्टेट मॉडल करने का एक सरल तरीका (AppMaster Data Designer में बनाने योग्य तालिकाएँ):
webhook_events(event_id UNIQUE, type, processed_at, status)invoices(invoice_id UNIQUE, subscription_id, status, paid_at, refunded_at)entitlements(customer_id, product, active, valid_until, source_invoice_id)
हर इवेंट के बाद डेटाबेस कैसा दिखना चाहिए
Event A (renewal, पहली डिलीवरी) के बाद: webhook_events में event_id=evt_123 के लिए एक नई row status=processed के साथ। invoices को paid मार्क किया गया। entitlements.active=true और valid_until एक बिलिंग पीरियड आगे चला गया।
Event A फिर से (renewal, retry) के बाद: webhook_events में insert फेल हो जाता है (unique event_id) या आपका हैंडलर देखता है कि यह पहले से प्रोसेस किया जा चुका है। invoices या entitlements में कोई परिवर्तन नहीं होता।
Event B (refund) के बाद: webhook_events में event_id=evt_456 के लिए नई row। invoices.refunded_at सेट होता है और status=refunded। entitlements.active=false (या valid_until अब) सेट किया जाता है, और source_invoice_id का उपयोग करके सही एक्सेस एक बार revoke किया जाता है।
महत्वपूर्ण बात यह है कि dedupe चेक किसी भी grant या revoke लिखने से पहले होता है।
प्री-लॉन्च त्वरित चेकलिस्ट
लाइव वेबहुक चालू करने से पहले, आप यह प्रमाणित करना चाहेंगे कि एक वास्तविक वर्ल्ड इवेंट बिलिंग रिकॉर्ड्स को ठीक एक बार अपडेट करता है, भले ही प्रोवाइडर उसे दो बार (या दस बार) भेज दे।
इस चेकलिस्ट का उपयोग अपने सेटअप को end-to-end validate करने के लिए करें:
- सुनिश्चित करें कि हर आने वाला इवेंट पहले सेव किया जाता है (raw payload, event id, type, created time, और signature verification result), भले ही बाद के स्टेप फेल हों।
- सत्यापित करें कि duplicates जल्दी पहचाने जाते हैं (same provider event id) और हैंडलर बिना invoices, subscriptions, या entitlements बदले निकल जाता है।
- साबित करें कि बिजनेस अपडेट एक-बार ही है: एक invoice status change, एक subscription state change, एक entitlement grant या revoke।
- सुनिश्चित करें कि फेल्यर्स इतने डिटेल के साथ रिकॉर्ड हों कि उन्हें सुरक्षित रूप से replay किया जा सके (error message, failed step, retry status)।
- टेस्ट करें कि आपका हैंडलर तेज़ी से रिस्पॉन्ड करता है: स्टोर करने के बाद रिसीट स्वीकार करें, और रिक्वेस्ट के अंदर धीमा काम करने से बचें।
आपको बड़ी observability सेटअप की ज़रूरत नहीं है, पर संकेत चाहिए। लॉग्स या सरल डैशबोर्ड से ये ट्रैक करें:
- duplicate deliveries में spike (आम है, पर बड़े उछाल timeouts या प्रोवाइडर समस्याओं का संकेत दे सकते हैं)
- event type द्वारा उच्च error rate (उदाहरण: invoice payment failed)
- retries में फँसे इवेंट्स का बढ़ता backlog
- mismatch checks (paid invoice पर missing entitlement, revoked subscription पर अभी भी active access)
- processing time में अचानक वृद्धि
अगर आप AppMaster में बना रहे हैं, तो इवेंट संग्रहण को Data Designer में एक समर्पित टेबल में रखें और “mark processed” को अपने Business Process में एक सिंगल, एटॉमिक निर्णय बिंदु बनाएं।
आगे के कदम: टेस्ट करें, मॉनिटर करें, और इसे नो-कोड बैकएंड में बनाएं
टेस्टिंग वह जगह है जहाँ idempotency खुद को साबित करती है। केवल happy path न चलाएँ। एक ही इवेंट को कई बार replay करें, इवेंट्स को आउट ऑफ़ ऑर्डर भेजें, और timeouts फोर्स करें ताकि आपका प्रोवाइडर retries करे। दूसरी, तीसरी, और दसवीं डिलीवरी कुछ भी नहीं बदलनी चाहिए।
बैकफ़िलिंग के लिए जल्दी से योजना बनाएं। किसी बग फिक्स, स्कीमा बदलाव, या प्रोवाइडर घटना के बाद आपको पुराने इवेंट्स को फिर से प्रोसेस करना पड़ सकता है। अगर आपका हैंडलर सचमुच idempotent है, तो backfilling का अर्थ होता है “उसी पाइपलाइन के माध्यम से इवेंट्स को replay करना” बिना duplicates बनाए।
सपोर्ट के लिए एक छोटा रनबुक रखें ताकि समस्याएँ अनुमान पर न टिकी रहें:
- इवेंट ID खोजें और जाँचें कि क्या वह processed के रूप में रिकॉर्ड है
- invoice या subscription रिकॉर्ड देखें और अपेक्षित स्थिति और टाइमस्टैम्प की पुष्टि करें
- entitlement रिकॉर्ड देखें (कब और क्यों एक्सेस दिया गया)
- जरूरत पड़े तो सुरक्षित reprocess मोड में सिर्फ़ उसी single event ID के लिए प्रोसेसिंग फिर से चलाएँ
- अगर डेटा inconsistent है, तो एक corrective action लागू करें और उसे रिकॉर्ड करें
अगर आप बिना बहुत boilerplate लिखे यह लागू करना चाहते हैं, तो AppMaster (appmaster.io) आपको core टेबल्स मॉडल करने और वेबहुक फ्लो को एक विज़ुअल Business Process में बनाने देता है, जबकि बैकएंड के लिए असली स्रोत कोड भी जनरेट करता है।
पूरा वेबहुक हैंडलर end-to-end एक नो-कोड generated बैकएंड में बनाकर पहले ही सत्यापित कर लें कि retries के दौरान यह सुरक्षित रहता है, तब ही आप ट्रैफ़िक और रेवन्यू बड़े पैमाने पर ले जाएँ।
सामान्य प्रश्न
डुप्लिकेट वेबहुक डिलीवरियाँ सामान्य हैं क्योंकि प्रदाता कम से कम एक बार डिलीवरी के पक्ष में ऑप्टिमाइज़ करते हैं। अगर आपका endpoint टाइमआउट हो जाता है, 5xx लौटाता है, या कनेक्शन थोड़ी देर के लिए टूट जाता है, तो प्रदाता वही इवेंट फिर से भेज देगा जब तक उसे सफल प्रतिक्रिया नहीं मिलती।
प्रदाता के यूनिक event ID का उपयोग करें (वेबहुक इवेंट आइडेंटिफायर), न कि राशि, टाइमस्टैम्प, या ग्राहक ईमेल। उस event ID को unique constraint के साथ स्टोर करें ताकि retry तुरंत पता चल सके और सुरक्षित रूप से नज़रअंदाज़ किया जा सके।
इवेंट रिकॉर्ड को पहले डालें, उसके बाद ही invoices, subscriptions, या entitlements अपडेट करें। अगर insert उसीलिए फेल होता है क्योंकि वही event ID पहले से मौजूद है, तो प्रोसेसिंग रोकें और success लौटाएँ ताकि retries डबल अपडेट न करें।
देर से retries और जांच के लिए काफ़ी समय तक रखें। व्यावहारिक डिफ़ॉल्ट 30–90 दिन है; अगर आप विवाद, चार्जबैक, या लंबे सब्सक्रिप्शन चक्र से निपटते हैं तो इसे 6–12 महीने तक बढ़ाएँ। पुराने रिकॉर्ड्स को समय-समय पर purge करें ताकि क्वेरी तेज़ रहें।
बिलिंग डेटा छूने से पहले सिग्नेचर वेरिफ़ाई करें, फिर पार्स और आवश्यक फ़ील्ड वैलिडेट करें। अगर सिग्नेचर वेरिफ़िकेशन फेल हो जाए तो रिक्वेस्ट reject करें और बिलिंग चेंज न लिखें — क्योंकि डेडुपिंग नकली “paid” इवेंट से आपको बचाएगा नहीं।
इवेंट को सुरक्षित रूप से स्टोर करने के तुरंत बाद स्वीकार कर लें, और भारी काम बैकग्राउंड में शेड्यूल करें। धीमे हैंडलर अधिक टाइमआउट बढ़ाते हैं, जिससे retries बढ़ती हैं और डुप्लीकेट अपडेट का जोखिम बढ़ता है।
सिर्फ़ वही बदलाव लागू करें जो स्टेट को आगे बढ़ाए — स्टेल इवेंट्स को इग्नोर करें। उपलब्ध होने पर इवेंट टाइमस्टैम्प का उपयोग करें और एक सरल स्थिति प्रायोरिटी लागू करें (उदाहরণ: refunded को paid पर overwrite न होने दें, canceled को active पर)।
हर इवेंट पर नई entitlement रो बनाकर न दें। एक upsert-स्टाइल नियम अपनाएँ जैसे “एक entitlement प्रति user/product/period (या प्रति subscription) सुनिश्चित करें”, फिर dates/limits अपडेट करें और किस event ID ने परिवर्तन किया यह audit के लिए रिकॉर्ड करें।
Invoice, subscription, और entitlement चेंजेज़ को एक ही DB ट्रांज़ैक्शन में लिखें ताकि वे साथ में सफल हों या असफल हों। इससे split states जैसे “invoice paid पर access नहीं” या “access revoked पर refund रिकॉर्ड नहीं” से बचाव होता है।
हाँ। अच्छा फ़िट है: एक WebhookEvents मॉडल बनाइए जिसमें unique event ID हो, फिर एक Business Process बनाइए जो “पहले देखा गया?” चेक करे और जल्दी बाहर निकल जाए। invoices/subscriptions/entitlements को Data Designer में स्पष्ट रूप से मॉडल करें ताकि retries और replays duplicate rows न बनाएँ।


