अपने ग्राहकों के लिए कैप्चा API कुंजियाँ कैसे जारी करें

अगर आप CapSkip दूसरों के लिए चलाते हैं, तो ग्राहक के भुगतान करते ही, सीधे अपने बिलिंग webhook से कैप्चा API कुंजियाँ जारी की जा सकती हैं। Remote Key Management ऐसे POST एंडपॉइंट देता है जो चल रहे इंस्टेंस पर कुंजियाँ जोड़ते, दिखाते, मिटाते, सीमित करते और मापते हैं, और नई कुंजी एकदम अगली हल करने वाली रिक्वेस्ट पर ही चालू हो जाती है। न रीस्टार्ट, न कोई मैनुअल कदम, और भुगतान पूरा होने तथा ग्राहक के हल करना शुरू करने के बीच अब कुछ भी नहीं बचता। हर कुंजी हर कैप्चा प्रकार के लिए अपनी अलग क्रेडिट सीमा भी रख सकती है, जिससे प्लान का स्तर आपकी बिलिंग व्यवस्था को निभाने वाला नियम न रहकर एक ऐसा आँकड़ा बन जाता है जिसे सॉल्वर खुद लागू करता है। इस पोस्ट में सदस्यता लेते समय कुंजी देना, ये सीमाएँ तय करना और बढ़ाना, हर ग्राहक की खपत मापना, रद्द करते समय वापस लेना, और वह एक नेटवर्क गलती जो यह सेटअप बहुत आसान बना देता है, सब शामिल है।
आपको क्या चाहिए
- आपके अपने नियंत्रण वाली एक Windows मशीन पर चलता हुआ CapSkip। यही मशीन आपकी हल करने वाली सेवा है, और यही अकेली मशीन है जिस पर ऐप्लिकेशन इंस्टॉल होना ज़रूरी है। आपके ग्राहक कुछ भी इंस्टॉल नहीं करते।
- और Server मोड, ताकि वे ग्राहक असल में इस तक पहुँच सकें। Local मोड लूपबैक पते पर सुनता है और सिर्फ़ उसी डिवाइस को सेवा देता है, जबकि Server मोड आपके नेटवर्क या पब्लिक IP पर सुनता है, ताकि दूसरी मशीनें जुड़ सकें। दोनों मोड यहाँ बताए गए हैं: कनेक्शन सेटिंग्स. और पता किसी को देने से पहले एक स्टैटिक पब्लिक IP रखना बेहतर है।
- एडमिन token बनाने के लिए CapSkip विंडो पर एक बार जाना। इंटरफ़ेस में होने वाला यही अकेला कदम है, और इसे दोबारा दोहराना नहीं पड़ता।
- इन कॉल को चलाने के लिए कोई जगह: आपका बिलिंग webhook हैंडलर, आपके डैशबोर्ड का बैकएंड, या जब तक आप जाँच रहे हैं तब तक एक टर्मिनल।
कोड से पहले एक बात साफ़ कह देना ठीक रहेगा, क्योंकि यही वह चीज़ है जिस पर यह पूरा मॉडल टिका है। जो कुंजियाँ आप बाँट रहे हैं, वे आपकी अपनी बनाई हुई हैं। ये किसी विक्रेता से खरीदे हुए क्रेडिट नहीं हैं जिन्हें आप आगे बेच रहे हों, और एक कुंजी बनाने में आपका कुछ भी खर्च नहीं होता। CapSkip आपके अपने हार्डवेयर पर चलता है, इसलिए सौ ग्राहक कुंजियों की लागत ठीक उतनी ही है जितनी एक की।
कुंजी अब जो चीज़ अतिरिक्त रूप से रख सकती है, वह है उसकी अपनी क्रेडिट सीमा, और वह भी हर कैप्चा प्रकार के लिए अलग से तय की हुई। इससे वह महज़ एक लेबल न रहकर ऐसा प्लान स्तर बन जाती है जिसे सॉल्वर खुद लागू करता है: "स्टार्टर, 10,000 reCAPTCHA हल" अब कोई ऐसी बात नहीं रह जाती जिस पर आपको नज़र रखनी पड़े, बल्कि एक आँकड़ा बन जाती है जिसे CapSkip खुद घटाता जाता है। बिलिंग की आठ बकेट हैं, और इन्हें जानबूझकर सॉल्वर के टास्क प्रकारों से ज़्यादा मोटा रखा गया है:
- ImageToText बकेट में इमेज और OCR हल आते हैं।
- Recaptcha बकेट में v2, v3 और दोनों Enterprise रूप एक साथ आते हैं।
- Turnstile बकेट में Cloudflare Turnstile आता है।
- GeeTest बकेट में GeeTest आता है।
- Altcha बकेट में ALTCHA आता है।
- CaptchaFox बकेट में CaptchaFox आता है।
- Capy बकेट में Capy Puzzle आता है।
- FriendlyCaptcha बकेट में Friendly Captcha आता है।
जिस बकेट पर कोई सीमा तय नहीं है वह असीमित है, और यह सुविधा आने से पहले हर कुंजी का व्यवहार ठीक यही था। इसलिए CapSkip अपग्रेड करने भर से कुछ नहीं बदलता: आपने पहले जो भी कुंजी जारी की है वह आठों बकेट में तब तक असीमित रहती है जब तक आप कोई सीमा तय न करें, और सीमाओं का कोई ज़िक्र न करने वाली जोड़ने की कॉल अब भी असीमित कुंजी ही बनाती है।
चरण 1: Remote Key Management चालू करें
यह स्विच Settings में है, API access के नीचे, फिर Remote Key Management। इसे चालू कीजिए और Generate बटन दबाइए।
token सिर्फ़ एक बार दिखता है और सिर्फ़ साल्टेड हैश के रूप में सहेजा जाता है, इसलिए बाद में उसे कहीं से पढ़ा नहीं जा सकता। खो जाए तो नया बना लीजिए, पुराना उसी क्षण बेकार हो जाता है। नया token जारी किए बिना admin API बंद करना हो तो Revoke दबाइए, जो सहेजा गया token मिटा देता है। इसे वहीं रखिए जहाँ आपका बिलिंग सिस्टम अपने बाकी गोपनीय मान रखता है, अपने ऐप्लिकेशन कोड में नहीं।
ये एंडपॉइंट उसी होस्ट और उसी पोर्ट पर रहते हैं जो पहले से आपकी हल करने वाली रिक्वेस्ट का जवाब देता है। अगर आपके ग्राहक पोर्ट 8080 से बात करते हैं तो एडमिन API भी वहीं है। यह सुविधाजनक है और यही वह चीज़ भी है जिससे सावधान रहना पड़ता है; नीचे इसके लिए अलग खंड है।
जब तक यह सुविधा बंद है, हर एडमिन पथ बिना बॉडी वाला सादा 404 लौटाता है। अनजान पथ को भी ठीक यही जवाब मिलता है, और यह जानबूझकर ऐसा रखा गया है: पोर्ट टटोलने वाला यह नहीं बता सकता कि यह सुविधा बंद किए हुए इंस्टेंस है या ऐसा इंस्टेंस जिसने इस सुविधा का नाम भी नहीं सुना।
चरण 2: ग्राहक के सदस्यता लेते ही कुंजी बनाइए
हर एंडपॉइंट POST है, JSON बॉडी लेता है, और एक Authorization हेडर माँगता है जिसमें token, Bearer क्रेडेंशियल के रूप में हो। जोड़ने वाली कॉल को एक नाम चाहिए। सिर्फ़ नाम भेजिए और कुंजी का मान CapSkip को बनाने दीजिए, यहाँ यही सही है: ग्राहक को अपना क्रेडेंशियल खुद नहीं चुनना चाहिए। साथ में एक grants ऑब्जेक्ट भी भेज दीजिए, तो कुंजी बनते ही उस ग्राहक की प्लान सीमाओं के साथ आती है।
# No install needed. Name the key after the customer, not after the plan.
curl -X POST http://YOUR_SERVER:8080/admin/keys/add \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"cust_10482","grants":{"Recaptcha":10000,"ImageToText":50000}}'{
"errorId": 0,
"key": {
"name": "cust_10482",
"key": "a1b2c3d4e5f6...",
"grants": {
"ImageToText": 50000,
"Recaptcha": 10000,
"Turnstile": null,
"GeeTest": null, "Altcha": null, "CaptchaFox": null, "Capy": null, "FriendlyCaptcha": null
}
}
}जवाब सिर्फ़ उन दो बकेट का नहीं, बल्कि आठों का ब्योरा देता है, इसलिए आपने क्या बनाया यह पक्का करने के लिए दोबारा पढ़ने की ज़रूरत कभी नहीं पड़ती। कुंजी का नाम अपने ही सिस्टम की किसी स्थिर चीज़ पर रखिए, जैसे ग्राहक ID या सदस्यता ID। नाम अद्वितीय होते हैं और यह नियम जोड़ते समय ही लागू होता है, इसलिए नाम आगे के हर काम के लिए एक भरोसेमंद पकड़ बन जाता है। इसके बजाय प्लान या तारीख के नाम पर कुंजियाँ रखना ही वह गलती है जो रद्द करने वाले दिन को तकलीफ़देह बना देती है।
grants ऑब्जेक्ट पर चार नियम लागू होते हैं, और असल में बचाता आपको आखिरी वाला ही है:
- इसे बिल्कुल न भेजिए तो कुंजी हर जगह असीमित रहती है। यही पहले से चला आ रहा व्यवहार है, इसलिए कोटा आने से पहले लिखे गए इंटीग्रेशन बिना किसी बदलाव के चलते रहते हैं।
- कोई एक बकेट छोड़ दीजिए, या उसे JSON null भेज दीजिए, तो वह असीमित हो जाती है। शून्य भेजिए तो उल्टा होता है: वह बकेट शुरुआत से ही खत्म मानी जाती है।
- बकेट के नाम में छोटे-बड़े अक्षर से फ़र्क नहीं पड़ता, पर credits शून्य या उससे बड़ा, बिना उद्धरण वाला JSON पूर्णांक ही होना चाहिए, न उद्धरण में लिखी कोई स्ट्रिंग और न कोई दशमलव संख्या।
- पहले पूरी रिक्वेस्ट जाँची जाती है, और कुंजी उसके बाद ही बनती है। अनजान बकेट नाम या खराब संख्या पर 400 लौटता है और कुछ भी नहीं बनता, इसलिए ठुकराई गई रिक्वेस्ट पीछे आधी-अधूरी सीमा वाली कुंजी छोड़ ही नहीं सकती।
सीमाएँ कुंजी बनाते समय ही तय कीजिए, न कि पहले कुंजी बनाकर बाद में उस पर पाबंदी लगाइए। दूसरे तरीके में एक अंतराल बच जाता है, चाहे कितना ही छोटा क्यों न हो, जिसमें एक बिल्कुल नई ग्राहक कुंजी बिना किसी सीमा के चालू रहती है।
यही अद्वितीयता का नियम आपकी दोहराव-सुरक्षा भी है। भुगतान सेवाएँ webhook दोबारा भेजती हैं, और दोबारा आया सदस्यता इवेंट उसी ग्राहक के लिए चुपचाप दूसरी कुंजी बनाने के बजाय 409 और ERROR_KEY_EXISTS लौटाता है। अपने हैंडलर में इस 409 को सफलता मानिए, दोहराव की समस्या खत्म।
मान ठीक एक बार, उसी जवाब में लौटता है। उसे ग्राहक को दिखाइए या वहाँ सहेजिए जहाँ से आपका डैशबोर्ड पढ़ता है, क्योंकि बाद में कुंजियाँ सूचीबद्ध करना एक एडमिन काम है और किसी एक ग्राहक का क्रेडेंशियल वापस पाने के लिए उसे चलाना आप नहीं चाहेंगे।
चरण 3: अपग्रेड या नवीनीकरण पर सीमाएँ बदलें
grant एंडपॉइंट एक कुंजी पर एक बकेट की सीमा लिखता है। कुंजी को नाम से या मान से चुनिए, दोनों में से ठीक एक, बिल्कुल वैसे ही जैसे मिटाने में होता है।
# Upgrade: replace the limit outright.
curl -X POST http://YOUR_SERVER:8080/admin/keys/grant \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"cust_10482","bucket":"Recaptcha","credits":25000,"mode":"set"}'
# Top-up: add to whatever is left.
# -d '{"name":"cust_10482","bucket":"Recaptcha","credits":10000,"mode":"add"}'दोनों मोड एक जैसे नहीं हैं। set मोड संख्या को सीधे बदल देता है, और set मोड में credits को JSON null भेजना या बिल्कुल न भेजना सीमा को हटाकर वापस असीमित कर देता है। एक बार सीमा लग जाने के बाद उसे हटाने का यही अकेला तरीका है। ध्यान देने लायक बात यह है कि credits का मान शून्य होना null होने का ठीक उल्टा अर्थ रखता है: वह सीमा हटाता नहीं, बल्कि बकेट को खत्म कर देता है। add मोड मौजूदा मान में आपकी संख्या जोड़ता है और असीमित को शून्य मानता है, इसलिए वह ऐसी बकेट पर भी सीमा बना देता है जिस पर पहले कोई सीमा नहीं थी। इसके लिए credits का शून्य से बड़ा होना ज़रूरी है।
तीन तरह की रिक्वेस्ट मानी नहीं जातीं, सीधे ठुकरा दी जाती हैं, और हर एक इसलिए क्योंकि वरना गड़बड़ी चुपचाप हो जाती:
- शून्य जोड़ना। लक्ष्य में से मौजूदा घटाकर हिसाब लगाने वाली रात की कोई क्रेडिट-भरने वाली स्क्रिप्ट, हर उस ग्राहक के लिए शून्य निकालेगी जो पहले ही लक्ष्य पर है। किसी असीमित बकेट पर यह ठीक शून्य की सीमा बना देगा और उस कुंजी का हल करना ही बंद करा देगा, इसलिए जब आपका मतलब सचमुच शून्य हो तब set मोड इस्तेमाल कीजिए।
- ऋणात्मक संख्याएँ, दोनों ही मोड में। ऋण 500 जोड़ना किसी सीमित बकेट पर कटौती जैसा लगता है, पर वही रिक्वेस्ट किसी असीमित बकेट पर जाए तो शून्य पर आकर रुक जाती है और उसे चुपचाप खत्म हुई बकेट बना देती है। कॉल करने वाला अपनी ही रिक्वेस्ट देखकर यह नहीं बता सकता कि उसे कौन-सा नतीजा मिलेगा, इसलिए दोनों में से किसी की भी इजाज़त नहीं है।
- उद्धरण में लिखी संख्याएँ। credits का मान उद्धरण वाली स्ट्रिंग के रूप में भेजने का मतलब है "इस कुंजी को उतनी संख्या पर सीमित कर दो", पर अगर उसे गायब मान समझ लिया जाए तो अर्थ बिल्कुल उल्टा, यानी सीमा हटाना हो जाता है, और ऊपर से सफलता भी लौटती है। संख्याओं को उद्धरण में डालना ठीक वही चीज़ है जो कोई शेल स्क्रिप्ट अनजाने में कर बैठती है, इसलिए credits बिना उद्धरण के भेजिए।
एक बात की योजना पहले से बना लीजिए: grant एक क्रेडिट बकाया है, महीने का कोई कोटा नहीं। CapSkip के भीतर कुछ भी कैलेंडर के हिसाब से नहीं चलता। खपत के हिसाब से चलने वाले मासिक प्लान के लिए, हर बिलिंग चक्र की शुरुआत में अपने नवीनीकरण webhook से grant एंडपॉइंट को set मोड में उस प्लान के कोटे के साथ चलाइए। जिस प्लान में बची हुई क्रेडिट आगे बढ़ती है, उसके लिए इसके बजाय उस चक्र के कोटे के साथ add मोड इस्तेमाल कीजिए।
चरण 4: हर ग्राहक की खपत मापें
usage एंडपॉइंट एक खाली ऑब्जेक्ट लेता है और सभी कुंजियों के लिए, हर कुंजी और हर बकेट के हिसाब से खपत एक साथ लौटाता है।
# Every key, every bucket, in one call.
curl -X POST http://YOUR_SERVER:8080/admin/keys/usage \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" -d '{}'{
"errorId": 0,
"keys": [
{
"name": "cust_10482",
"key": "a1b2c3d4e5f6...",
"buckets": [
{
"bucket": "Recaptcha",
"granted": 10000,
"remaining": 9581,
"used": 419,
"failed": 12,
"inFlight": 3,
"lastUsed": "2026-08-26T09:14:02.1234567Z"
}
]
}
]
}| फ़ील्ड | अर्थ |
|---|---|
| "granted" | सीमा, या असीमित होने पर null। |
| "remaining" | बची हुई क्रेडिट, या बकेट असीमित होने पर null। |
| "used" | सफल हल। क्रेडिट सिर्फ़ यही खर्च करते हैं। |
| "failed" | असफल कोशिशें: जाँच-पड़ताल के लिए गिनी जाती हैं, पर इनका शुल्क कभी नहीं लगता। |
| "inFlight" | इस समय आरक्षित टास्क। बची हुई क्रेडिट में से ये पहले ही घटाए जा चुके हैं, और असफल होने पर वापस लौट आते हैं। |
| "lastUsed" | आखिरी सफल हल का ISO-8601 UTC समय, या कुंजी कभी इस्तेमाल ही न हुई हो तो null। |
ये गिनतियाँ कुंजी के पूरे जीवनकाल तक जुड़ती रहती हैं और रीस्टार्ट के बाद भी बनी रहती हैं। चूँकि एक ही कॉल सारी कुंजियाँ लौटा देती है, इसलिए हर ग्राहक के लिए अलग-अलग कॉल करने के बजाय इसे नियमित अंतराल पर चलाकर अपने डैशबोर्ड को खिलाइए। lastUsed फ़ील्ड ही वह तरीका भी है जिससे आप ऐसी निष्क्रिय कुंजियाँ ढूँढ़ते हैं जिन्हें वापस लेना बेहतर है।
चरण 5: रद्द करने पर कुंजी वापस लीजिए
मिटाने के लिए ठीक एक ही पहचान चाहिए, या तो कुंजी का मान या फिर नाम। चूँकि आपने कुंजियों के नाम ग्राहकों पर रखे हैं, इसलिए नाम ही सही विकल्प है:
# By name, which is unambiguous because names are unique.
curl -X POST http://YOUR_SERVER:8080/admin/keys/delete \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" -d '{"name":"cust_10482"}'
# By value works too, if that is what your records hold:
# -d '{"key":"the-old-value"}'वापसी अगली हल करने वाली रिक्वेस्ट पर लागू होती है, इसलिए कॉल लौटते ही पहुँच खत्म हो जाती है। उसके बाद से उस ग्राहक की कॉल इस त्रुटि के साथ विफल होंगी: ERROR_KEY_DOES_NOT_EXIST। यह संकेत काफ़ी साफ़ है, उनका इंटीग्रेशन इसे किसी खराबी के बजाय समाप्त हो चुकी सदस्यता के रूप में दिखा सकता है।
यह कब चलाना है, सोच-समझकर तय कीजिए। रद्द होने वाले इवेंट पर मिटाने से पहुँच तुरंत कट जाती है, जबकि ग्राहक ने आमतौर पर अवधि के अंत तक का भुगतान किया होता है। अवधि समाप्त होने वाले इवेंट पर मिटाना ही वह है जो ज़्यादातर लोग असल में चाहते हैं। भुगतान विफल होना तीसरी स्थिति है: वापसी से पहले थोड़ी रियायत अवधि देना, कार्ड दोबारा चार्ज होते समय गायब हो जाने वाली कुंजी के मुकाबले कहीं कम शिकायतें पैदा करता है।
चरण 6: सूची को अपने बिलिंग सिस्टम से मिलाइए
सूची वाली कॉल एक खाली ऑब्जेक्ट लेती है और वह हर कुंजी लौटाती है जिसे इंस्टेंस इस समय स्वीकार करता है।
# The audit: exactly who can solve right now.
curl -X POST http://YOUR_SERVER:8080/admin/keys/list \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" -d '{}'{
"errorId": 0,
"keys": [
{ "name": "cust_10482", "key": "..." },
{ "name": "cust_10515", "key": "..." }
]
}errorId का 0 होना सफलता है, यही परिपाटी हल करने वाले एंडपॉइंट भी इस्तेमाल करते हैं। इसे नियमित अंतराल पर चलाइए और नामों की तुलना अपनी सक्रिय सदस्यताओं से कीजिए। जिस कुंजी की कोई सदस्यता नहीं है, उसका मतलब है कोई अब भी मुफ़्त में हल करा रहा है; और जिस सदस्यता की कोई कुंजी नहीं है, उसका मतलब है ऐसा ग्राहक जिसका कुंजी जारी करने वाला webhook गिर गया और जो शायद अभी शिकायत दर्ज करने वाला है। दोनों तब तक चुप रहते हैं जब तक आप देखें नहीं, और सच सिर्फ़ इसी कॉल में रहता है।
सीमा पूरी हो जाने पर आपके ग्राहक क्या देखते हैं
जिस कुंजी की किसी बकेट में कोई क्रेडिट नहीं बची, उसे हल करने वाले एंडपॉइंट से ERROR_ZERO_BALANCE मिलता है, और सिर्फ़ उसी बकेट के टास्क के लिए। उसी कुंजी की बाकी सारी बकेट चलती रहती हैं। यह त्रुटि इस एडमिन API से नहीं, बल्कि हल करने वाले API से आती है, और 2captcha, Anti-Captcha, CapSolver तथा DeathByCaptcha तीनों की अनुकरण परतों में एक जैसी ही आती है। यानी ग्राहक ने 2captcha का बैलेंस खत्म होने के लिए जो व्यवस्था पहले से लिख रखी है, वही इसे भी सँभाल लेती है, उनकी तरफ़ कोई बदलाव किए बिना।
बैलेंस जाँचने का व्यवहार भी समझदारी भरा है। getBalance या res.php की बैलेंस क्रिया चलाने वाले ग्राहक को सभी सीमित बकेट को जोड़कर बनी असली बची हुई क्रेडिट दिखती है, यानी इस्तेमाल के साथ आँकड़ा घटता जाता है। जिस कुंजी पर कहीं भी सीमा नहीं है वह पुराना 999999.99 वाला संकेतक मान ही लौटाती रहती है, जिसका मतलब है कि भेजने से पहले बैलेंस की जाँच करने वाले क्लाइंट बिल्कुल पहले की तरह काम करते रहते हैं।
एक अंधा कोना जानने लायक है। भेजे जाने वाले प्रारूप में सिर्फ़ एक ही संख्या की जगह है, इसलिए जिस कुंजी पर 50,000 इमेज क्रेडिट हों और reCAPTCHA क्रेडिट शून्य हो, वह भी बैलेंस स्वस्थ ही बताती है। जिस भी चीज़ को प्रकार के हिसाब से ब्योरा चाहिए, उसे इसके बजाय usage एंडपॉइंट पढ़ना होगा, और वह सिर्फ़ एडमिन के लिए है।
इसे सदस्यता webhook में जोड़ना
सब मिलाकर पूरा इंटीग्रेशन एक प्लान तालिका और चार हैंडलर भर है:
# pip install requests
import requests
BASE = "http://YOUR_SERVER:8080"
AUTH = {"Authorization": "Bearer YOUR_TOKEN"}
BUCKETS = ("ImageToText", "Recaptcha", "Turnstile", "GeeTest", "Altcha", "CaptchaFox", "Capy", "FriendlyCaptcha")
# None means unlimited; an omitted bucket is unlimited too.
PLANS = {
"starter": {"ImageToText": 50_000, "Recaptcha": 10_000},
"pro": {"ImageToText": 500_000, "Recaptcha": 100_000,
"Turnstile": 25_000},
"unlimited": {},
}
def admin(path, body):
r = requests.post(f"{BASE}/admin/keys/{path}", json=body,
headers=AUTH, timeout=10)
if r.status_code == 409:
return None # already exists: a retried webhook
r.raise_for_status()
return r.json()
def on_subscription_active(customer_id, plan):
body = {"name": f"cust_{customer_id}"}
if PLANS[plan]:
body["grants"] = PLANS[plan]
created = admin("add", body)
return created["key"]["key"] if created else None
def apply_plan(customer_id, plan):
"""Upgrade, downgrade, or start-of-cycle reset."""
limits = PLANS[plan]
for bucket in BUCKETS:
admin("grant", {"name": f"cust_{customer_id}", "bucket": bucket,
"mode": "set", "credits": limits.get(bucket)})
def on_subscription_ended(customer_id):
admin("delete", {"name": f"cust_{customer_id}"})
def usage_report():
return admin("usage", {})["keys"]ध्यान दीजिए कि apply_plan सिर्फ़ बदली हुई बकेट को ठीक करने के बजाय हर बकेट में लिखता है। यह जानबूझकर है: डाउनग्रेड पर यह उन सीमाओं को हटा देता है जो पुराने प्लान में थीं और नए में नहीं हैं, क्योंकि set मोड में credits का गायब होना असीमित का मतलब रखता है। आगे बढ़ने वाले प्लान के लिए इसे add मोड में बदल दीजिए और उन बकेट को छोड़ दीजिए जिनकी सीमा None है, क्योंकि add मोड गायब या शून्य मान ठुकरा देता है।
दोहराए गए सदस्यता इवेंट से आने वाले None को ध्यान से संभालिए। इसका मतलब है कि कुंजी मौजूद है पर उसका मान अब आपको दिखाई नहीं देगा, इसलिए अगर आपने पहली बार उसे सहेजा नहीं था, तो उपाय सूची देखना नहीं बल्कि मिटाकर दोबारा बनाना है। पहली बार मिलते ही मान सहेज लेने से यह स्थिति आती ही नहीं।
एडमिन API को ग्राहकों वाले पोर्ट पर मत छोड़िए
इस सेटअप के लिए यही सबसे अहम खंड है, क्योंकि एक ही होस्ट और एक ही पोर्ट की सुविधा दोनों तरफ़ काटती है। आपके ग्राहकों को हल करने वाले एंडपॉइंट तक पहुँचना ही है। एडमिन एंडपॉइंट उसी पोर्ट पर हैं, उनकी सुरक्षा सिर्फ़ Bearer token से है, और कोई ट्रांसपोर्ट जाँच है ही नहीं, इसलिए किसी अलग पोर्ट पर यह token सादे HTTP में जाता है। जो कोई भी उस ट्रैफ़िक को देख सकता है वह उसे पढ़ लेगा, और जिसके हाथ वह लग गया वह अपने लिए कुंजी बना लेगा।
इंस्टेंस के आगे एक रिवर्स प्रॉक्सी लगाइए और दोनों तरह के लोगों को अलग कीजिए:
- इंटरनेट पर सिर्फ़ हल करने वाले पथ प्रकाशित कीजिए, प्रॉक्सी पर समाप्त होने वाले HTTPS के ज़रिए, और एडमिन पथ के नीचे की हर चीज़ के लिए 404 लौटाइए।
- एडमिन एंडपॉइंट तक अपने ही बैकएंड से किसी निजी रास्ते से पहुँचिए: अगर बिलिंग सेवा उसी Windows मशीन पर चलती है तो लूपबैक पता, वरना कोई निजी नेटवर्क, कोई VPN, या कोई SSH टनल।
मोटा नियम यह है कि एडमिन token को कभी आपके ढाँचे से बाहर नहीं जाना चाहिए, और वह जिस पोर्ट से बात करता है वह कभी ऐसा पोर्ट नहीं होना चाहिए जहाँ ग्राहक पहुँच सके। सुविधा चालू हो या बंद, एडमिन पथ को कभी खुले इंटरनेट पर मत लाइए।
एक और सीमा जानने लायक है, क्योंकि यहाँ सबसे पहले दिमाग में ग्राहक डैशबोर्ड बनाने का ही खयाल आता है। ब्राउज़र इन एंडपॉइंट को बुला ही नहीं सकते, क्योंकि एडमिन प्रतिक्रियाएँ डिज़ाइन के हिसाब से कोई CORS हेडर नहीं भेजतीं। आपके डैशबोर्ड को आपके ही बैकएंड से होकर जाना होगा, और token की सही जगह भी वही है।
कुंजियाँ असल में लिखी कहाँ जाती हैं
कुंजियाँ वहीं जाती हैं जहाँ आपकी कुंजियाँ पहले से रहती हैं। Direct Input और From File अलग-अलग दो सूचियाँ हैं, और API उसी को पढ़ता और लिखता है जो इस समय चुनी हुई है। सेटिंग्स विंडो में स्रोत बदलने से एंडपॉइंट को दिखने वाली चीज़ भी बदल जाती है, इसलिए अभी-अभी जारी की कुंजी सूची में क्यों नहीं है, यह सोचने से पहले पुष्टि कर लीजिए कि आप किस मोड में हैं।
फ़ाइल मोड का एक व्यवहार पहले से जान लेना ठीक रहेगा: पहली ही लिखाई सादे टेक्स्ट वाली कुंजी फ़ाइल को JSON के रूप में दोबारा लिख देती है। कुछ खोता नहीं, लेकिन प्रारूप हमेशा के लिए बदल जाता है, इसलिए अगर उस फ़ाइल को कोई और चीज़ भी पढ़ती है तो पहले बैकअप ले लीजिए। और अगर कोई फ़ाइल चुनी ही नहीं गई है तो लिखाई कहीं टिक ही नहीं सकती, और आपको 500 के साथ ERROR_ADMIN_STORE_FAILURE मिलेगा। यहाँ 500 का मतलब लगभग हमेशा यही होता है।
इस सुविधा से पहले बनी सूचियों में अब भी दोहराए हुए नाम हो सकते हैं, क्योंकि अद्वितीयता सिर्फ़ जोड़ते समय जाँची जाती है। ऐसे में नाम से मिटाने पर ERROR_KEY_NAME_AMBIGUOUS मिलता है और कुछ भी नहीं बदलता। इसके बजाय मान से मिटाइए, दुविधा खत्म हो जाती है।
Windows शेल में उद्धरण चिह्न, अगर आप उसी मशीन से जाँच रहे हैं
CapSkip एक Windows ऐप्लिकेशन है, इसलिए जिस मशीन से आप जाँच रहे हैं वह भी अक्सर Windows ही होती है। कमांड प्रॉम्प्ट एकल उद्धरण को उद्धरण चिह्न मानता ही नहीं। नकल किया हुआ कमांड उद्धरण चिह्नों को JSON बॉडी का हिस्सा बनाकर भेज देता है, पार्सर उसे ठुकरा देता है, और बिल्कुल सही दिखने वाले कमांड पर आपको ERROR_ADMIN_BAD_REQUEST मिल जाता है।
| शेल | खाली body | फ़ील्ड के साथ |
|---|---|---|
| कमांड प्रॉम्प्ट | -d "{}" | -d "{\"name\":\"prod\"}" |
| PowerShell, curl.exe के साथ | -d '{}' | -d '{\"name\":\"prod\"}' |
| Git Bash, macOS, Linux | -d '{}' | -d '{"name":"prod"}' |
PowerShell में curl.exe को पूरे नाम से बुलाइए। वहाँ सादा curl दरअसल Invoke-WebRequest का उपनाम है, जो बिल्कुल अलग आर्ग्युमेंट लेता है और ऐसी गड़बड़ी दिखाएगा जिसका इस API से कोई लेना-देना नहीं।
एरर कोड
| स्थिति | कोड | कारण |
|---|---|---|
| 404 | सादा टेक्स्ट | सुविधा बंद है, या पथ अनजान है। दोनों को जानबूझकर एक जैसा रखा गया है। |
| 401 | ERROR_ADMIN_UNAUTHORIZED | token नहीं है, बिगड़ा हुआ है, या गलत है। |
| 405 | ERROR_ADMIN_METHOD_NOT_ALLOWED | आपने POST के अलावा कुछ भेजा है। |
| 400 | ERROR_ADMIN_BAD_REQUEST | बॉडी में गड़बड़ी: बिना नाम के जोड़ना, मिटाने में दोनों पहचान देना या एक भी न देना, कोई अनजान बकेट नाम, credits या grants का ऐसा मान जो बिना उद्धरण वाला ऋणेतर पूर्णांक न हो, या add मोड का ऐसा grant जिसमें शून्य से बड़ा credits न हो। कुछ भी न बनाया जाता है, न बदला जाता है। |
| 409 | ERROR_KEY_EXISTS | वह नाम या वह मान पहले से इस्तेमाल में है। सदस्यता हैंडलर में इसका मतलब आमतौर पर दोबारा भेजा गया webhook होता है। |
| 409 | ERROR_KEY_NAME_AMBIGUOUS | नाम से मिटाने या grant करने पर कई कुंजियाँ मिलीं। इसके बजाय कुंजी के मान से चुनिए। |
| 404 | ERROR_KEY_NOT_FOUND | मिटाने या grant करने पर कुछ भी नहीं मिला। आमतौर पर यह पहले ही निपटाई जा चुकी रद्दीकरण होती है। |
| 500 | ERROR_ADMIN_STORE_FAILURE | सहेजा नहीं जा सका। आमतौर पर फ़ाइल मोड में कोई फ़ाइल चुनी नहीं गई होती। |
एक और कोड यहीं का है, भले ही वह उत्पाद के दूसरे हिस्से से आता हो। ERROR_ZERO_BALANCE इस एडमिन API से नहीं, हल करने वाले एंडपॉइंट से लौटता है, और तब आता है जब टास्क जिस बकेट का है उसमें उस कुंजी की क्रेडिट खत्म हो चुकी हो। अगर ग्राहक इसकी शिकायत करे तो जवाब है उसकी क्रेडिट बढ़ा देना, न कि यह कि कुंजी जारी करने में कुछ गड़बड़ है।
ये कोड हल करने वाले एरर कोड की जगह नहीं लेते, उनके साथ ही रहते हैं, और पूरी सूची यहाँ है: CapSkip API दस्तावेज़.
FAQ
नई कुंजी असल में कितनी जल्दी काम करने लगती है?
अगली हल करने वाली रिक्वेस्ट पर। कुछ भी कैश नहीं होता और कुछ भी दोबारा लोड करने की ज़रूरत नहीं, इसलिए जिस ग्राहक को आपके चेकआउट पेज से कुंजी मिली है वह उसी मिनट उसका इस्तेमाल कर सकता है। दूसरी दिशा में वापसी भी उतनी ही तत्काल है, और इसीलिए आपके रद्दीकरण हैंडलर का समय एक नीतिगत फ़ैसला है, तकनीकी बंदिश नहीं।
हर अतिरिक्त ग्राहक कुंजी पर मुझे कुछ खर्च होता है?
अब भी कुछ नहीं। कुंजी बनाना मुफ़्त है और उसके पीछे किसी विक्रेता का कोई मीटर नहीं चलता। बदलाव यह है कि कुंजी अब आपकी अपनी क्रेडिट सीमाएँ रख सकती है, इसलिए वह एक प्लान स्तर का प्रतिनिधित्व कर सकती है: हिसाब-किताब आपका है, और कीमत तय करना भी आपका। कैप्चा हल करने वाला SDK आप उसे जो कुंजी देते हैं वही पढ़ता है, इसलिए बँटवारा कैसे करना है यह पूरी तरह आपका डिज़ाइन है।
क्या मैं इस API से किसी ग्राहक पर दर सीमा लगा सकता हूँ?
नहीं, और क्रेडिट सीमाएँ उसका विकल्प नहीं हैं। कोटा एक बजट है, प्रति सेकंड अनुरोधों की छत नहीं: वह तय करता है कि ग्राहक को कुल कितना मिलेगा, यह नहीं कि वह किसी एक सेकंड में एंडपॉइंट पर कितना ज़ोर डाल सकता है। दर सीमा अब भी उसी रिवर्स प्रॉक्सी का काम है जिसे आप वैसे भी इंस्टेंस के आगे लगा रहे हैं, और वह ग्राहक द्वारा भेजी गई API कुंजी के आधार पर लगती है।
क्या क्रेडिट सीमाएँ हर महीने रीसेट हो जाती हैं?
नहीं। grant एक ऐसा बकाया है जो तब तक घटता रहता है जब तक आप उसे बदल न दें, और CapSkip के भीतर कुछ भी कैलेंडर के हिसाब से नहीं चलता। हर बिलिंग चक्र की शुरुआत में grant एंडपॉइंट को set मोड में चलाकर उसे प्लान के कोटे पर लौटा दीजिए, या फिर add मोड इस्तेमाल कीजिए अगर बची हुई क्रेडिट आगे बढ़नी चाहिए।
क्या असफल हल ग्राहक की क्रेडिट खर्च करते हैं?
नहीं। क्रेडिट सिर्फ़ सफल हल पर खर्च होती है। असफलताएँ उपयोग रिपोर्ट में अलग से गिनी जाती हैं, इसलिए वे आपको फिर भी दिखती हैं, और जो टास्क प्रक्रिया के बीच में असफल हो जाए वह अपना आरक्षण लौटा देता है। यानी जिस ग्राहक को कोई मुश्किल साइट बार-बार चुनौती दे रही है, वह ऐसी कोशिशों पर चुपचाप अपनी सीमा नहीं फूँकता जिनसे कुछ मिला ही नहीं।
CapSkip अपग्रेड करने पर मेरी मौजूदा कुंजियों का क्या होगा?
कुछ नहीं। आपने पहले जो भी कुंजी जारी की है वह आठों बकेट में तब तक असीमित है जब तक आप उस पर कोई सीमा न लगाएँ, और grants का ज़िक्र न करने वाली जोड़ने की कॉल पहले की तरह ही असीमित कुंजियाँ बनाती रहती है। जब तक संचालक खुद इसे अपनाकर इस्तेमाल न करे, यह सुविधा निष्क्रिय ही रहती है, इसलिए न कोई माइग्रेशन कदम है और न ही ऐसा कोई अंतराल जिसमें ग्राहकों की पहुँच चली जाए।
क्या ग्राहक अपनी बची हुई क्रेडिट देख सकते हैं?
बैलेंस जाँच के ज़रिए, हाँ। सीमाएँ तय हो जाने के बाद वह पुराने संकेतक मान की जगह उनकी असली बची हुई कुल क्रेडिट बताती है, इसलिए काम करने के साथ आँकड़ा घटता जाता है। बकेट के हिसाब से ब्योरा अलग बात है: उसके लिए एडमिन token चाहिए और वह एक बार में सारी कुंजियाँ लौटाता है, इसलिए उस एंडपॉइंट को ग्राहकों के सामने खोलने के बजाय अपने ही डैशबोर्ड से दिखाइए।
मेरा एडमिन token खो गया है। अब क्या करूँ?
उसी सेटिंग्स पैनल में जाकर नया बना लीजिए। पुराना token तुरंत काम करना बंद कर देता है, इसलिए कोई सफ़ाई का चरण नहीं है और ऐसा कोई अंतराल भी नहीं जिसमें दोनों चलें। आपके ग्राहकों पर इसका असर नहीं पड़ता: उनकी कुंजियाँ अछूती रहती हैं और वे हल कराते रहते हैं। सिर्फ़ आपकी अपनी कुंजी जारी करने की प्रक्रिया रुकती है, जब तक आप अपने webhook हैंडलर द्वारा पढ़े जाने वाले गोपनीय मान को अपडेट न कर दें।
सबसे छोटा जवाब
Remote Key Management एक बार चालू कीजिए और token को अपने बाकी बिलिंग गोपनीय मानों के साथ रखिए। ग्राहक की सदस्यता सक्रिय होने पर उसके नाम वाली एक कुंजी जोड़िए, उसी समय उसके प्लान के grants भी उस पर लगा दीजिए, और दोहराव पर आने वाले 409 को सफलता मानिए। इन सीमाओं को रीसेट करना या बढ़ाना अपने नवीनीकरण webhook से कीजिए, क्योंकि यहाँ कुछ भी कैलेंडर के हिसाब से नहीं चलता। usage एंडपॉइंट को नियमित अंतराल पर चलाइए; वह बिल मिलान के लिए भी काम आता है और निष्क्रिय कुंजियाँ ढूँढ़ने के लिए भी। सदस्यता खत्म होने पर नाम से मिटा दीजिए, और कुंजी सूची को अपने सक्रिय सदस्यों से मिलाइए, क्योंकि असली जवाब सिर्फ़ वहीं रहता है। फिर आगे एक प्रॉक्सी लगाइए और एडमिन पथ को उस पोर्ट से दूर रखिए जो आपके ग्राहक देख सकते हैं। इतना कर लीजिए, तो कैप्चा बायपास ऐसी चीज़ बन जाती है जिसे आप अपने ही हार्डवेयर पर आगे बेच सकते हैं, अपने ही आँकड़ों से नापते हुए, और जारी करने की रफ़्तार उतनी ही होगी जितनी तेज़ी से आपका चेकआउट webhook चला देता है।
