कस्टम पैरामीटर्स के साथ ऐप रूटिंग के लिए QR कोड कैसे बनाएं

opoinstall
2026-10-29
5 min read

QR कोड बनाते समय कस्टम पैरामीटर्स को कैसे लिंक करें? ऐप रूटिंग के लिए कस्टम पैरामीटर्स को लिंक करने में होस्ट किए गए लैंडिंग-पेज लिंक में संरचित URL क्वेरी कुंजियों को जोड़ना, वेब इंटीग्रेशन लेयर के माध्यम से उन कुंजियों को कैप्चर करना, और सीधे ऐप वेकअप या इंस्टॉल के बाद लॉन्च होने पर नेटिव नेविगेशन कंट्रोलर्स को वैidated पेलोड भेजना शामिल है।

QR कोड के माध्यम से इन-ऐप रूटिंग एक क्लाइंट-सर्वर नेविगेशन आर्किटेक्चर है जो संरचित गंतव्य पैरामीटर्स को टू-डाइमेंशनल बारकोड URL में एम्बेड करता है। जब एप्लिकेशन इंस्टॉल होता है, तो सीधे ऑपरेटिंग-सिस्टम डीप लिंक्स के माध्यम से इन पैरामीटर्स को हल करके, या जब एप्लिकेशन ऐप स्टोर से नया इंस्टॉल किया जाता है, तो प्लेटफ़ॉर्म-समर्थित डिफ़र्ड रिकवरी के माध्यम से, इंजीनियरिंग टीमें उपयोगकर्ताओं को डिफ़ॉल्ट होम व्यू के बजाय सीधे विशिष्ट इन-ऐप स्क्रीन पर रूट कर सकती हैं।

शब्द परिभाषा संबंधित एंटिटी सर्च इंटेंट रोल
ऐप के लिए QR कोड बनाएं (Create QR Code for App) मोबाइल नेविगेशन के लिए 2D बारकोड में कार्रवाई योग्य रूटिंग URL को एन्कोड करने का इंजीनियरिंग वर्कफ़्लो। सीन रिस्टोरेशन सूचनात्मक / तकनीकी
ऐप QR कोड (app qr code) नेटिव एप्लिकेशन लॉन्च या स्टोर ऑनबोर्डिंग को ट्रिगर करने के लिए कॉन्फ़िगर किया गया एक बारकोड एसेट। यूनिवर्सल लिंक्स / ऐप लिंक्स तकनीकी / व्यावसायिक
डायनेमिक QR कोड (dynamic qr code) एक मध्यवर्ती रूटिंग एंडपॉइंट की ओर इशारा करने वाला बारकोड जिसका गंतव्य पेलोड सर्वर-साइड अपडेट किया जा सकता है। URI इंडायरेक्शन व्यावसायिक
QR कोड ट्रैकिंग (qr code tracking) रूटिंग इनग्रेस अनुरोधों और डाउनस्ट्रीम इन-ऐप स्क्रीन एक्टिवेशन का मापन। चैनल सांख्यिकी और ट्रैकिंग व्यावसायिक

आर्किटेक्चर ओवरव्यू: ऐप सीन रिस्टोरेशन के लिए QR कोड कैसे बनाएं

पैरामीटराइज्ड QR रूटिंग ऐप के अंदर प्रोडक्ट डिटेल स्क्रीन को रिस्टोर कर सकती है।

होम स्क्रीन ड्रॉप-ऑफ़ समस्या: जेनेरिक स्टोर रीडायरेक्ट उच्च-इरादे वाली यूजर जर्नी को क्यों बाधित करते हैं

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

एप्लिकेशन लॉन्च होने पर, उपयोगकर्ता को डिफ़ॉल्ट टॉप-लेवल होम स्क्रीन पर छोड़ दिया जाता है। उपयोगकर्ता को जटिल श्रेणी पेड़ों को मैन्युअल रूप से नेविगेट करने, खोज क्वेरी फिर से दर्ज करने, या रूम आइडेंटिफ़ायर टाइप करने के लिए मजबूर करना पर्याप्त संज्ञानात्मक और यांत्रिक घर्षण पैदा करता है। इन-ऐप सीन रिस्टोरेशन QR कोड URL में गंतव्य स्थिति को एन्कोड करके और नेटिव क्लाइंट के अंदर उस लक्षित दृश्य को प्रोग्रामेटिक रूप से पुनर्निर्माण करके इस ड्रॉप-ऑफ़ को संबोधित करता है।

डुअल-स्टेट रूटिंग मैकेनिक्स: डायरेक्ट वेकअप बनाम डिफ़र्ड सीन रिस्टोरेशन

इंस्टॉल किए गए ऐप्स डायरेक्ट लिंक का उपयोग करते हैं जबकि नए इंस्टॉल को समर्थित संदर्भ पुनर्प्राप्ति की आवश्यकता होती है।

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

  • डायरेक्ट वेकअप रूटिंग (एप्लिकेशन पहले से इंस्टॉल है): जब लक्षित एप्लिकेशन डिवाइस पर पहले से मौजूद होता है, तो QR कोड स्कैन करने से ऑपरेटिंग-सिस्टम डीप लिंक ट्रिगर होता है—विशेष रूप से iOS पर एप्पल यूनिवर्सल लिंक्स या Android पर एंड्रॉइड ऐप लिंक्स। दोनों तंत्रों को क्रिप्टोग्राफ़िक रूप से सत्यापित डोमेन एसोसिएशन फ़ाइलों को होस्ट करने की आवश्यकता होती है: iOS को Xcode Associated Domains हक़दार के साथ एक apple-app-site-association (AASA) फ़ाइल की आवश्यकता होती है, जबकि Android को एप्लिकेशन के हस्ताक्षर प्रमाणपत्र SHA-256 फ़िंगरप्रिंट से मेल खाने वाली और सत्यापित मैनिफ़ेस्ट इंटेंट फ़िल्टर वाली /.well-known/assetlinks.json डिजिटल एसेट लिंक्स फ़ाइल की आवश्यकता होती है। सत्यापित होने पर, OS एप्लिकेशन को जगाता है और URI को सीधे क्लाइंट के लाइफसाइकिल डेलीगेट को पास करता है।
  • डिफ़र्ड सीन रिस्टोरेशन (एप्लिकेशन इंस्टॉल नहीं है): जब एप्लिकेशन अनुपस्थित होता है, तो उपयोगकर्ता को एक मध्यवर्ती वेब लैंडिंग पेज के माध्यम से प्लेटफ़ॉर्म ऐप स्टोर पर रूट किया जाता है। चूँकि ऑपरेटिंग-सिस्टम इंटेंट प्राप्त करने के लिए नेटिव बाइनरी अभी तक इंस्टॉल नहीं है, इसलिए रूटिंग पैरामीटर्स को स्टोर रीडायरेक्ट से पहले स्टेज किया जाना चाहिए और इंस्टॉलेशन पूरा होने के बाद एप्लिकेशन के शुरुआती लॉन्च (डे 0 कोल्ड स्टार्ट) के दौरान पुनर्प्राप्त किया जाना चाहिए।

प्लेटफ़ॉर्म-विशिष्ट रिकवरी सीमाएं: Google Play इंस्टॉल रेफ़रर, iOS यूनिवर्सल लिंक्स, और SDK एडेप्टर

उपयोगकर्ता द्वारा ऐप स्टोर से एप्लिकेशन इंस्टॉल करने के बाद, रेफ़रल और रूटिंग पैरामीटर पुनर्प्राप्ति प्लेटफ़ॉर्म और एप्लिकेशन या एट्रिब्यूशन SDK द्वारा कार्यान्वित तंत्र पर निर्भर करती है। वेब URL के माध्यम से route या id पैरामीटर पास करने से अपने आप यह गारंटी नहीं मिलती है कि पैरामीटर ऐप-स्टोर इंस्टॉलेशन से बच जाएगा:

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

