react-native-otp-auto-verify 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -82,7 +82,7 @@ Your backend **must** include the app hash at the **end** of the SMS.
82
82
  Requirements:
83
83
 
84
84
  - Message must be **≤ 140 bytes**
85
- - Must contain a **4–6 digit** OTP
85
+ - Must contain a **4–8 digit** OTP
86
86
  - Must **end with** the app hash from `getHash()`
87
87
 
88
88
  Recommended format:
@@ -327,7 +327,7 @@ Use this on your OTP screen. It manages:
327
327
 
328
328
  | Property | Type | Default | Description |
329
329
  | ---------------- | --------------------- | ------- | ---------------------------------------- |
330
- | `numberOfDigits` | `4 \| 5 \| 6` | `6` | OTP length to extract |
330
+ | `numberOfDigits` | `4 \| 5 \| 6 \| 7 \| 8` | `6` | OTP length to extract |
331
331
  | `hashCode` | `string` | `''` | App hash (send to backend) |
332
332
  | `otp` | `string \| null` | `null` | Extracted OTP |
333
333
  | `sms` | `string \| null` | `null` | Full SMS text |
@@ -348,7 +348,7 @@ Android only. Throws on iOS.
348
348
 
349
349
  Android only. Removes native listeners.
350
350
 
351
- ### `extractOtp(sms: string, numberOfDigits?: 4 | 5 | 6): string | null`
351
+ ### `extractOtp(sms: string, numberOfDigits?: 4 | 5 | 6 | 7 | 8): string | null`
352
352
 
353
353
  Pure helper to extract the OTP from an SMS string.
354
354
 
@@ -100,18 +100,25 @@ class OtpAutoVerifyModule(reactContext: ReactApplicationContext) :
100
100
  }
101
101
 
