From 240cea7f9cc2ca1daf42c4ae763c537bb6079eb5 Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Thu, 8 Jan 2026 19:37:17 +0530 Subject: [PATCH 1/6] feat(chore): opener ab test iOS SC App --- .../reactnativesmallcasegateway/SmallcaseGatewayModule.kt | 8 ++++++-- ios/SmallcaseGateway.m | 7 ++++--- react-native-smallcase-gateway.podspec | 2 +- smart_investing_react_native/App.tsx | 1 + src/SmallcaseGateway.js | 6 +++++- types/SmallcaseGateway.d.ts | 4 ++++ 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt b/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt index a2fa9e9e..6d6938f8 100644 --- a/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt +++ b/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt @@ -24,8 +24,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte @ReactMethod - fun setConfigEnvironment(envName: String, gateway: String, isLeprechaunActive: Boolean, isAmoEnabled: Boolean, preProvidedBrokers: ReadableArray, promise: Promise) { - + fun setConfigEnvironment(envName: String, gateway: String, isLeprechaunActive: Boolean, isAmoEnabled: Boolean, preProvidedBrokers: ReadableArray, userId: String?, promise: Promise) { try { val brokerList = ArrayList() for (index in 0 until preProvidedBrokers.size()) { @@ -38,6 +37,11 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte val protocol = getProtocol(envName) val env = Environment(gateway = gateway, buildType = protocol, isAmoEnabled = isAmoEnabled, preProvidedBrokers = brokerList, isLeprachaunActive = isLeprechaunActive) + + // Set userIdentification if provided - For Now, we are not accepting this on Gateway SDK + // if (!userId.isNullOrEmpty()) { + // env.userId = userIdentification + // } SmallcaseGatewaySdk.setConfigEnvironment(environment = env, smallcaseGatewayListeners = object : SmallcaseGatewayListeners { override fun onGatewaySetupSuccessfull() { diff --git a/ios/SmallcaseGateway.m b/ios/SmallcaseGateway.m index 554efaf3..a79b5b1f 100644 --- a/ios/SmallcaseGateway.m +++ b/ios/SmallcaseGateway.m @@ -33,6 +33,7 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject) isLeprechaunActive: (BOOL)isLeprechaunActive isAmoEnabled: (BOOL)isAmoEnabled preProvidedBrokers: (NSArray *)preProvidedBrokers + userId:(NSString *)userId setConfigEnvironmentWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSInteger environment = EnvironmentProduction; @@ -51,9 +52,10 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject) brokerConfig:preProvidedBrokers apiEnvironment:environment isLeprechaunActive:isLeprechaunActive - isAmoEnabled:isAmoEnabled]; + isAmoEnabled:isAmoEnabled + userId:userId]; - [SCGateway.shared setupWithConfig: config completion:^(BOOL success,NSError * error) { + [SCGateway.shared setupWithConfig:config completion:^(BOOL success, NSError *error) { if(success) { resolve(@(YES)); } else { @@ -65,7 +67,6 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject) reject(@"setConfigEnvironment", @"Env setup failed", err); } - }]; } diff --git a/react-native-smallcase-gateway.podspec b/react-native-smallcase-gateway.podspec index 97040b27..9c087bf8 100644 --- a/react-native-smallcase-gateway.podspec +++ b/react-native-smallcase-gateway.podspec @@ -33,6 +33,6 @@ Pod::Spec.new do |s| s.dependency "ReactCommon/turbomodule/core" end - s.dependency 'SCGateway', '7.0.0' + s.dependency 'SCGateway-experiment-opener-ab-test-591c814', '6.1.1-16-debug' s.dependency 'SCLoans', '6.0.2' end diff --git a/smart_investing_react_native/App.tsx b/smart_investing_react_native/App.tsx index 610dc920..0f781779 100644 --- a/smart_investing_react_native/App.tsx +++ b/smart_investing_react_native/App.tsx @@ -51,6 +51,7 @@ const App: React.FC = () => { isAmoEnabled: true, brokerList: ['zerodha', 'upstox', 'angelone'], environmentName: 'development', + userId: 'user_12345' }); console.log('Configuration set successfully'); diff --git a/src/SmallcaseGateway.js b/src/SmallcaseGateway.js index 61f34fe3..221a9e77 100644 --- a/src/SmallcaseGateway.js +++ b/src/SmallcaseGateway.js @@ -12,6 +12,7 @@ const { SmallcaseGateway: SmallcaseGatewayNative } = NativeModules; * @property {boolean} isAmoEnabled - support AMO (subject to broker support) * @property {Array} brokerList - list of broker names * @property {'production' | 'staging' | 'development'} environmentName - environment name + * @property {string} userId - user identification (iOS only, optional) * * @typedef {Object} transactionRes * @property {string} data - response data @@ -60,6 +61,7 @@ const setConfigEnvironment = async (envConfig) => { isLeprechaun, isAmoEnabled, environmentName, + userId, } = safeConfig; const safeIsLeprechaun = Boolean(isLeprechaun); @@ -68,6 +70,7 @@ const setConfigEnvironment = async (envConfig) => { const safeGatewayName = typeof gatewayName === 'string' ? gatewayName : ''; const safeEnvName = typeof environmentName === 'string' ? environmentName : ENV.PROD; + const safeUserId = typeof userId === 'string' ? userId : null; defaultBrokerList = safeBrokerList; @@ -76,7 +79,8 @@ const setConfigEnvironment = async (envConfig) => { safeGatewayName, safeIsLeprechaun, safeIsAmoEnabled, - safeBrokerList + safeBrokerList, + safeUserId ); }; diff --git a/types/SmallcaseGateway.d.ts b/types/SmallcaseGateway.d.ts index 8b892487..fedddfb9 100644 --- a/types/SmallcaseGateway.d.ts +++ b/types/SmallcaseGateway.d.ts @@ -20,6 +20,10 @@ export type envConfig = { * - environment name */ environmentName: 'production' | 'staging' | 'development'; + /** + * - user identification (iOS only, optional) + */ + userId?: string; }; export type transactionRes = { /** From 368e9117ccef8d1d7232d423d905f3f82d846410 Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Wed, 14 Jan 2026 00:45:37 +0530 Subject: [PATCH 2/6] feat(gateway-sdk): pass user ID in init for A/B testing --- .../SmallcaseGatewayModule.kt | 7 ++++--- ios/SmallcaseGateway.m | 10 ++++++---- src/SmallcaseGateway.js | 14 +++++++------- types/SmallcaseGateway.d.ts | 8 +++----- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt b/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt index 6d6938f8..b2b45085 100644 --- a/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt +++ b/android/src/main/java/com/reactnativesmallcasegateway/SmallcaseGatewayModule.kt @@ -24,7 +24,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte @ReactMethod - fun setConfigEnvironment(envName: String, gateway: String, isLeprechaunActive: Boolean, isAmoEnabled: Boolean, preProvidedBrokers: ReadableArray, userId: String?, promise: Promise) { + fun setConfigEnvironment(envName: String, gateway: String, isLeprechaunActive: Boolean, isAmoEnabled: Boolean, preProvidedBrokers: ReadableArray, promise: Promise) { try { val brokerList = ArrayList() for (index in 0 until preProvidedBrokers.size()) { @@ -70,8 +70,9 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte } @ReactMethod - fun init(sdkToken: String, promise: Promise) { - + fun init(sdkToken: String, externalMeta: ReadableMap?, promise: Promise) { + // externalMeta is accepted but not used on Android (iOS only feature) + // Extract externalIdentifier if needed in future val initReq = InitRequest(sdkToken) SmallcaseGatewaySdk.init(authRequest = initReq, gatewayInitialisationListener = object : DataListener { override fun onFailure(errorCode: Int, errorMessage: String, data: String?) { diff --git a/ios/SmallcaseGateway.m b/ios/SmallcaseGateway.m index a79b5b1f..bc8d7699 100644 --- a/ios/SmallcaseGateway.m +++ b/ios/SmallcaseGateway.m @@ -33,7 +33,6 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject) isLeprechaunActive: (BOOL)isLeprechaunActive isAmoEnabled: (BOOL)isAmoEnabled preProvidedBrokers: (NSArray *)preProvidedBrokers - userId:(NSString *)userId setConfigEnvironmentWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSInteger environment = EnvironmentProduction; @@ -52,8 +51,7 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject) brokerConfig:preProvidedBrokers apiEnvironment:environment isLeprechaunActive:isLeprechaunActive - isAmoEnabled:isAmoEnabled - userId:userId]; + isAmoEnabled:isAmoEnabled]; [SCGateway.shared setupWithConfig:config completion:^(BOOL success, NSError *error) { if(success) { @@ -73,9 +71,13 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject) //MARK: SDK init RCT_REMAP_METHOD(init, sdkToken:(NSString *)sdkToken + externalMeta:(NSDictionary *)externalMeta initWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - [SCGateway.shared initializeGatewayWithSdkToken:sdkToken completion:^(BOOL success, NSError * error) { + + [SCGateway.shared initializeGatewayWithSdkToken:sdkToken + externalMeta:externalMeta + completion:^(BOOL success, NSError *error) { if(success) { resolve(@(YES)); } else { diff --git a/src/SmallcaseGateway.js b/src/SmallcaseGateway.js index 221a9e77..36a0aa94 100644 --- a/src/SmallcaseGateway.js +++ b/src/SmallcaseGateway.js @@ -12,7 +12,6 @@ const { SmallcaseGateway: SmallcaseGatewayNative } = NativeModules; * @property {boolean} isAmoEnabled - support AMO (subject to broker support) * @property {Array} brokerList - list of broker names * @property {'production' | 'staging' | 'development'} environmentName - environment name - * @property {string} userId - user identification (iOS only, optional) * * @typedef {Object} transactionRes * @property {string} data - response data @@ -61,7 +60,6 @@ const setConfigEnvironment = async (envConfig) => { isLeprechaun, isAmoEnabled, environmentName, - userId, } = safeConfig; const safeIsLeprechaun = Boolean(isLeprechaun); @@ -70,7 +68,6 @@ const setConfigEnvironment = async (envConfig) => { const safeGatewayName = typeof gatewayName === 'string' ? gatewayName : ''; const safeEnvName = typeof environmentName === 'string' ? environmentName : ENV.PROD; - const safeUserId = typeof userId === 'string' ? userId : null; defaultBrokerList = safeBrokerList; @@ -79,8 +76,7 @@ const setConfigEnvironment = async (envConfig) => { safeGatewayName, safeIsLeprechaun, safeIsAmoEnabled, - safeBrokerList, - safeUserId + safeBrokerList ); }; @@ -89,10 +85,14 @@ const setConfigEnvironment = async (envConfig) => { * * note: this must be called after `setConfigEnvironment()` * @param {string} sdkToken + * @param {Object} [externalMeta] - external metadata (iOS only, optional) + * @param {Object} [externalMeta.externalIdentifier] - key-value pairs for external identifiers (e.g., { userId: '123' }) */ -const init = async (sdkToken) => { +const init = async (sdkToken, externalMeta) => { const safeToken = typeof sdkToken === 'string' ? sdkToken : ''; - return SmallcaseGatewayNative.init(safeToken); + const safeExternalMeta = externalMeta && typeof externalMeta === 'object' ? externalMeta : null; + + return SmallcaseGatewayNative.init(safeToken, safeExternalMeta); }; /** diff --git a/types/SmallcaseGateway.d.ts b/types/SmallcaseGateway.d.ts index fedddfb9..666eec11 100644 --- a/types/SmallcaseGateway.d.ts +++ b/types/SmallcaseGateway.d.ts @@ -20,10 +20,6 @@ export type envConfig = { * - environment name */ environmentName: 'production' | 'staging' | 'development'; - /** - * - user identification (iOS only, optional) - */ - userId?: string; }; export type transactionRes = { /** @@ -99,8 +95,10 @@ declare namespace SmallcaseGateway { * * note: this must be called after `setConfigEnvironment()` * @param {string} sdkToken + * @param {Object} [externalMeta] - external metadata (iOS only, optional) + * @param {Object} [externalMeta.externalIdentifier] - key-value pairs for external identifiers (e.g., { userId: '123' }) */ -declare function init(sdkToken: string): unknown; +declare function init(sdkToken: string, externalMeta?: { externalIdentifier?: { [key: string]: string } }): unknown; /** * Logs the user out and removes the web session. * From bf1284aab46195958b75336f4297e7fc06b32b6f Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Wed, 14 Jan 2026 01:11:37 +0530 Subject: [PATCH 3/6] feat(gateway-sdk): pass user ID in init for A/B testing --- .../app/apis/Functions.tsx | 5 +++++ src/__tests__/init.test.js | 22 ++++++++++++++----- types/index.d.ts | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/smart_investing_react_native/app/apis/Functions.tsx b/smart_investing_react_native/app/apis/Functions.tsx index 159d6a2a..d9990eb0 100644 --- a/smart_investing_react_native/app/apis/Functions.tsx +++ b/smart_investing_react_native/app/apis/Functions.tsx @@ -73,6 +73,11 @@ async function setEnvironment( } const initGatewayResponse = await SmallcaseGateway.init( authJwtResult.authJwt, + { + externalIdentifier: { + userId: "testValue", // Replace with actual userId if needed + }, + }, ); console.log('initGatewayResponse: ' + initGatewayResponse); alert('Set Environment', 'Successful!!'); diff --git a/src/__tests__/init.test.js b/src/__tests__/init.test.js index c40fb918..1b06f93c 100644 --- a/src/__tests__/init.test.js +++ b/src/__tests__/init.test.js @@ -8,22 +8,32 @@ describe('init', () => { test('valid', async () => { await SmallcaseGateway.init('test-token'); - expect(initFn).toHaveBeenNthCalledWith(1, 'test-token'); + expect(initFn).toHaveBeenNthCalledWith(1, 'test-token', null); + }); + + test('valid with externalMeta', async () => { + const externalMeta = { + externalIdentifier: { + userId: 'user123' + } + }; + await SmallcaseGateway.init('test-token', externalMeta); + expect(initFn).toHaveBeenNthCalledWith(2, 'test-token', externalMeta); }); test('empty', async () => { await SmallcaseGateway.init(); - expect(initFn).toHaveBeenNthCalledWith(2, ''); + expect(initFn).toHaveBeenNthCalledWith(3, '', null); }); test('invalid', async () => { await SmallcaseGateway.init(undefined); - expect(initFn).toHaveBeenNthCalledWith(3, ''); + expect(initFn).toHaveBeenNthCalledWith(4, '', null); - await SmallcaseGateway.init({}); - expect(initFn).toHaveBeenNthCalledWith(4, ''); + await SmallcaseGateway.init('test-token', {}); + expect(initFn).toHaveBeenNthCalledWith(5, 'test-token', {}); await SmallcaseGateway.init(123); - expect(initFn).toHaveBeenNthCalledWith(5, ''); + expect(initFn).toHaveBeenNthCalledWith(6, '', null); }); }); diff --git a/types/index.d.ts b/types/index.d.ts index 68e21b06..66417fb5 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -67,7 +67,7 @@ declare const _default: { authorizeHoldings: string; mfHoldingsImport: string; }; - init: (sdkToken: string) => unknown; + init: (sdkToken: string, externalMeta?: { externalIdentifier?: { [key: string]: string } }) => unknown; logoutUser: () => Promise; triggerLeadGen: (userDetails?: import("./SmallcaseGateway").userDetails, utmParams?: any) => any; triggerLeadGenWithStatus: (userDetails?: import("./SmallcaseGateway").userDetails) => Promise; From 24f732ec5081ac0aedaa187982cad6a134f1bc8c Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Wed, 14 Jan 2026 15:16:14 +0530 Subject: [PATCH 4/6] chore(scg): bumping up iOS, android gateway sdk versions --- android/build.gradle | 2 +- react-native-smallcase-gateway.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 8e4e4dc2..9c116ccd 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -149,7 +149,7 @@ def kotlin_version = getExtOrDefault('kotlinVersion') dependencies { //noinspection GradleDynamicVersion implementation 'com.facebook.react:react-native:+' // From node_modules - implementation 'com.smallcase.gateway:sdk:6.0.0' + implementation 'com.smallcase.gateway:sdk:6.0.1' implementation 'com.smallcase.loans:sdk:4.0.0' 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 9c087bf8..40136e06 100644 --- a/react-native-smallcase-gateway.podspec +++ b/react-native-smallcase-gateway.podspec @@ -33,6 +33,6 @@ Pod::Spec.new do |s| s.dependency "ReactCommon/turbomodule/core" end - s.dependency 'SCGateway-experiment-opener-ab-test-591c814', '6.1.1-16-debug' + s.dependency 'SCGateway', '7.0.1' s.dependency 'SCLoans', '6.0.2' end From 705b3bfa84d4368cc300f9fa00e80812f465610d Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Wed, 14 Jan 2026 16:02:58 +0530 Subject: [PATCH 5/6] feat(a/b-test): working state --- smart_investing_react_native/app/apis/Functions.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/smart_investing_react_native/app/apis/Functions.tsx b/smart_investing_react_native/app/apis/Functions.tsx index d9990eb0..10fead11 100644 --- a/smart_investing_react_native/app/apis/Functions.tsx +++ b/smart_investing_react_native/app/apis/Functions.tsx @@ -99,6 +99,7 @@ async function connect(env: Environment, userId: string): Promise { null, null, ); + console.log(`Transaction ID: ${transactionId}, Intent: ${transactionType.connect}`); console.log('transactionId: ' + transactionId); const transactionResponse = await SmallcaseGateway.triggerTransaction( transactionId, @@ -128,6 +129,7 @@ async function connect(env: Environment, userId: string): Promise { async function triggerMftxn(env: Environment, transactionId: string) { try { + console.log(`Transaction ID: ${transactionId}, Intent: MF_HOLDINGS_IMPORT`); console.log('triggerMftxn txn id: ' + transactionId); const res = await SmallcaseGateway.triggerMfTransaction(transactionId); console.log('triggerMftxn res: ' + JSON.stringify(res)); @@ -153,6 +155,7 @@ async function placeSstOrder( null, null, ); + console.log(`Transaction ID: ${transactionId}, Intent: ${transactionType.transaction}`); console.log('sst txn id: ' + transactionId); const res = await SmallcaseGateway.triggerTransaction(transactionId, { test: 'test', @@ -185,6 +188,7 @@ async function authorizeHoldings(env: Environment, userId: string) { null, null, ); + console.log(`Transaction ID: ${transactionId}, Intent: ${transactionType.authorizeHoldings}`); console.log('authorizeHoldings txn id: ' + transactionId); const res = await SmallcaseGateway.triggerTransaction(transactionId, { test: 'test', @@ -209,6 +213,7 @@ async function reconcileHoldings(env: Environment, userId: string) { null, null, ); + console.log(`Transaction ID: ${transactionId}, Intent: ${transactionType.transaction} (RECONCILIATION)`); const res = await SmallcaseGateway.triggerTransaction(transactionId); console.log('reconcileHoldings res: ' + JSON.stringify(res)); alert('Reconcile Holdings', JSON.stringify(res)); @@ -243,6 +248,7 @@ async function importHoldings( assetConfig, null, ); + console.log(`Transaction ID: ${transactionId}, Intent: ${transactionType.holdingsImport}`); console.log('importHoldings txn id: ' + transactionId); const res = await SmallcaseGateway.triggerTransaction(transactionId, { test: 'test', @@ -401,6 +407,7 @@ async function fetchFunds(env: Environment, userId: string): Promise { null, null, ); + console.log(`Transaction ID: ${transactionId}, Intent: ${transactionType.fetchFunds}`); console.log('fetchFunds txn id: ' + transactionId); const res = await SmallcaseGateway.triggerTransaction(transactionId, { test: 'test', @@ -509,6 +516,7 @@ async function logout() { async function triggerTxn(txnId: string) { try { + console.log(`Transaction ID: ${txnId}, Intent: MANUAL_TRIGGER`); const res = await SmallcaseGateway.triggerTransaction(txnId, { test: 'test', }); From 48a33a1cfa35f0df09edf11b92db3d3d775cf166 Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Wed, 14 Jan 2026 16:05:48 +0530 Subject: [PATCH 6/6] feat(a/b-test): working state --- smart_investing_react_native/App.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/smart_investing_react_native/App.tsx b/smart_investing_react_native/App.tsx index 0f781779..610dc920 100644 --- a/smart_investing_react_native/App.tsx +++ b/smart_investing_react_native/App.tsx @@ -51,7 +51,6 @@ const App: React.FC = () => { isAmoEnabled: true, brokerList: ['zerodha', 'upstox', 'angelone'], environmentName: 'development', - userId: 'user_12345' }); console.log('Configuration set successfully');