iOS एट्रिब्यूशन और गोपनीयता सीमा: एप्पल के एंटी-फ़िंगरप्रिंटिंग नियमों का अनुपालन

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

जब एप्लिकेशन इंस्टॉल हो जाता है, तो सत्यापित डायरेक्ट ऐप रूटिंग के लिए यूनिवर्सल लिंक्स का उपयोग करें। iOS पर पोस्ट-इंस्टॉल सीन रिस्टोरेशन के लिए, एक प्रलेखित तंत्र का उपयोग करें जिसके व्यवहार की समीक्षा तैनात SDK संस्करण और लागू एप्पल आवश्यकताओं के लिए की गई है। जहां लागू हो, SDK के गोपनीयता खुलासे और आवश्यक-कारण API घोषणाओं की समीक्षा करें, लेकिन प्राइवेसी मैनिफ़ेस्ट (PrivacyInfo.xcprivacy) की उपस्थिति को इस बात के प्रमाण के रूप में न लें कि एट्रिब्यूशन कार्यान्वयन अनुरूप है।

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

डुअल-स्टेट QR सीन रिस्टोरेशन पाइपलाइन की कल्पना करना

नीचे दिया गया आर्किटेक्चर आरेख निम्नलिखित चरणों को चित्रित करता है क्योंकि एक पैरामीटराइज्ड QR कोड क्लाइंट-साइड रूट वैलिडेटर और प्रमाणीकरण गेट पर अभिसरण करने से पहले सीधे OS वेकअप या डिफ़र्ड स्टोर रिकवरी में शाखाएं बनाता है:

[User Scans QR Code] ──> URL (?route=product_detail&id=sku_8842&channelCode=pos_01)
          │
     ┌────┴────────────────────────────────────────┐
     ▼ (App Already Installed)                     ▼ (App Not Installed)
[OS Universal Link / App Link]               [Intermediary H5 Landing Page]
     │                                             │
     ▼                                             ▼
[Hot/Cold Wakeup Callback]                   [Store Download & First Launch]
(onNewIntent / continueUserActivity)         (Platform-Supported Deferred Recovery)
     │                                             │
     └────────────────────┬────────────────────────┘
                          ▼
           [Client Route Validator & Auth Gate]
                          │
     ┌────────────────────┴────────────────────────┐
     ▼ (Allowlisted & Authenticated)               ▼ (Invalid Route / Unmatched)
[Navigate to Product Detail Screen]          [Fallback to Default Home Screen]

चरण 1: कस्टम रूटिंग पैरामीटर स्कीमा डिज़ाइन करना

कार्रवाई योग्य रूटिंग पेलोड बनाम पैसिव एट्रिब्यूशन टैग की संरचना करना

ऐप रूटिंग के लिए QR कोड बनाते समय, इंजीनियरिंग टीमों को एक स्वच्छ टू-टियर डेटा अनुबंध स्थापित करना चाहिए जो बाहरी कॉम्पैक्ट URL क्वेरी कुंजियों को आंतरिक सामान्यीकृत JSON पेलोड से अलग करता है:

अनुबंध टियर फ़ील्ड नामकरण परंपरा उदाहरण प्रतिनिधित्व आर्किटेक्चरल उद्देश्य
बाहरी URL क्वेरी टियर route, id, channelCode ?route=product_detail&id=sku_8842&channelCode=pos_01 मुद्रित QR मैट्रिक्स को कॉम्पैक्ट रखने के लिए URI वर्ण लंबाई को कम करता है
आंतरिक सामान्यीकृत टियर route_key, target_id, channel_code, fallback_route {"route_key": "product_detail", "target_id": "sku_8842", ...} H5 और नेटिव क्लाइंट राउटर द्वारा मान्य मानकीकृत कैनोनिकल ऑब्जेक्ट
क्लाइंट सुरक्षा रजिस्ट्री PROTECTED_ROUTES (आंतरिक स्थिरांक) setOf("room_lobby", "promo_campaign") आंतरिक रूप से प्रमाणीकरण आवश्यकताओं को निर्धारित करता है; बाहरी URL से कभी भरोसा नहीं किया जाता है

QR URL पैरामीटर्स को अलग-अलग नेविगेशन और एट्रिब्यूशन संदर्भों में सामान्यीकृत किया जाता है।

इस टू-टियर मॉडल में:

  • पैसिव एट्रिब्यूशन टैग (channelCode →\to channel_code) का उपयोग क्लाइंट UI स्थिति को बदले बिना विपणन रूपांतरण मात्रा को मापने के लिए एनालिटिक्स पाइपलाइनों द्वारा किया जाता है।
  • कार्रवाई योग्य रूटिंग पैरामीटर्स (route →\to route_key और id →\to target_id) नेटिव क्लाइंट के नेविगेशन कंट्रोलर को निर्देश देते हैं कि किस स्क्रीन को माउंट करना है और किस एंटिटी को फ़ेच करना है।
  • प्रमाणीकरण नीति अलगाव: क्या किसी रूट को प्रमाणीकरण की आवश्यकता है (requires_auth) यह बाहरी QR क्वेरी पैरामीटर से स्वीकार किए जाने के बजाय क्लाइंट एप्लिकेशन की आंतरिक विश्वसनीय रूट रजिस्ट्री से कड़ाई से प्राप्त किया जाना चाहिए।

IETF RFC 3986 प्रतिशत-एनकोडिंग और पेलोड आकार की बाधाओं को लागू करना

QR गंतव्य URL में जोड़े गए सभी रूटिंग पैरामीटर्स को IETF RFC 3986 में निर्दिष्ट URI जेनेरिक सिंटैक्स का पालन करना चाहिए:

  • नेस्टेड सीमाकों से बचें: URL क्वेरी स्ट्रिंग्स में सीधे कच्चे JSON स्ट्रिंग्स (?data={"route":"product","id":123}) को एम्बेड न करें। अनएस्केप किए गए ब्रेसिज़, कोट्स और कोलन अक्सर इन-ऐप ब्राउज़र URL पार्सर को तोड़ देते हैं और बारकोड मॉड्यूल घनत्व को बढ़ाते हैं।
  • कुंजी-मूल्य युग्मों को समतल करें: बाहरी रूटिंग संदर्भ को फ्लैट, प्रतिशत-एनकोडेड क्वेरी पैरामीटर्स के रूप में दर्शाएं (https://promo.example.com/open?route=product_detail&id=sku_8842&channelCode=pos_01)।
  • सख्त वर्ण सीमा लागू करें: route और id मूल्यों को URL-सुरक्षित अल्फ़ान्यूमेरिक वर्णों, हाइफ़न और अंडरस्कोर तक सीमित करें (^[A-Za-z0-9_-]{1,64}$)।

क्लाइंट-साइड रूट अलाउलिस्ट अनुबंध को परिभाषित करना

एक बार बाहरी URL पैरामीटर्स को आंतरिक पेलोड संरचना में इनजेस्ट और सामान्यीकृत कर लिया जाता है, तो उन्हें एक सख्त स्कीमा अनुबंध के अनुरूप होना चाहिए। यदि route_key पहचानी नहीं जाती है, या यदि किसी एंटिटी-विशिष्ट रूट (product_detail, room_lobby, promo_campaign) में उसका आवश्यक target_id गायब है, तो नॉर्मलाइज़र को पेलोड को target_id: null के साथ home_default पर डाउनग्रेड करना चाहिए।

नीचे दिया गया JSON स्कीमा एक सामान्यीकृत सीन रिस्टोरेशन पेलोड के लिए कैनोनिकल संरचना को परिभाषित करता है:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "QrSceneRestorationPayload",
  "description": "Canonical, normalized routing payload. Authentication policy is derived separately from the application's trusted route registry.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schema_version",
    "route_key",
    "target_id",
    "fallback_route"
  ],
  "properties": {
    "schema_version": {
      "const": "1.0.0"
    },
    "route_key": {
      "type": "string",
      "enum": [
        "product_detail",
        "room_lobby",
        "promo_campaign",
        "home_default"
      ]
    },
    "target_id": {
      "type": ["string", "null"],
      "pattern": "^[A-Za-z0-9_-]{1,64}$"
    },
    "channel_code": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_.-]{1,64}$"
    },
    "fallback_route": {
      "const": "home_default"
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "route_key": {
            "enum": [
              "product_detail",
              "room_lobby",
              "promo_campaign"
            ]
          }
        },
        "required": ["route_key"]
      },
      "then": {
        "properties": {
          "target_id": {
            "type": "string"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "route_key": {
            "const": "home_default"
          }
        },
        "required": ["route_key"]
      },
      "then": {
        "properties": {
          "target_id": {
            "type": "null"
          }
        }
      }
    }
  ],
  "examples": [
    {
      "schema_version": "1.0.0",
      "route_key": "product_detail",
      "target_id": "sku_8842",
      "channel_code": "pos_01",
      "fallback_route": "home_default"
    }
  ]
}