102
102
  private fun unregisterReceiver() {
103
+ if (!isReceiverRegistered || smsReceiver == null) {
104
+ isListening = false
105
+ return
106
+ }
103
107
  val activity = currentActivity
104
- if (isReceiverRegistered && activity != null && smsReceiver != null) {
105
- try {
108
+ try {
109
+ if (activity != null) {
106
110
  activity.unregisterReceiver(smsReceiver)
107
111
  Log.d(TAG, "SMS receiver unregistered")
108
- } catch (e: Exception) {
109
- Log.e(TAG, "Failed to unregister SMS receiver", e)
110
112
  }
113
+ } catch (e: IllegalArgumentException) {
114
+ Log.w(TAG, "Receiver already unregistered", e)
115
+ } catch (e: Exception) {
116
+ Log.e(TAG, "Failed to unregister SMS receiver", e)
117
+ } finally {
111
118
  isReceiverRegistered = false
112
119
  smsReceiver = null
120
+ isListening = false
113
121
  }
114
- isListening = false
115
122
  }
116
123
 
117
124
  override fun addListener(eventName: String) {
@@ -1,13 +1,25 @@
1
- import { TurboModuleRegistry, type TurboModule } from 'react-native';
2
-
3
- export interface OtpAutoVerifySpec extends TurboModule {
4
- getConstants(): { OTP_RECEIVED_EVENT: string };
5
- getHash(): Promise<ReadonlyArray<string>>;
6
- startSmsRetriever(): Promise<boolean>;
7
- addListener(eventName: string): void;
8
- removeListeners(count: number): void;
9
- }
10
-
11
- export default TurboModuleRegistry.getEnforcing<OtpAutoVerifySpec>(
12
- 'OtpAutoVerify'
13
- );
1
+ import { NativeModules, TurboModuleRegistry, type TurboModule } from 'react-native';
2
+
3
+ export interface OtpAutoVerifySpec extends TurboModule {
4
+ getConstants(): { OTP_RECEIVED_EVENT: string };
5
+ getHash(): Promise<ReadonlyArray<string>>;
6
+ startSmsRetriever(): Promise<boolean>;
7
+ addListener(eventName: string): void;
8
+ removeListeners(count: number): void;
9
+ }
10
+
11
+ function getNativeModule(): OtpAutoVerifySpec {
12
+ try {
13
+ return TurboModuleRegistry.getEnforcing<OtpAutoVerifySpec>('OtpAutoVerify');
14
+ } catch {
15
+ const legacy = NativeModules.OtpAutoVerify;
16
+ if (!legacy) {
17
+ throw new Error(
18
+ 'OtpAutoVerify native module is not available. Ensure the library is properly linked.'
19
+ );
20
+ }
21
+ return legacy as OtpAutoVerifySpec;
22
+ }
23
+ }
24
+
25
+ export default getNativeModule();
@@ -1,29 +1,29 @@
1
1
  "use strict";
2
2
 
3
3
  import * as React from 'react';
4
- import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
4
+ import { NativeEventEmitter, Platform } from 'react-native';
5
5
  import NativeOtpAutoVerify from './NativeOtpAutoVerify';
6
- const {
7
- OtpAutoVerify
8
- } = NativeModules;
9
- const eventEmitter = Platform.OS === 'android' && OtpAutoVerify ? new NativeEventEmitter(OtpAutoVerify) : null;
6
+ const eventEmitter = Platform.OS === 'android' && NativeOtpAutoVerify ? new NativeEventEmitter(NativeOtpAutoVerify) : null;
10
7
  const OTP_RECEIVED_EVENT = NativeOtpAutoVerify.getConstants?.()?.OTP_RECEIVED_EVENT ?? 'otpReceived';
11
- const TIMEOUT_MESSAGE = 'Timeout Error.';
8
+ export const TIMEOUT_MESSAGE = 'Timeout Error.';
12
9
  const DEFAULT_DIGITS = 6;
13
- const OTP_REGEX = {
14
- 4: /\b(\d{4})\b/,
15
- 5: /\b(\d{5})\b/,
16
- 6: /\b(\d{6})\b/
17
- };
10
+ const MIN_OTP_DIGITS = 4;
11
+ const MAX_OTP_DIGITS = 8;
12
+ function getOtpRegex(digits) {
13
+ if (digits < MIN_OTP_DIGITS || digits > MAX_OTP_DIGITS) {
14
+ return new RegExp(`\\b(\\d{${DEFAULT_DIGITS}})\\b`);
15
+ }
16
+ return new RegExp(`\\b(\\d{${digits}})\\b`);
17
+ }
18
18
  /**
19
- * Extracts a numeric OTP of 4, 5, or 6 digits from SMS text.
20
- * Only these lengths are supported (numberOfDigits: 4 | 5 | 6).
19
+ * Extracts a numeric OTP of 4–8 digits from SMS text.
21
20
  */
22
21
  export function extractOtp(sms, numberOfDigits = DEFAULT_DIGITS) {
23
22
  if (!sms || typeof sms !== 'string') return null;
24
23
  const trimmed = sms.trim();
25
24
  if (!trimmed) return null;
26
- const match = trimmed.match(OTP_REGEX[numberOfDigits]);
25
+ const regex = getOtpRegex(numberOfDigits);
26
+ const match = trimmed.match(regex);
27
27
  return match ? match[1] : null;
28
28
  }
29
29
 
@@ -34,10 +34,15 @@ export async function getHash() {
34
34
  return Array.from(arr);
35
35
  }
36
36
 
37
- /** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). */
37
+ /** No-op subscription for platforms where SMS Retriever is not supported (e.g. iOS). */
38
+ const NOOP_SUBSCRIPTION = {
39
+ remove: () => {}
40
+ };
41
+
42
+ /** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). On iOS, returns no-op (call remove() safely). */
38
43
  export async function activateOtpListener(handler, options) {
39
44
  if (Platform.OS !== 'android' || !eventEmitter) {
40
- throw new Error('SMS Retriever is only supported on Android.');
45
+ return NOOP_SUBSCRIPTION;
41
46
  }
42
47
  const numberOfDigits = options?.numberOfDigits ?? DEFAULT_DIGITS;
43
48
  const subscription = eventEmitter.addListener(OTP_RECEIVED_EVENT, (...args) => {
@@ -72,24 +77,30 @@ export function useOtpVerification(options = {}) {
72
77
  const [timeoutError, setTimeoutError] = React.useState(false);
73
78
  const [error, setError] = React.useState(null);
74
79
  const subscriptionRef = React.useRef(null);
80
+ const isStartingRef = React.useRef(false);
75
81
  const stopListening = React.useCallback(() => {
76
82
  subscriptionRef.current?.remove();
77
83
  subscriptionRef.current = null;
84
+ isStartingRef.current = false;
78
85
  removeListener();
79
86
  }, []);
80
87
  const startListening = React.useCallback(async () => {
81
88
  if (Platform.OS !== 'android') return;
89
+ if (isStartingRef.current) return;
90
+ isStartingRef.current = true;
91
+ subscriptionRef.current?.remove();
92
+ subscriptionRef.current = null;
82
93
  setOtp(null);
83
94
  setSms(null);
84
95
  setTimeoutError(false);
85
96
  setError(null);
86
97
  try {
87
- const hashes = await getHash();
88
- setHashCode(hashes[0] ?? '');
89
- } catch (err) {
90
- setError(err instanceof Error ? err : new Error(String(err)));
91
- }
92
- try {
98
+ try {
99
+ const hashes = await getHash();
100
+ setHashCode(hashes[0] ?? '');
101
+ } catch (err) {
102
+ setError(err instanceof Error ? err : new Error(String(err)));
103
+ }
93
104
  const sub = await activateOtpListener((smsText, extractedOtp) => {
94
105
  setSms(smsText);
95
106
  if (smsText === TIMEOUT_MESSAGE) {
@@ -108,6 +119,8 @@ export function useOtpVerification(options = {}) {
108
119
  });
109
120
  setError(wrapped);
110
121
  throw wrapped;
122
+ } finally {
123
+ isStartingRef.current = false;
111
124
  }
112
125
  }, [numberOfDigits]);
113
126
  React.useEffect(() => () => stopListening(), [stopListening]);
@@ -1 +1 @@
1
- {"version":3,"names":["React","NativeEventEmitter","NativeModules","Platform","NativeOtpAutoVerify","OtpAutoVerify","eventEmitter","OS","OTP_RECEIVED_EVENT","getConstants","TIMEOUT_MESSAGE","DEFAULT_DIGITS","OTP_REGEX","extractOtp","sms","numberOfDigits","trimmed","trim","match","getHash","arr","Array","from","activateOtpListener","handler","options","Error","subscription","addListener","args","smsText","String","startSmsRetriever","remove","removeListeners","removeListener","useOtpVerification","hashCode","setHashCode","useState","otp","setOtp","setSms","timeoutError","setTimeoutError","error","setError","subscriptionRef","useRef","stopListening","useCallback","current","startListening","hashes","err","sub","extractedOtp","wrapped","cause","useEffect","useMemo"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","NativeEventEmitter","Platform","NativeOtpAutoVerify","eventEmitter","OS","OTP_RECEIVED_EVENT","getConstants","TIMEOUT_MESSAGE","DEFAULT_DIGITS","MIN_OTP_DIGITS","MAX_OTP_DIGITS","getOtpRegex","digits","RegExp","extractOtp","sms","numberOfDigits","trimmed","trim","regex","match","getHash","arr","Array","from","NOOP_SUBSCRIPTION","remove","activateOtpListener","handler","options","subscription","addListener","args","smsText","String","startSmsRetriever","removeListeners","removeListener","useOtpVerification","hashCode","setHashCode","useState","otp","setOtp","setSms","timeoutError","setTimeoutError","error","setError","subscriptionRef","useRef","isStartingRef","stopListening","useCallback","current","startListening","hashes","err","Error","sub","extractedOtp","wrapped","cause","useEffect","useMemo"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"NativeOtpAutoVerify.d.ts","sourceRoot":"","sources":["../../../src/NativeOtpAutoVerify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,YAAY,IAAI;QAAE,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAED,wBAEE"}
1
+ {"version":3,"file":"NativeOtpAutoVerify.d.ts","sourceRoot":"","sources":["../../../src/NativeOtpAutoVerify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAEpF,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,YAAY,IAAI;QAAE,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAgBD,wBAAiC"}
@@ -1,7 +1,8 @@
1
1
  declare const OTP_RECEIVED_EVENT: string;
2
- export type OtpDigits = 4 | 5 | 6;
2
+ export declare const TIMEOUT_MESSAGE = "Timeout Error.";
3
+ export type OtpDigits = 4 | 5 | 6 | 7 | 8;
3
4
  export interface UseOtpVerificationOptions {
4
- /** Extract OTP with this many digits (4, 5, or 6). OTP is set when SMS is received. */
5
+ /** Extract OTP with this many digits (4–8). OTP is set when SMS is received. */
5
6
  numberOfDigits?: OtpDigits;
6
7
  }
7
8
  export interface UseOtpVerificationResult {
@@ -24,13 +25,12 @@ export interface OtpListenerSubscription {
24
25
  remove: () => void;
25
26
  }
26
27
  /**
27
- * Extracts a numeric OTP of 4, 5, or 6 digits from SMS text.
28
- * Only these lengths are supported (numberOfDigits: 4 | 5 | 6).
28
+ * Extracts a numeric OTP of 4–8 digits from SMS text.
29
29
  */
30
30
  export declare function extractOtp(sms: string, numberOfDigits?: OtpDigits): string | null;
31
31
  /** Returns app hash strings for the current app. Android only; iOS returns []. */
32
32
  export declare function getHash(): Promise<string[]>;
33
- /** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). */
33
+ /** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). On iOS, returns no-op (call remove() safely). */
34
34
  export declare function activateOtpListener(handler: (sms: string, extractedOtp?: string | null) => void, options?: {
35
35
  numberOfDigits?: OtpDigits;
36
36
  }): Promise<OtpListenerSubscription>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAUA,QAAA,MAAM,kBAAkB,QAET,CAAC;AAKhB,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAQlC,MAAM,WAAW,yBAAyB;IACxC,uFAAuF;IACvF,cAAc,CAAC,EAAE,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,mCAAmC;IACnC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,6DAA6D;IAC7D,YAAY,EAAE,OAAO,CAAC;IACtB,oHAAoH;IACpH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,mEAAmE;IACnE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,oDAAoD;IACpD,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,cAAc,GAAE,SAA0B,GACzC,MAAM,GAAG,IAAI,CAMf;AAED,kFAAkF;AAClF,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAIjD;AAED,6FAA6F;AAC7F,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,EAC5D,OAAO,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,SAAS,CAAA;CAAE,GACvC,OAAO,CAAC,uBAAuB,CAAC,CAqBlC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAIrC;AAED,iGAAiG;AACjG,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,CA8D1B;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AASA,QAAA,MAAM,kBAAkB,QAET,CAAC;AAEhB,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAMhD,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAS1C,MAAM,WAAW,yBAAyB;IACxC,gFAAgF;IAChF,cAAc,CAAC,EAAE,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,mCAAmC;IACnC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,6DAA6D;IAC7D,YAAY,EAAE,OAAO,CAAC;IACtB,oHAAoH;IACpH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,mEAAmE;IACnE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,oDAAoD;IACpD,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,cAAc,GAAE,SAA0B,GACzC,MAAM,GAAG,IAAI,CAOf;AAED,kFAAkF;AAClF,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAIjD;AAOD,2IAA2I;AAC3I,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,EAC5D,OAAO,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,SAAS,CAAA;CAAE,GACvC,OAAO,CAAC,uBAAuB,CAAC,CAqBlC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAIrC;AAED,iGAAiG;AACjG,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,CAyE1B;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-otp-auto-verify",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
4
4
  "description": "react-native-otp-auto-verify is a React Native library for automatic OTP verification on Android and iOS. It uses the Google SMS Retriever API to detect OTP codes automatically without requiring the READ_SMS permission. The library is fully Play Store compliant, improves user login experience, supports TurboModule, and works with the React Native New Architecture",
5
5
  "keywords": [
6
6
  "react-native",
@@ -1,13 +1,25 @@
1
- import { TurboModuleRegistry, type TurboModule } from 'react-native';
2
-
3
- export interface OtpAutoVerifySpec extends TurboModule {
4
- getConstants(): { OTP_RECEIVED_EVENT: string };
5
- getHash(): Promise<ReadonlyArray<string>>;
6
- startSmsRetriever(): Promise<boolean>;
7
- addListener(eventName: string): void;
8
- removeListeners(count: number): void;
9
- }
10
-
11
- export default TurboModuleRegistry.getEnforcing<OtpAutoVerifySpec>(
12
- 'OtpAutoVerify'
13
- );
1
+ import { NativeModules, TurboModuleRegistry, type TurboModule } from 'react-native';
2
+
3
+ export interface OtpAutoVerifySpec extends TurboModule {
4
+ getConstants(): { OTP_RECEIVED_EVENT: string };
5
+ getHash(): Promise<ReadonlyArray<string>>;
6
+ startSmsRetriever(): Promise<boolean>;
7
+ addListener(eventName: string): void;
8
+ removeListeners(count: number): void;
9
+ }
10
+
11
+ function getNativeModule(): OtpAutoVerifySpec {
12
+ try {
13
+ return TurboModuleRegistry.getEnforcing<OtpAutoVerifySpec>('OtpAutoVerify');
14
+ } catch {
15
+ const legacy = NativeModules.OtpAutoVerify;
16
+ if (!legacy) {
17
+ throw new Error(
18
+ 'OtpAutoVerify native module is not available. Ensure the library is properly linked.'
19
+ );
20
+ }
21
+ return legacy as OtpAutoVerifySpec;
22
+ }
23
+ }
24
+
25
+ export default getNativeModule();
package/src/index.tsx CHANGED
@@ -1,30 +1,33 @@
1
1
  import * as React from 'react';
2
- import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
2
+ import { NativeEventEmitter, Platform } from 'react-native';
3
3
  import NativeOtpAutoVerify from './NativeOtpAutoVerify';
4
4
 
5
- const { OtpAutoVerify } = NativeModules;
6
5
  const eventEmitter =
7
- Platform.OS === 'android' && OtpAutoVerify
8
- ? new NativeEventEmitter(OtpAutoVerify)
6
+ Platform.OS === 'android' && NativeOtpAutoVerify
7
+ ? new NativeEventEmitter(NativeOtpAutoVerify)
9
8
  : null;
10
9
 
11
10
  const OTP_RECEIVED_EVENT =
12
11
  (NativeOtpAutoVerify.getConstants?.()?.OTP_RECEIVED_EVENT as string) ??
13
12
  'otpReceived';
14
13
 
15
- const TIMEOUT_MESSAGE = 'Timeout Error.';
14
+ export const TIMEOUT_MESSAGE = 'Timeout Error.';
16
15
  const DEFAULT_DIGITS = 6;
17
16
 
18
- export type OtpDigits = 4 | 5 | 6;
17
+ const MIN_OTP_DIGITS = 4;
18
+ const MAX_OTP_DIGITS = 8;
19
19
 
20
- const OTP_REGEX: Record<OtpDigits, RegExp> = {
21
- 4: /\b(\d{4})\b/,
22
- 5: /\b(\d{5})\b/,
23
- 6: /\b(\d{6})\b/,
24
- };
20
+ export type OtpDigits = 4 | 5 | 6 | 7 | 8;
21
+
22
+ function getOtpRegex(digits: number): RegExp {
23
+ if (digits < MIN_OTP_DIGITS || digits > MAX_OTP_DIGITS) {
24
+ return new RegExp(`\\b(\\d{${DEFAULT_DIGITS}})\\b`);
25
+ }
26
+ return new RegExp(`\\b(\\d{${digits}})\\b`);
27
+ }
25
28
 
26
29
  export interface UseOtpVerificationOptions {
27
- /** Extract OTP with this many digits (4, 5, or 6). OTP is set when SMS is received. */
30
+ /** Extract OTP with this many digits (4–8). OTP is set when SMS is received. */
28
31
  numberOfDigits?: OtpDigits;
29
32
  }
30
33
 
@@ -50,8 +53,7 @@ export interface OtpListenerSubscription {
50
53
  }
51
54
 
52
55
  /**
53
- * Extracts a numeric OTP of 4, 5, or 6 digits from SMS text.
54
- * Only these lengths are supported (numberOfDigits: 4 | 5 | 6).
56
+ * Extracts a numeric OTP of 4–8 digits from SMS text.
55
57
  */
56
58
  export function extractOtp(
57
59
  sms: string,
@@ -60,7 +62,8 @@ export function extractOtp(
60
62
  if (!sms || typeof sms !== 'string') return null;
61
63
  const trimmed = sms.trim();
62
64
  if (!trimmed) return null;
63
- const match = trimmed.match(OTP_REGEX[numberOfDigits]);
65
+ const regex = getOtpRegex(numberOfDigits);
66
+ const match = trimmed.match(regex);
64
67
  return match ? match[1]! : null;
65
68
  }
66
69
 
@@ -71,13 +74,18 @@ export async function getHash(): Promise<string[]> {
71
74
  return Array.from(arr);
72
75
  }
73
76
 
74
- /** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). */
77
+ /** No-op subscription for platforms where SMS Retriever is not supported (e.g. iOS). */
78
+ const NOOP_SUBSCRIPTION: OtpListenerSubscription = {
79
+ remove: () => {},
80
+ };
81
+
82
+ /** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). On iOS, returns no-op (call remove() safely). */
75
83
  export async function activateOtpListener(
76
84
  handler: (sms: string, extractedOtp?: string | null) => void,
77
85
  options?: { numberOfDigits?: OtpDigits }
78
86
  ): Promise<OtpListenerSubscription> {
79
87
  if (Platform.OS !== 'android' || !eventEmitter) {
80
- throw new Error('SMS Retriever is only supported on Android.');
88
+ return NOOP_SUBSCRIPTION;
81
89
  }
82
90
 
83
91
  const numberOfDigits = options?.numberOfDigits ?? DEFAULT_DIGITS;
@@ -119,26 +127,35 @@ export function useOtpVerification(
119
127
  const [timeoutError, setTimeoutError] = React.useState(false);
120
128
  const [error, setError] = React.useState<Error | null>(null);
121
129
  const subscriptionRef = React.useRef<OtpListenerSubscription | null>(null);
130
+ const isStartingRef = React.useRef(false);
122
131
 
123
132
  const stopListening = React.useCallback(() => {
124
133
  subscriptionRef.current?.remove();
125
134
  subscriptionRef.current = null;
135
+ isStartingRef.current = false;
126
136
  removeListener();
127
137
  }, []);
128
138
 
129
139
  const startListening = React.useCallback(async () => {
130
140
  if (Platform.OS !== 'android') return;
141
+ if (isStartingRef.current) return;
142
+
143
+ isStartingRef.current = true;
144
+ subscriptionRef.current?.remove();
145
+ subscriptionRef.current = null;
131
146
  setOtp(null);
132
147
  setSms(null);
133
148
  setTimeoutError(false);
134
149
  setError(null);
150
+
135
151
  try {
136
- const hashes = await getHash();
137
- setHashCode(hashes[0] ?? '');
138
- } catch (err) {
139
- setError(err instanceof Error ? err : new Error(String(err)));
140
- }
141
- try {
152
+ try {
153
+ const hashes = await getHash();
154
+ setHashCode(hashes[0] ?? '');
155
+ } catch (err) {
156
+ setError(err instanceof Error ? err : new Error(String(err)));
157
+ }
158
+
142
159
  const sub = await activateOtpListener(
143
160
  (smsText, extractedOtp) => {
144
161
  setSms(smsText);
@@ -156,6 +173,8 @@ export function useOtpVerification(
156
173
  const wrapped = new Error('Failed to start OTP listener', { cause: err });
157
174
  setError(wrapped);
158
175
  throw wrapped;
176
+ } finally {
177
+ isStartingRef.current = false;
159
178
  }
160
179
  }, [numberOfDigits]);
161
180