From 3f119a724c60bcb7ba15ee4f34695a8c9f4a2edf Mon Sep 17 00:00:00 2001 From: Rishabh Vinay Date: Thu, 3 Sep 2026 13:24:31 +0530 Subject: [PATCH 1/5] feat: add incognito param to triggerTransaction Threads an optional incognito boolean (default false) through all three bridge layers to the new incognito support in the native SDKs: - JS: triggerTransaction(transactionId, utmParams, brokerList, incognito) - Android: new trailing incognito param on the @ReactMethod, forwarded to SmallcaseGatewaySdk.triggerTransaction's incognito param - iOS: new trailing incognito param on the RCT_REMAP_METHOD, forwarded to the triggerTransactionFlow(...incognito:completion:) Obj-C overload Additive/backward-compatible - existing callers passing 1-3 args are unaffected. Requires the native SDK dependency pins (android/build.gradle, react-native-smallcase-gateway.podspec) to point at builds containing the incognito support before this compiles - currently still pinned to stable 6.1.1 / 7.2.0, which predate it. Co-Authored-By: Claude Sonnet 5 --- .../SmallcaseGatewayModule.kt | 3 ++- ios/SmallcaseGateway.m | 2 ++ src/SmallcaseGateway.js | 12 ++++++++++-- types/SmallcaseGateway.d.ts | 3 ++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt b/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt index c36f9a6e..d937656e 100644 --- a/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt +++ b/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt @@ -88,7 +88,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte } @ReactMethod - fun triggerTransaction(transactionId: String, utmParams: ReadableMap?, brokerList: ReadableArray?, promise: Promise) { + fun triggerTransaction(transactionId: String, utmParams: ReadableMap?, brokerList: ReadableArray?, incognito: Boolean, promise: Promise) { var safeBrokerList = listOf() @@ -104,6 +104,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte activity = activity, transactionId = transactionId, preProvidedBrokers = safeBrokerList, + incognito = incognito, transactionResponseListener = object : TransactionResponseListener { override fun onSuccess(transactionResult: TransactionResult) { val res = resultToWritableMap(transactionResult, true) diff --git a/ios/SmallcaseGateway.m b/ios/SmallcaseGateway.m index fa04027c..7ee3fade 100644 --- a/ios/SmallcaseGateway.m +++ b/ios/SmallcaseGateway.m @@ -145,6 +145,7 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject) transactionId:(NSString *)transactionId utmParams:(NSDictionary *)utmParams brokerList:(NSArray *)brokerList + incognito:(BOOL)incognito triggerTransactionWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_main_queue(), ^(void) { @@ -153,6 +154,7 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject) presentingController:[[[UIApplication sharedApplication] keyWindow] rootViewController] utmParams:utmParams brokerConfig:brokerList + incognito:incognito completion: ^(id response, NSError * error) { if (error != nil) { NSMutableDictionary *responseDict = [[NSMutableDictionary alloc] init]; diff --git a/src/SmallcaseGateway.js b/src/SmallcaseGateway.js index b2837d24..1964af7f 100644 --- a/src/SmallcaseGateway.js +++ b/src/SmallcaseGateway.js @@ -106,11 +106,18 @@ const init = async (sdkToken, externalMeta) => { * @param {string} transactionId * @param {Object} [utmParams] * @param {Array} [brokerList] + * @param {boolean} [incognito] - when true, opens the broker flow in a private/incognito session that leaves no local browser data behind * @returns {Promise} */ -const triggerTransaction = async (transactionId, utmParams, brokerList) => { +const triggerTransaction = async ( + transactionId, + utmParams, + brokerList, + incognito +) => { const safeUtm = safeObject(utmParams); const safeId = typeof transactionId === 'string' ? transactionId : ''; + const safeIncognito = typeof incognito === 'boolean' ? incognito : false; let safeBrokerList = sanitizeBrokerList(brokerList); @@ -121,7 +128,8 @@ const triggerTransaction = async (transactionId, utmParams, brokerList) => { return SmallcaseGatewayNative.triggerTransaction( safeId, safeUtm, - safeBrokerList + safeBrokerList, + safeIncognito ); }; diff --git a/types/SmallcaseGateway.d.ts b/types/SmallcaseGateway.d.ts index 666eec11..bd36ad2a 100644 --- a/types/SmallcaseGateway.d.ts +++ b/types/SmallcaseGateway.d.ts @@ -142,9 +142,10 @@ declare function archiveSmallcase(iscid: string): unknown; * @param {string} transactionId * @param {Object} [utmParams] * @param {Array} [brokerList] + * @param {boolean} [incognito] - when true, opens the broker flow in a private/incognito session that leaves no local browser data behind * @returns {Promise} */ -declare function triggerTransaction(transactionId: string, utmParams?: any, brokerList?: Array): Promise; +declare function triggerTransaction(transactionId: string, utmParams?: any, brokerList?: Array, incognito?: boolean): Promise; /** * triggers a transaction with a transaction id * From 8931510a96a9a4c3b97fe373f9838404c679498a Mon Sep 17 00:00:00 2001 From: Rishabh Vinay Date: Thu, 3 Sep 2026 18:43:28 +0530 Subject: [PATCH 2/5] docs: document incognito mode for SDK consumers Adds an Incognito Mode section to the README covering the new triggerTransaction(transactionId, utmParams, brokerList, incognito) parameter - what it does, backward compatibility, and known limitations (AutoFill, account-level connection state). Co-Authored-By: Claude Sonnet 5 --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 80a9d484..0653d1f0 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,31 @@ const res = await SmallcaseGateway.triggerTransaction(transactionId); SmallcaseGateway.triggerLeadGen({ email: "test@gmail.com" }); ``` +## Incognito Mode + +`triggerTransaction` accepts an optional 4th `incognito` argument (default `false`): + +```javascript +// execute a transaction in incognito mode +const res = await SmallcaseGateway.triggerTransaction( + transactionId, + utmParams, + brokerList, + true // incognito +); +``` + +When `true`, the broker flow opens in a private/ephemeral browsing session: + +- No cookies, cache, or login state from the flow persist on the device once it closes. +- Native broker app login (e.g. Kite/Zerodha app-to-app login) is skipped in favor of the in-app web login, even if the broker app is installed and would normally be used. + +This is opt-in and fully backward compatible — existing calls to `triggerTransaction(transactionId)`, `triggerTransaction(transactionId, utmParams)`, or `triggerTransaction(transactionId, utmParams, brokerList)` are unaffected and continue to run in normal (non-incognito) mode. + +**Known limitations:** incognito prevents local browser data from persisting, but it does not (and cannot) override OS-level Password AutoFill suggestions already saved to the device's keychain, and it does not affect the smallcase account's own broker-connection state — once a broker is connected (incognito or not), subsequent transactions for that same account will correctly recognize it as already connected, since that state lives on smallcase's backend, not in local browser storage. + +**Requires:** `com.smallcase.gateway:sdk` (Android) and `SCGateway` (iOS) versions with incognito support. Check with the platform SDK release notes for the minimum version once released. + ## Debug / Contribution Make sure you have react native dev environment set up From eb28ad689a006c0914a62024a41ccf4e901469d6 Mon Sep 17 00:00:00 2001 From: Rishabh Vinay Date: Thu, 3 Sep 2026 18:52:08 +0530 Subject: [PATCH 3/5] docs: clarify incognito does not switch smallcase accounts Co-Authored-By: Claude Sonnet 5 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0653d1f0..df11f8f0 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,8 @@ This is opt-in and fully backward compatible — existing calls to `triggerTrans **Requires:** `com.smallcase.gateway:sdk` (Android) and `SCGateway` (iOS) versions with incognito support. Check with the platform SDK release notes for the minimum version once released. +**Using incognito for a different (e.g. linked/family) account:** `incognito: true` on its own does not switch which smallcase account the SDK is acting as — it only changes how that call's browser session behaves. To connect or transact on behalf of a different smallcase account than the one currently authenticated, call `SmallcaseGateway.init(sdkToken)` again with that account's own token before calling `triggerTransaction` — the same way you would for any account switch, incognito or not. + ## Debug / Contribution Make sure you have react native dev environment set up From 485745bd2d1f541a7579effaf909279d0cdba3b8 Mon Sep 17 00:00:00 2001 From: Rishabh Vinay Date: Fri, 4 Sep 2026 11:19:04 +0530 Subject: [PATCH 4/5] chore: pin to feat/incognito-mode native SDK builds Internal test pins for QA against the incognito-mode branch builds, mirroring the existing dark-theme branch pin pattern: - Android: com.smallcase.gateway:sdk-feat-incognito-mode-c1b21c9:6.1.2-3193-release - iOS: SCGateway-feat-incognito-mode-2d15c7b 7.2.0-28-release Co-Authored-By: Claude Sonnet 5 --- android/build.gradle | 4 +++- react-native-smallcase-gateway.podspec | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 68bb5f72..553b3db9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -149,7 +149,9 @@ def kotlin_version = getExtOrDefault('kotlinVersion') dependencies { //noinspection GradleDynamicVersion implementation 'com.facebook.react:react-native:+' // From node_modules - implementation 'com.smallcase.gateway:sdk:6.1.1' + // INTERNAL TEST PIN: feat/incognito-mode branch build. + // Revert to: implementation 'com.smallcase.gateway:sdk:6.1.1' + implementation 'com.smallcase.gateway:sdk-feat-incognito-mode-c1b21c9:6.1.2-3193-release' implementation 'com.smallcase.loans:sdk-sourav-native-dark-theme-cb54bf9:5.1.4-92-release' implementation "androidx.core:core-ktx:1.3.1" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/react-native-smallcase-gateway.podspec b/react-native-smallcase-gateway.podspec index 686d423d..2e0a2543 100644 --- a/react-native-smallcase-gateway.podspec +++ b/react-native-smallcase-gateway.podspec @@ -34,7 +34,9 @@ Pod::Spec.new do |s| s.dependency "ReactCommon/turbomodule/core" end - s.dependency 'SCGateway', '7.2.0' + # INTERNAL TEST PIN: feat/incognito-mode branch build. + # Revert to: s.dependency 'SCGateway', '7.2.0' + s.dependency 'SCGateway-feat-incognito-mode-2d15c7b', '7.2.0-28-release' # INTERNAL TEST PIN: dark-theme branch build (mirrors the android/build.gradle pin). # Revert to: s.dependency 'SCLoans', '7.2.0' s.dependency 'SCLoans-sourav-native-dark-theme-37c97d9', '7.1.2-45-release' From b1ed52e604cff2f6d0826715bb2f42f81f6401e7 Mon Sep 17 00:00:00 2001 From: Rishabh Vinay Date: Fri, 4 Sep 2026 11:44:14 +0530 Subject: [PATCH 5/5] fix: make incognito genuinely optional at the RN bridge boundary Previously incognito was a required non-nullable param at the native bridge layer (Boolean in Kotlin, BOOL in Objective-C) - it only felt optional to JS callers because src/SmallcaseGateway.js always computed a default before forwarding. That's inconsistent with utmParams/brokerList, which are genuinely nullable at the bridge, and with the native SDKs themselves where incognito is a real default parameter. Now nullable on both platforms (Boolean? / nullable NSNumber *), defaulting to false when absent, so the bridge methods are robust regardless of whether a caller goes through the JS wrapper. Co-Authored-By: Claude Sonnet 5 --- .../reactnativesmallcasegateway/SmallcaseGatewayModule.kt | 4 ++-- ios/SmallcaseGateway.m | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt b/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt index d937656e..46eb0655 100644 --- a/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt +++ b/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt @@ -88,7 +88,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte } @ReactMethod - fun triggerTransaction(transactionId: String, utmParams: ReadableMap?, brokerList: ReadableArray?, incognito: Boolean, promise: Promise) { + fun triggerTransaction(transactionId: String, utmParams: ReadableMap?, brokerList: ReadableArray?, incognito: Boolean?, promise: Promise) { var safeBrokerList = listOf() @@ -104,7 +104,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte activity = activity, transactionId = transactionId, preProvidedBrokers = safeBrokerList, - incognito = incognito, + incognito = incognito ?: false, transactionResponseListener = object : TransactionResponseListener { override fun onSuccess(transactionResult: TransactionResult) { val res = resultToWritableMap(transactionResult, true) diff --git a/ios/SmallcaseGateway.m b/ios/SmallcaseGateway.m index 7ee3fade..edc4976d 100644 --- a/ios/SmallcaseGateway.m +++ b/ios/SmallcaseGateway.m @@ -145,16 +145,17 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject) transactionId:(NSString *)transactionId utmParams:(NSDictionary *)utmParams brokerList:(NSArray *)brokerList - incognito:(BOOL)incognito + incognito:(nullable NSNumber *)incognito triggerTransactionWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_main_queue(), ^(void) { + BOOL isIncognito = incognito != nil ? [incognito boolValue] : NO; [SCGateway.shared triggerTransactionFlowWithTransactionId:transactionId presentingController:[[[UIApplication sharedApplication] keyWindow] rootViewController] utmParams:utmParams brokerConfig:brokerList - incognito:incognito + incognito:isIncognito completion: ^(id response, NSError * error) { if (error != nil) { NSMutableDictionary *responseDict = [[NSMutableDictionary alloc] init];