चरण 2: लैंडिंग पेज पर रूटिंग संदर्भ को कैप्चर करना और अग्रेषित करना

H5 लैंडिंग पेज पर अनुमति प्राप्त रूटिंग कुंजियों को पार्स करना

जब बिना एप्लिकेशन इंस्टॉल किया हुआ उपयोगकर्ता QR कोड स्कैन करता है, तो मोबाइल ब्राउज़र होस्ट किया गया मध्यवर्ती H5 लैंडिंग पेज खोलता है। लैंडिंग-पेज स्क्रिप्ट बाहरी क्वेरी कुंजियों (route, id, channelCode) को निकालती है, अस्पष्ट डुप्लिकेट पैरामीटर्स को अस्वीकार करती है, और उन्हें चरण 1 में परिभाषित कैनोनिकल संरचना में सामान्यीकृत करती है.

नीचे दिया गया जावास्क्रिप्ट नॉर्मलाइज़र एकल-कुंजी कार्डिनलिटी,regex बाउंड और सशर्त home_default डाउनग्रेडिंग को लागू करता है जबकि डुप्लिकेट आरक्षित पैरामीटर्स को अमान्य मानता है:

const query = new URLSearchParams(window.location.search);

const routeValues = query.getAll("route");
const idValues = query.getAll("id");
const channelValues = query.getAll("channelCode");

const allowedRoutes = new Set([
  "product_detail",
  "room_lobby",
  "promo_campaign",
  "home_default"
]);

const idPattern = /^[A-Za-z0-9_-]{1,64}$/;
const channelPattern = /^[A-Za-z0-9_.-]{1,64}$/;

function normalizeQrRoutingPayload() {
  // Reject duplicate keys to defend against parameter pollution
  if (routeValues.length > 1 || idValues.length > 1 || channelValues.length > 1) {
    return {
      schema_version: "1.0.0",
      route_key: "home_default",
      target_id: null,
      fallback_route: "home_default"
    };
  }

  const rawRoute = routeValues.length === 1 ? routeValues[0] : null;
  const rawId = idValues.length === 1 ? idValues[0] : null;
  const rawChannel = channelValues.length === 1 ? channelValues[0] : null;

  let routeKey = allowedRoutes.has(rawRoute)
    ? rawRoute
    : "home_default";

  let targetId =
    rawId && idPattern.test(rawId)
      ? rawId
      : null;

  if (routeKey === "home_default" || targetId === null) {
    routeKey = "home_default";
    targetId = null;
  }

  const payload = {
    schema_version: "1.0.0",
    route_key: routeKey,
    target_id: targetId,
    fallback_route: "home_default"
  };

  if (rawChannel && channelPattern.test(rawChannel)) {
    payload.channel_code = rawChannel;
  }

  return payload;
}

const routingPayload = normalizeQrRoutingPayload();

संस्करण-सत्यापित एडेप्टर के माध्यम से वेब SDK को एकीकृत करना

लैंडिंग पेज तात्कालिक ऐप वेक-अप प्रयासों और डिफ़र्ड पैरामीटर स्टेजिंग दोनों का समन्वय करता है। एट्रिब्यूशन वेब SDK (जैसे opoinstall.js) को तैनात करने से पहले, तैनात किए जा रहे SDK संस्करण के लिए आधिकारिक दस्तावेज़ीकरण के विरुद्ध सटीक JavaScript बंडल URL, आरंभीकरण हस्ताक्षर, तत्परता कॉलबैक, वेक-अप विधि और पैरामीटर स्कीमा की पुष्टि करें।

SDK-विशिष्ट व्यवहार को एक छोटे एप्लिकेशन एडेप्टर के पीछे रखें जो सामान्यीकृत routingPayload ऑब्जेक्ट को स्वीकार करता है। वेब इंटीग्रेशन और चैनल पैरामीटर कॉन्फ़िगरेशन पर विस्तृत विनिर्देशों के लिए, OpoInstall डेवलपर दस्तावेज़ से परामर्श करें।

प्रत्यक्ष स्टोर फ़ॉलबैक लिंक पर रूटिंग पैरामीटर्स को संरक्षित करना

यदि वेब JS SDK अनुपलब्ध है या ब्राउज़र सुरक्षा नियंत्रणों द्वारा अवरुद्ध है, तो लैंडिंग पेज सीधे स्टोर रीडायरेक्ट लिंक पर वापस आ जाता है। Google Play का उपयोग करने वाले Android डिप्लॉयमेंट पर, फ़ॉलबैक लिंक Play Store referrer पैरामीटर में सामान्यीकृत रूटिंग कुंजियों को एन्कोड कर सकता है ताकि जब आधिकारिक Play Store प्रवाह के माध्यम से इंस्टॉल किया जाए तो नेटिव क्लाइंट Google Play इंस्टॉल रेफ़रर API के माध्यम से रूटिंग संदर्भ को पुनर्प्राप्त कर सके:

// Example Android Play Store fallback URL preserving normalized routing context
const referrerString = `route_key=${routingPayload.route_key}&target_id=${routingPayload.target_id || ""}&channel_code=${routingPayload.channel_code || ""}`;
const playStoreFallbackUrl = `https://play.google.com/store/apps/details?id=com.example.app&referrer=${encodeURIComponent(referrerString)}`;

चरण 3: नेटिव क्लाइंट रूट डिस्पैच और सीन रिस्टोरेशन को लागू करना

Android गतिविधि लॉन्च मोड को संभालना: singleTask को onCreate और onNewIntent दोनों की आवश्यकता क्यों होती है

एंड्रॉइड डीप-लिंक आर्किटेक्चर में, कोई गतिविधि रूटिंग इंटेंट कैसे प्राप्त करती है, यह उसके कॉन्फ़िगर किए गए लॉन्च मोड और नेविगेशन ग्राफ़ डिज़ाइन पर निर्भर करता है। डेवलपर अपनी टास्क-स्टैक आवश्यकताओं के आधार पर जेटपैक नेविगेशन, singleTop, या singleTask के साथ मानक लॉन्च मोड का उपयोग कर सकते हैं।

जब डीप-लिंक वेकअप के दौरान डुप्लिकेट इंस्टेंस को स्टैक होने से रोकने के लिए गतिविधि को singleTop या singleTask के साथ कॉन्फ़िगर किया जाता है, तो दो अलग-अलग लाइफसाइकिल पथों को संभाला जाना चाहिए:

  • कोल्ड स्टार्ट (onCreate): जब एप्लिकेशन प्रक्रिया नहीं चल रही होती है, तो QR कोड स्कैन करने से गतिविधि शुरू से लॉन्च होती है। आने वाला Intent onCreate(savedInstanceState: Bundle?) को डिलीवर किया जाता है।
  • वार्म स्टार्ट (onNewIntent): जब ऑपरेटिंग सिस्टम कॉन्फ़िगर किए गए लॉन्च मोड और टास्क-स्टैक स्थितियों के तहत मौजूदा गतिविधि इंस्टेंस का पुनरुत्योग करता है, तो आने वाला डीप-लिंक इंटेंट onNewIntent() को डिलीवर किया जाता है। singleTop के तहत, पुनरुत्योग तभी होता है जब लक्षित गतिविधि पहले से ही प्रासंगिक टास्क स्टैक के शीर्ष पर हो; अन्यथा, एक नया गतिविधि इंस्टेंस बनाया जा सकता है। onCreate() और onNewIntent() दोनों हैंडलिंग लागू करें, और एप्लिकेशन के नेविगेशन आर्किटेक्चर के लिए वास्तविक व्यवहार को सत्यापित करें।

उस उपयोगकर्ता को स्थायी रूप से अवरुद्ध किए बिना जो अपने सत्र में बाद में वैध रूप से उसी QR कोड को दोबारा स्कैन करता है, दोनों राज्यों को मज़बूती से संभालने के लिए, गतिविधि को प्रत्येक संसाधित Intent इंस्टेंस को खपत ध्वज (EXTRA_ROUTE_CONSUMED) के साथ चिह्नित करना चाहिए और ऐतिहासिक ऐप-स्विचचर लॉन्च (Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) द्वारा विशेष रूप से पुनः डिलीवर किए गए इंटेंट को अनदेखा करना चाहिए।

iOS लाइफसाइकिल प्रविष्टि बिंदुओं को संभालना: AppDelegate बनाम UISceneDelegate निरंतरता गतिविधियाँ

iOS पर, सीधे यूनिवर्सल लिंक रूटिंग और डिफ़र्ड पैरामीटर पुनर्प्राप्ति को एप्लिकेशन के कॉन्फ़िगर किए गए UI लाइफसाइकिल के साथ संरेखित करना चाहिए:

  • सीन-आधारित लाइफसाइकिल (UISceneDelegate): UIScene अपनाने वाले अनुप्रयोगों में (iOS 13 के बाद से मानक), कोल्ड-लंच यूनिवर्सल लिंक connectionOptions.userActivities के माध्यम से scene(_:willConnectTo:options:) में आते हैं, जबकि वार्म-रेज़्यूमे यूनिवर्सल लिंक scene(_:continue:) में आते हैं।
  • एप्लिकेशन-डेलीगेट लाइफसाइकिल (UIApplicationDelegate): उन अनुप्रयोगों में जो सीन डेलीगेट का उपयोग नहीं करते हैं, यूनिवर्सल लिंक application(_:continue:restorationHandler:) को डिलीवर किए जाते हैं।
  • डिफ़र्ड फ़र्स्ट-लॉन्च रिकवरी: यूनिवर्सल लिंक निरंतरता डेलीगेट से स्वतंत्र, डिफ़र्ड इंस्टॉल पैरामीटर क्वेरी रूट व्यू पदानुक्रम तैयार होने के बाद प्रारंभिक एप्लिकेशन स्टार्टअप के दौरान एक बार निष्पादित होती है।

डेवलपर प्लेटफ़ॉर्म SDK पैकेज डाउनलोड कर सकते हैं और OpoInstall SDK डाउनलोड सेंटर के माध्यम से लाइफसाइकिल इंटीग्रेशन टेम्पलेट्स का निरीक्षण कर सकते हैं।

प्रत्यक्ष वेकअप पैरामीटर्स और फ़र्स्ट-लॉन्च डिफ़र्ड पैरामीटर्स को एक सिंगल नेविगेशन राउटर में एकजुट करना

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

  1. मूल सत्यापन (प्रत्यक्ष इंटेंट): सत्यापित करें कि आने वाले Intent.ACTION_VIEW URI https स्कीमा का उपयोग करते हैं, एप्लिकेशन के सत्यापित संबंधित होस्ट (promo.example.com) और पथ (/open) से मेल खाते हैं, और इसमें कोई विरोधी डुप्लिकेट क्वेरी कुंजियाँ नहीं हैं।
  2. स्कीमा सामान्यीकरण और अलाउलिस्ट जाँच: बाहरी route / id कुंजियों या आंतरिक route_key / target_id फ़ील्ड को कैनोनिकल मॉडल में मैप करें, अनअलाउलिस्टेड रूट या विकृत आईडी को अस्वीकार करें। ध्यान दें कि क्लाइंट कोटलिन पार्सिंग लॉजिक एक वर्णनात्मक, रक्षात्मक पार्सिंग कंकाल के रूप में काम करता है; प्रोडक्शन सिस्टम में, भेजने से पहले पूर्ण कैनोनिकल स्कीमा नियमों के विरुद्ध पेलोड को पार्स करें।
  3. सिंगल-फ़्लाइट री-एंट्रेंसी गार्ड: एक परमाणु प्रति-वितरण गार्ड का उपयोग करें ताकि समवर्ती कॉलबैक ओवरलैपिंग फ़्रैगमेंट लेनदेन को ट्रिगर न कर सकें।

प्रमाणीकरण गेट्स का प्रबंधन: उपयोगकर्ता लॉगिन पूरा होने तक लंबित डीप-लिंक रूट को कतारबद्ध करना

सुरक्षित QR रूट प्रमाणीकरण के बाद फिर से शुरू हो सकते हैं जबकि अमान्य लक्ष्य सुरक्षित रूप से वापस आते हैं।

जब कोई नया उपयोगकर्ता QR कोड के माध्यम से ऐप स्टोर से एप्लिकेशन इंस्टॉल करता है और इसे पहली बार लॉन्च करता है, तो उपयोगकर्ता आमतौर पर प्रमाणीकृत नहीं होता है। यदि QR कोड किसी सुरक्षित स्क्रीन (जैसे room_lobby या promo_campaign) को लक्षित करता है और राउटर तुरंत नेविगेट करने का प्रयास करता है, तो प्रमाणीकरण गार्ड उपयोगकर्ता को लॉगिन स्क्रीन पर रीडायरेक्ट कर देगा और डीप-लिंक संदर्भ खो देगा।

प्रमाणीकरण सीमाओं में सीन रिस्टोरेशन को संरक्षित करने के लिए:

  • आंतरिक रूट नीति लुकअप: बाहरी पेलोड फ़्लैग पर भरोसा करने के बजाय क्लाइंट की आंतरिक PROTECTED_ROUTES रजिस्ट्री की जाँच करके निर्धारित करें कि क्या रूट को प्रमाणीकरण की आवश्यकता है।
  • परमाणु स्टेजिंग: यदि उपयोगकर्ता प्रमाणीकृत नहीं है, तो मान्य NavigationTarget को परमाणु, सत्र-स्कوبेड PendingRouteStore (AtomicReference) में संग्रहीत करें और उपयोगकर्ता को लॉगिन प्रवाह पर रूट करें। ध्यान दें कि इन-मेमोरी PendingRouteStore सक्रिय एप्लिकेशन प्रक्रिया पर लागू होता है; यदि प्रमाणीकरण प्रवाह प्रक्रिया मृत्यु तक फैल सकता है, तो एक एन्क्रिप्टेड, समय-बद्ध स्थानीय स्टोर का उपयोग करें जो खाता स्विच करने पर स्थिति को साफ़ करता है।
  • पोस्ट-ऑथ री-वैलिडेशन और डिस्पैच: जब लॉगिन पूरा हो जाता है, तो परमाणु रूप से स्टेज किए गए लक्ष्य (getAndSet(null)) का उपभोग करें, सत्यापित करें कि सत्र अब प्रमाणीकृत है, रूट पैरामीटर्स को फिर से सत्यापित करें, और स्क्रीन संक्रमण निष्पादित करें।

नेविगेशन होस्ट की तत्परता और स्थिति पुनर्स्थापना

onNavigationGraphReady() कॉलबैक को एप्लिकेशन के वास्तविक नेविगेशन-होस्ट आरंभीकरण से जोड़ा जाना चाहिए। जब नेविगेशन ग्राफ़ को XML-होस्टेड NavHostFragment में परिभाषित किया जाता है, तो होस्ट और उसका गंतव्य तैयार होने के बाद गतिविधि इस कनेक्शन को स्थापित कर सकती है। यदि ग्राफ़ को एसिंक्रोनस रूप से इंस्टॉल किया जाता है, तो ग्राफ़ आरंभीकरण पूरा होने के बाद ही कॉलबैक को लागू करें।

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

नीचे दिया गया कोटलिन कोड इन नियंत्रणों को प्रदर्शित करने वाले एप्लिकेशन-स्वामित्व वाले SDK और नेविगेशन एडेप्टर के साथ एक वर्णनात्मक एंड्रॉइड रूटिंग कंकाल प्रदान करता है:

<!-- Android Manifest Configuration for App Link Verification -->
<!-- File: app/src/main/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.Light.NoActionBar">

        <activity
            android:name=".SceneRestorationActivity"
            android:exported="true"
            android:launchMode="singleTask">

            <!-- Verified Android App Link Intent Filter -->
            <intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <!-- Origin must match /.well-known/assetlinks.json SHA-256 fingerprint -->
                <data
                    android:scheme="https"
                    android:host="promo.example.com"
                    android:pathPrefix="/open" />
            </intent-filter>
        </activity>
    </application>
</manifest>
// Android Routing Activity Implementation
// File: app/src/main/java/com/example/app/SceneRestorationActivity.kt
//
// INTEGRATION NOTE:
// This example is an architectural routing skeleton, not a drop-in production implementation.
// Bind AttributionRoutingAdapter and NavController initialization to your verified SDK release,
// session manager, and NavHostFragment before deployment.

package com.example.app

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.fragment.NavHostFragment
import org.json.JSONObject
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference

/**
 * Validated navigation target model representing an allowlisted in-app destination.
 * Authentication requirement is derived strictly from the internal route registry.
 */
data class NavigationTarget(
    val eventKey: String,
    val routeKey: String,
    val targetId: String?,
    val requiresAuth: Boolean
)

/**
 * Process-scoped atomic store holding a pending route when an unauthenticated user
 * triggers a protected deep link during cold start or first-launch onboarding.
 */
object PendingRouteStore {
    private val pending = AtomicReference<NavigationTarget?>(null)

    fun stage(target: NavigationTarget) {
        pending.set(target)
    }

    fun consume(): NavigationTarget? {
        return pending.getAndSet(null)
    }
}

class SceneRestorationActivity : AppCompatActivity() {

    companion object {
        private const val TAG = "SceneRestorationRouter"
        private const val TRUSTED_HOST = "promo.example.com"
        private const val TRUSTED_PATH = "/open"
        private const val EXTRA_ROUTE_CONSUMED = "com.example.app.EXTRA_ROUTE_CONSUMED"

        private const val ROUTE_HOME = "home_default"
        private const val ROUTE_PRODUCT = "product_detail"
        private const val ROUTE_LOBBY = "room_lobby"
        private const val ROUTE_PROMO = "promo_campaign"

        private val ALLOWED_ROUTES = setOf(ROUTE_HOME, ROUTE_PRODUCT, ROUTE_LOBBY, ROUTE_PROMO)
        private val PROTECTED_ROUTES = setOf(ROUTE_LOBBY, ROUTE_PROMO)
        private val SAFE_ID_REGEX = Regex("^[A-Za-z0-9_-]{1,64}$")

        // Process-level guard ensuring deferred install recovery is queried only once per cold start
        private val hasQueriedDeferredInstall = AtomicBoolean(false)
    }

    // Per-delivery re-entrancy guard; allows legitimate subsequent scans of the same QR code
    private val processingIntent = AtomicBoolean(false)

    // Staging container for routes that arrive before the UI/NavController is mounted
    private val pendingUiTarget = AtomicReference<NavigationTarget?>(null)
    private var navigationReady = false

    // Application-owned adapter isolating vendor SDK and session calls
    private val attributionAdapter = AttributionRoutingAdapter()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_scene_restoration)

        // Connect NavHost initialization when defined in layout
        connectNavigationHost()

        // Activity recreation (e.g., screen rotation) is not a new install or new QR scan
        if (savedInstanceState != null) {
            return
        }

        val launchIntent = intent
        val isDirectLinkLaunch =
            launchIntent?.action == Intent.ACTION_VIEW &&
            launchIntent.data != null

        if (isDirectLinkLaunch) {
            processIncomingIntentOnce(launchIntent) { candidate ->
                handleIncomingWakeupIntent(candidate)
            }
            // Do not start deferred recovery after a direct-link launch,
            // even if the direct route was rejected.
            return
        }

        // Only initial normal application launches may request deferred recovery
        if (hasQueriedDeferredInstall.compareAndSet(false, true)) {
            queryDeferredSceneParameters()
        }
    }

    /**
     * Connect this method to the actual NavHost initialization.
     * Uses currentDestination check and destination listener to safely verify graph readiness
     * without throwing IllegalStateException on unset controller.graph.
     */
    private fun connectNavigationHost() {
        val navHost = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as? NavHostFragment

        if (navHost == null) {
            Log.w(TAG, "NavHost not attached; route remains queued.")
            // For an asynchronously attached NavHost, call this
            // method again after the host has been attached.
            return
        }

        val controller = navHost.navController

        // currentDestination is nullable before a destination has been established.
        // Do not access controller.graph merely to check whether setGraph() has been called.
        if (controller.currentDestination != null) {
            onNavigationGraphReady()
            return
        }

        // If the graph/destination becomes available later,
        // mark navigation ready when a destination is established.
        val listener = object : androidx.navigation.NavController.OnDestinationChangedListener {
            override fun onDestinationChanged(
                navController: androidx.navigation.NavController,
                destination: androidx.navigation.NavDestination,
                arguments: Bundle?
            ) {
                navController.removeOnDestinationChangedListener(this)
                onNavigationGraphReady()
            }
        }

        controller.addOnDestinationChangedListener(listener)
    }

    /**
     * Captures warm-start QR scans when the Activity is reused under singleTop or singleTask.
     */
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
        processIncomingIntentOnce(intent) { candidate ->
            handleIncomingWakeupIntent(candidate)
        }
    }

    private fun processIncomingIntentOnce(incomingIntent: Intent?, process: (Intent) -> Unit) {
        val candidate = incomingIntent ?: return
        // Ignore historical intents re-delivered from recent-apps switcher or configuration changes
        if ((candidate.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) return
        if (candidate.getBooleanExtra(EXTRA_ROUTE_CONSUMED, false)) return

        if (!processingIntent.compareAndSet(false, true)) return
        try {
            process(candidate)
            candidate.putExtra(EXTRA_ROUTE_CONSUMED, true)
        } finally {
            processingIntent.set(false)
        }
    }

    private fun handleIncomingWakeupIntent(incomingIntent: Intent?): Boolean {
        if (incomingIntent?.action != Intent.ACTION_VIEW) return false
        val uri: Uri = incomingIntent.data ?: return false

        // 1. Verify trusted HTTPS origin and path
        val isTrusted =
            uri.scheme.equals("https", ignoreCase = true) &&
            uri.host.equals(TRUSTED_HOST, ignoreCase = true) &&
            uri.path == TRUSTED_PATH

        if (!isTrusted) {
            Log.w(TAG, "Rejected untrusted deep-link origin: $uri")
            return false
        }

        // 2. Reject missing or conflicting duplicate query parameters
        if (uri.getQueryParameters("route").size != 1 ||
            uri.getQueryParameters("id").size != 1) {
            Log.w(TAG, "Rejected missing or duplicate routing keys in URI")
            return false
        }

        val rawRoute = uri.getQueryParameter("route")
        val rawId = uri.getQueryParameter("id")

        val target = validateAndBuildTarget("wakeup", rawRoute, rawId)
        if (target.routeKey != ROUTE_HOME) {
            dispatchNavigationTarget(target)
            return true
        }
        return false
    }

    private fun queryDeferredSceneParameters() {
        // Application-defined timeout configuration; tune for deployed service behavior
        attributionAdapter.fetchDeferredInstallParams(timeoutMs = 5000L) { jsonPayload ->
            if (jsonPayload == null) {
                Log.d(TAG, "No deferred routing parameters recovered; remaining on default view.")
                return@fetchDeferredInstallParams
            }
            val target = parseJsonRoutingPayload("deferred_install", jsonPayload)
            runOnUiThread {
                dispatchNavigationTarget(target)
            }
        }
    }

    private fun parseJsonRoutingPayload(sourcePrefix: String, rawJson: String): NavigationTarget {
        return try {
            val obj = JSONObject(rawJson)
            // Defensive normalization: map canonical 'route_key'/'target_id' with external 'route'/'id' fallback
            val rawRoute = obj.optString("route_key", "").ifEmpty { obj.optString("route", "") }
            val rawId = if (obj.has("target_id") && !obj.isNull("target_id")) {
                obj.optString("target_id", "")
            } else {
                obj.optString("id", "")
            }
            validateAndBuildTarget(sourcePrefix, rawRoute, rawId)
        } catch (e: Exception) {
            Log.w(TAG, "Malformed routing JSON payload: ${e.message}")
            NavigationTarget(sourcePrefix, ROUTE_HOME, null, false)
        }
    }

    /**
     * Enforces strict allowlist and regex bounds on untrusted routing parameters.
     */
    private fun validateAndBuildTarget(
        sourcePrefix: String,
        rawRoute: String?,
        rawId: String?
    ): NavigationTarget {
        val cleanRoute = if (rawRoute != null && ALLOWED_ROUTES.contains(rawRoute)) {
            rawRoute
        } else {
            ROUTE_HOME
        }

        if (cleanRoute == ROUTE_HOME) {
            return NavigationTarget(sourcePrefix, ROUTE_HOME, null, false)
        }

        // Entity-specific routes strictly require a valid, sanitized targetId
        val cleanId = if (rawId != null && SAFE_ID_REGEX.matches(rawId)) {
            rawId
        } else {
            null
        }

        if (cleanId == null) {
            Log.w(TAG, "Route $cleanRoute rejected due to missing or invalid target_id.")
            return NavigationTarget(sourcePrefix, ROUTE_HOME, null, false)
        }

        val needsAuth = PROTECTED_ROUTES.contains(cleanRoute)
        return NavigationTarget("${sourcePrefix}_${cleanRoute}_${cleanId}", cleanRoute, cleanId, needsAuth)
    }

    /**
     * Invoked when the application's NavHost / NavController graph is attached and ready.
     */
    fun onNavigationGraphReady() {
        navigationReady = true
        flushPendingNavigation()
    }

    private fun flushPendingNavigation() {
        if (!navigationReady || isFinishing || isDestroyed) return
        val target = pendingUiTarget.getAndSet(null) ?: return
        dispatchReadyNavigationTarget(target)
    }

    private fun dispatchNavigationTarget(target: NavigationTarget) {
        if (isFinishing || isDestroyed) return

        // Stage route if the UI navigation graph has not finished inflating
        if (!navigationReady) {
            pendingUiTarget.set(target)
            return
        }

        dispatchReadyNavigationTarget(target)
    }

    private fun dispatchReadyNavigationTarget(target: NavigationTarget) {
        if (target.routeKey == ROUTE_HOME) {
            navigateToHomeScreen()
            return
        }

        // Check authentication gate for protected destinations
        if (target.requiresAuth && !attributionAdapter.isUserAuthenticated()) {
            Log.d(TAG, "Protected route ${target.routeKey} staged pending user authentication.")
            PendingRouteStore.stage(target)
            navigateToLoginScreen()
            return
        }

        executeScreenTransition(target.routeKey, target.targetId)
    }

    /**
     * Invoked by the authentication controller after login or registration completes.
     */
    fun onUserAuthenticationCompleted() {
        val pending = PendingRouteStore.consume()
        if (pending == null) {
            navigateToHomeScreen()
            return
        }

        if (isFinishing || isDestroyed) return

        if (!attributionAdapter.isUserAuthenticated()) {
            PendingRouteStore.stage(pending)
            navigateToLoginScreen()
            return
        }

        val validated = validateAndBuildTarget(
            pending.eventKey,
            pending.routeKey,
            pending.targetId
        )

        if (validated.routeKey == ROUTE_HOME) {
            navigateToHomeScreen()
            return
        }

        executeScreenTransition(validated.routeKey, validated.targetId)
    }

    private fun executeScreenTransition(routeKey: String, targetId: String?) {
        // Implementation: perform backend-authorized resource fetch and invoke NavController.navigate()
        Log.d(TAG, "Dispatching verified screen transition: route=$routeKey, id=$targetId")
    }

    private fun navigateToLoginScreen() {
        // Implementation: transition to LoginFragment / LoginActivity
    }

    private fun navigateToHomeScreen() {
        // Implementation: display default home screen
    }
}

/**
 * Application-owned adapter skeleton. Replace stubbed methods with verified OpoInstall SDK
 * callbacks, Google Play Install Referrer queries, and your production session manager.
 */
class AttributionRoutingAdapter {
    fun fetchDeferredInstallParams(timeoutMs: Long, callback: (String?) -> Unit) {
        // Bind to verified SDK install parameter API or Play Install Referrer in production
        callback(null)
    }

    fun isUserAuthenticated(): Boolean {
        // Bind to production authentication session state
        return false
    }
}

सुरक्षा और निदान: रूट इंजेक्शन और नेविगेशन ड्रॉप को रोकना

OWASP दिशानिर्देशों के अनुसार डीप-लिंक रूट इंजेक्शन और ओपन-रिडायरेक्ट शोषण से बचाव

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

  • मनमाने WebView URL लोड करने पर प्रतिबंध: आंतरिक एप्लिकेशन WebView के अंदर किसी असत्यापित बाहरी URL को लोड करने के लिए कभी भी किसी रूटिंग पैरामीटर (?route=webview&url=https://...) की अनुमति न दें। हमलावर क्रेडेंशियल्स प्राप्त करने के लिए एप्लिकेशन के मूल UI फ़्रेम का शोषण करते हुए QR कोड में फ़िशिंग URL को एन्कोड कर सकते हैं।
  • डुप्लिकेट पैरामीटर प्रदूषण को अस्वीकार करें: यदि किसी हेरफेर किए गए URL में कई विरोधी कुंजियाँ हैं (?route=home_default&route=room_lobby), तो पार्सर को मनमाना पहला या अंतिम तत्व चुनने की अनुमति देने के बजाय URI को अस्वीकार करें।
  • बैकएंड संसाधन प्राधिकरण लागू करें: target_id=sku_8842 पास करना केवल क्लाइंट को निर्देश देता है कि किस दृश्य का अनुरोध करना है। जब स्क्रीन बैकएंड API से डेटा प्राप्त करती है, तो सर्वर को स्वतंत्र रूप से यह सत्यापित करना होगा कि वर्तमान उपयोगकर्ता सत्र उस एंटिटी तक पहुँचने के लिए अधिकृत है, जिससे असुरक्षित प्रत्यक्ष वस्तु संदर्भ (IDOR) कमजोरियों को रोका जा सके।

कोल्ड-स्टार्ट बनाम वार्म-स्टार्ट नेविगेशन रेस कंडीशन का निदान करना

कोल्ड स्टार्ट के दौरान, मुख्य NavHostFragment या रूट नेविगेशन ग्राफ़ के फूले होने से पहले डीप-लिंक इंटेंट या कैश्ड SDK कॉलबैक हल हो सकते हैं। व्यू पदानुक्रम तैयार होने से पहले navController.navigate() को कॉल करने का प्रयास करने से रनटाइम अपवाद या छोड़े गए संक्रमण होते हैं। हमेशा नेविगेशन निष्पादन को तब तक स्थगित रखें जब तक कि होस्ट व्यू पदानुक्रम संलग्न और सक्रिय न हो जाए (!isFinishing && !isDestroyed)।

समाप्त हो चुके प्रचारों, हटाई गई SKUs और गायब पैरामीटर्स को सुचारू रूप से संभालना

भौतिक QR कोड अक्सर डिजिटल प्रचार समाप्त होने या उत्पाद SKU के सेवानिवृत्त होने के बाद खुदरा स्टोर या प्रिंट मीडिया में तैनात रहते हैं। जब लक्षित स्क्रीन target_id के लिए डेटा प्राप्त करती है और बैकएंड से HTTP 404 Not Found या 410 Gone प्रतिक्रिया प्राप्त करती है, तो क्लाइंट को त्रुटि को पकड़ना चाहिए, उपयोगकर्ता को एक स्पष्ट सूचनात्मक नोटिस प्रदर्शित करना चाहिए, और साफ-सुथरे तरीके से home_default पर नेविगेट करना चाहिए।

डुअल-स्टेट QR रूटिंग के लिए प्री-डिप्लॉयमेंट सत्यापन चेकलिस्ट

भौतिक QR संपत्तियों को जारी करने से पहले, इंजीनियरिंग टीमों को भौतिक उपकरणों में निम्नलिखित सात-बिंदु सत्यापन प्रोटोकॉल को निष्पादित करना चाहिए:

  • चरण 1: इंस्टॉल किया गया कोल्ड-स्टार्ट वेकअप: इंस्टॉल किए गए ऐप को बलपूर्वक बंद करें और QR कोड स्कैन करें। पुष्टि करें कि OS सीधे लक्षित स्क्रीन (product_detail) में ऐप खोलता है।
  • चरण 2: इंस्टॉल किया गया वार्म-स्टार्ट वेकअप और रिपीट स्कैन: किसी द्वितीयक टैब पर ऐप खुले होने पर, ऐप को बैकग्राउंड में ले जाएं और QR कोड स्कैन करें। पुष्टि करें कि onNewIntent लक्षित स्क्रीन पर रूट करता है। दूर नेविगेट करें, बिल्कुल उसी QR कोड को दूसरी बार स्कैन करें, और पुष्टि करें कि यह अत्यधिक व्यापक डिडुप्लिकेशन द्वारा अवरुद्ध होने के बजाय फिर से नेविगेट करता है।
  • चरण 3: अनइंस्टॉल किया गया डिफ़र्ड रिकवरी (सार्वजनिक रूट): ऐप को अनइंस्टॉल करें, QR कोड स्कैन करें, स्टोर इंस्टॉलेशन पूरा करें, और लॉन्च करें। पुष्टि करें कि डिफ़र्ड पैरामीटर कॉलबैक पेलोड को सामान्यीकृत करता है और product_detail पर रूट करता है।
  • चरण 4: प्रमाणीकरण गेट के साथ अनइंस्टॉल किया गया डिफ़र्ड रिकवरी (सुरक्षित रूट): ऐप को अनइंस्टॉल करें, room_lobby को लक्षित करने वाला QR कोड स्कैन करें, इंस्टॉल करें, और लॉग आउट रहते हुए लॉन्च करें। पुष्टि करें कि ऐप पहले लॉगिन पर रूट करता है, और लॉगिन पूरा करने पर, PendingRouteStore का उपभोग करता है और room_lobby पर नेविगेट करता है।
  • चरण 5: असत्यापित मूल और डुप्लिकेट कुंजी अस्वीकृति: किसी असत्यापित होस्ट से या डुप्लिकेट क्वेरी पैरामीटर (?route=product_detail&route=room_lobby) के साथ एक ACTION_VIEW इंटेंट को ट्रिगर करें। पुष्टि करें कि handleIncomingWakeupIntent इंटेंट को अस्वीकार करता है और एक चेतावनी लॉग करता है।
  • चरण 6: स्क्रीन रोटेशन इडपोटेंसी: डीप-लिंक की गई स्क्रीन पर पहुंचने के बाद, गतिविधि पुनर्सृजन को ट्रिगर करने के लिए डिवाइस को घुमाएं। पुष्टि करें कि EXTRA_ROUTE_CONSUMED ध्वज मूल इंटेंट को फिर से निष्पादित होने से रोकता है।
  • चरण 7: ऑफ़लाइन / बेजोड़ सुचारू फ़ॉलबैक: एयरप्लेन मोड में एक साफ इंस्टॉलेशन लॉन्च करें। पुष्टि करें कि पुनर्प्राप्ति टाइमआउट UI थ्रेड को अवरुद्ध किए बिना हल होता है और उपयोगकर्ता को home_default पर उतारता है।

इन-ऐप रूटिंग तंत्र का तुलनात्मक विश्लेषण

URL योजनाओं, यूनिवर्सल लिंक्स और ऐप लिंक्स, और डिफ़र्ड सीन रिस्टोरेशन का मूल्यांकन

मोबाइल प्लेटफ़ॉर्म अलग-अलग इंस्टॉलेशन राज्यों में उपयोगकर्ताओं को एप्लिकेशन दृश्यों में रूट करने के लिए अलग-अलग तंत्र प्रदान करते हैं:

रूटिंग तंत्र लक्षित ऐप स्थिति OS सत्यापन आवश्यकता स्टोर इंस्टॉल में पैरामीटर का बचना प्राथमिक इंजीनियरिंग बाधा
कस्टम URL योजना (Custom URL Scheme) केवल इंस्टॉल किया गया कोई नहीं (केवल मैनिफ़ेस्ट घोषणा) नहीं (यदि ऐप इंस्टॉल नहीं है तो विफल होता है) अनुपस्थित होने पर ब्राउज़र त्रुटि पॉपअप ट्रिगर करता है; योजना हाईजैकिंग के प्रति संवेदनशील
यूनिवर्सल लिंक्स / ऐप लिंक्स प्राथमिक इंस्टॉल किया गया iOS: AASA फ़ाइल + संबंधित डोमेन हक़दार; Android: assetlinks.json + मिलान ऐप हस्ताक्षर प्रमाणपत्र और सत्यापित इंटेंट-फ़िल्टर कॉन्फ़िगरेशन नहीं (नए इंस्टॉल के लिए डिफ़र्ड लेयर की आवश्यकता होती है) कुछ प्रतिबंधित इन-ऐप वेबव्यू कंटेनरों द्वारा दबाया जा सकता है
Google Play इंस्टॉल रेफ़रर नए Android इंस्टॉल आधिकारिक Google Play Store वितरण पथ हाँ (समर्थित रेफ़रर स्ट्रिंग) Android Play Store विशिष्ट; iOS या गैर-Play स्टोर को कवर नहीं करता है
SDK-सहायता प्राप्त सीन रिस्टोरेशन इंस्टॉल किए गए और नए इंस्टॉल डोमेन एसोसिएशन + सत्यापित SDK एडेप्टर समर्थित प्लेटफ़ॉर्म तंत्र पर सशर्त रूट अलाउलिस्ट सत्यापन और प्लेटफ़ॉर्म गोपनीयता समीक्षा की आवश्यकता है

इंस्टॉलेशन राज्यों और ऑपरेटिंग सिस्टम में वास्तुशिल्प ट्रेड-ऑफ़

कस्टम URL योजनाओं (myapp://product/123) में क्रिप्टोग्राफ़िक डोमेन स्वामित्व सत्यापन का अभाव है, जिसका अर्थ है कि कोई भी एप्लिकेशन समान योजना को पंजीकृत कर सकता है और लिंक को रोक सकता है। इसके अलावा, जब ऐप इंस्टॉल नहीं होता है तो एक कच्चे कस्टम योजना QR कोड को स्कैन करने से एक अपरिवर्तनीय ब्राउज़र त्रुटि होती है।

यूनिवर्सल लिंक्स और एंड्रॉइड ऐप लिंक्स HTTPS पर डोमेन स्वामित्व को सत्यापित करते हैं और ऐप अनुपस्थित होने पर वेब लैंडिंग पेज पर वापस आ जाते हैं, जिससे वे QR रूटिंग के लिए आवश्यक आधार बन जाते हैं। इंस्टॉल किए गए वेकअप के लिए यूनिवर्सल लिंक्स और ऐप लिंक्स को प्लेटफ़ॉर्म-समर्थित डिफ़र्ड रिकवरी (जैसे एंड्रॉइड पर Google Play इंस्टॉल रेफ़रर और गोपनीयता-समीक्षा किए गए SDK एडेप्टर) के साथ संयोजित करना एकल QR संपत्ति से डुअल-स्टेट सीन रिस्टोरेशन को सक्षम बनाता है।

अक्सर पूछे जाने वाले प्रश्न (FAQ)

यदि ऐप अभी तक इंस्टॉल नहीं है, तो कस्टम QR पैरामीटर्स किसी विशिष्ट इन-ऐप पेज को कैसे खोलते हैं?
जब ऐप इंस्टॉल नहीं होता है, तो QR कोड एक वेब लैंडिंग पेज खोलता है जो उपयोगकर्ता को ऐप स्टोर पर निर्देशित करने से पहले रूटिंग पैरामीटर्स को रिकॉर्ड करता है। इंस्टॉलेशन के बाद शुरुआती लॉन्च पर, एप्लिकेशन प्लेटफ़ॉर्म-समर्थित डिफ़र्ड रिकवरी तंत्र के माध्यम से पैरामीटर्स को पुनर्प्राप्त करने का प्रयास करता है, लक्षित रूट को सत्यापित करता है, और निर्दिष्ट स्क्रीन पर नेविगेट करता है।
मेरा QR कोड स्कैन करने पर लक्षित पृष्ठ के बजाय ऐप होम स्क्रीन पर क्यों खुलता है?
होम स्क्रीन पर उतरना आमतौर पर तीन कारणों से होता है: एंड्रॉइड गतिविधि को वार्म-स्टार्ट इंटेंट को कैप्चर करने के लिए `onNewIntent` को ओवरराइड किए बिना `singleTop` या `singleTask` के रूप में कॉन्फ़िगर किया गया है, मुख्य UI ग्राफ़ माउंट होना समाप्त होने से पहले नेविगेशन राउटर निष्पादित हुआ, या लक्षित स्क्रीन को उपयोगकर्ता प्रमाणीकरण की आवश्यकता थी और लॉगिन प्रवाह के दौरान लंबित रूट को त्याग दिया।
मैं दुर्भावनापूर्ण QR कोड को उपयोगकर्ताओं को अनधिकृत आंतरिक स्क्रीन पर रूट करने से कैसे रोकूं?
कच्चे URL पथों को सीधे अपने नेविगेशन कंट्रोलर में कभी न पास करें। आने वाले URI स्कीमा, होस्ट और पथ को सत्यापित करें, अनुमत रूट कुंजियों की एक सख्त क्लाइंट-साइड अलाउलिस्ट लागू करें, नियमित अभिव्यक्तियों का उपयोग करके एंटिटी आईडी प्रारूपों को सत्यापित करें, और संरक्षित स्क्रीन को रेंडर करने से पहले क्लाइंट प्रमाणीकरण स्थिति और सर्वर-साइड संसाधन अनुमतियों दोनों को सत्यापित करें।

सारांश और निर्णय ढांचा

कस्टम पैरामीटर्स के साथ ऐप रूटिंग के लिए QR कोड बनाने के लिए, इंजीनियरिंग टीमों को कॉम्पैक्ट बाहरी URL क्वेरी स्ट्रिंग्स को स्थिति-जागरूक नेटिव नेविगेशन कंट्रोलर्स के साथ जोड़ना होगा। एक संपूर्ण कार्यान्वयन मौजूदा उपयोगकर्ताओं के लिए सीधे ऑपरेटिंग-सिस्टम वेकअप और नए ऐप-स्टोर इंस्टॉलेशन के लिए सशर्त डिफ़र्ड पैरामीटर पुनर्प्राप्ति दोनों को संभालता है।

चूँकि ऐप स्टोर में पैरामीटर निरंतरता प्लेटफ़ॉर्म-विशिष्ट तंत्रों पर निर्भर करती है—जैसे एंड्रॉइड पर Google Play इंस्टॉल रेफ़रर और iOS पर सत्यापित, गोपनीयता-अनुरूप वर्कफ़्लो—अनुप्रयोगों को पुनर्प्राप्त रूटिंग पेलोड को अविश्वसनीय उम्मीदवारों के रूप में मानना ​​चाहिए। अलाउलिस्टेड रूट डिस्पैचर्स, प्रमाणीकरण-स्थिति कतारबद्धता, और सुंदर होम-स्क्रीन फ़ॉलबैक को जोड़कर, टीमें एप्लिकेशन स्थिरता या प्लेटफ़ॉर्म गोपनीयता नियमों से समझौता किए बिना विश्वसनीय सीन रिस्टोरेशन प्रदान कर सकती हैं।

OpoInstall के SDK और डेवलपर दस्तावेज़ वेब-टू-ऐप रूटिंग और पैरामीटर पासिंग को लागू करने के लिए घटक प्रदान करते हैं। उत्पादन रोलआउट से पहले आपके तैनात SDK रिलीज़ द्वारा समर्थित विशिष्ट API हस्ताक्षरों, कॉलबैक स्कीमा और प्लेटफ़ॉर्म व्यवहार की पुष्टि करें।

एकीकरण विवरण के लिए, OpoInstall डेवलपर दस्तावेज़ से परामर्श करें, OpoInstall SDK डाउनलोड सेंटर तक पहुंचें, या OpoInstall डेवलपर कंसोल में अपने एप्लिकेशन रूटिंग डोमेन को कॉन्फ़िगर करें।

संबंधित सामग्रियां

Share this article

Keep Discovering

Google ने कनेक्टेड ऐप्स लॉन्च किया? ऐप लिंकिंग में कैसे बदलाव आ रहे हैं

Google ने कनेक्टेड ऐप्स लॉन्च किया? ऐप लिंकिंग में कैसे बदलाव आ रहे हैं

Google ने Gemini और Search के लिए कनेक्टेड ऐप्स प्लेटफॉर्म लॉन्च किया। जानें कि MCP प्रोटोकॉल और AppFunctions किस प्रकार ऐप लिंकिंग, रूटिंग और एट्रिब्यूशन को नया रूप दे रहे हैं।

कस्टम QR कोड को सुरक्षित करके ऑफ़लाइन रिडायरेक्शन धोखाधड़ी को कैसे रोकें

कस्टम QR कोड को सुरक्षित करके ऑफ़लाइन रिडायरेक्शन धोखाधड़ी को कैसे रोकें

HTTPS, HMAC हस्ताक्षर, भौतिक टैम्पर चेक और जोखिम-आधारित धोखाधड़ी पहचान के साथ स्कैन हाईजैकिंग के खिलाफ कस्टम QR कोड को सुरक्षित करना सीखें।

GitHub ने Copilot Fusion लॉन्च किया? मल्टी-मॉडल कोड क्यों मायने रखता है

GitHub ने Copilot Fusion लॉन्च किया? मल्टी-मॉडल कोड क्यों मायने रखता है

GitHub ने Copilot Fusion रनटाइम मॉडल ऑर्केस्ट्रेटर लॉन्च किया। जानें कि कैसे डायनेमिक मॉडल रूटिंग और निष्पादन पैटर्न टोकन लागत और कोडिंग विलंबता को कम करते हैं।