@sanctum-key/react-native-sdk 1.0.6 → 1.0.7

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.
Files changed (206) hide show
  1. package/README.md +3 -3
  2. package/android/build/.transforms/{f62cb96b2d1f78ca96ab35932dd530dc → c9d62bb333688ab562f51958998d5a48}/transformed/classes/classes_dex/classes.dex +0 -0
  3. package/android/build/generated/source/buildConfig/debug/kyc/{transfergratis → SanctumKey}/com/BuildConfig.java +2 -2
  4. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +1 -1
  5. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +1 -1
  6. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  7. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  8. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +2 -2
  9. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/kyc/SanctumKey/com/BuildConfig.class +0 -0
  10. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +1 -1
  11. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +1 -1
  12. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  13. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +1 -1
  14. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  15. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  16. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  17. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  18. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  19. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  20. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  21. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  22. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  23. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  24. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  25. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  26. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  27. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  28. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  29. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  30. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  45. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  46. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  47. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  48. package/android/build/outputs/logs/manifest-merger-debug-report.txt +1 -1
  49. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  50. package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$1$5$1.class +0 -0
  51. package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$1$5$2.class +0 -0
  52. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$1.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$1.class} +0 -0
  53. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$2.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$2.class} +0 -0
  54. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$3.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$3.class} +0 -0
  55. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$4.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$4.class} +0 -0
  56. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$5.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$5.class} +0 -0
  57. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$6.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$6.class} +0 -0
  58. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class} +0 -0
  59. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class} +0 -0
  60. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class} +0 -0
  61. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$4.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$4.class} +0 -0
  62. package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$View$1.class +0 -0
  63. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$lambda$4$$inlined$Prop$1.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$1.class} +0 -0
  64. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$lambda$4$$inlined$Prop$2.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$2.class} +0 -0
  65. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule.class → SanctumKey/com/SanctumKeySdkModule.class} +0 -0
  66. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkView.class → SanctumKey/com/SanctumKeySdkView.class} +0 -0
  67. package/android/build.gradle +2 -2
  68. package/android/src/main/AndroidManifest.xml +1 -1
  69. package/android/src/main/java/kyc/transfergratis/com/TransfergratisSdkModule.kt +6 -6
  70. package/android/src/main/java/kyc/transfergratis/com/TransfergratisSdkView.kt +2 -2
  71. package/build/package.json +9 -7
  72. package/build/src/App.d.ts +2 -2
  73. package/build/src/App.d.ts.map +1 -1
  74. package/build/src/App.js +2 -2
  75. package/build/src/App.js.map +1 -1
  76. package/build/src/{TransfergratisSdk.types.d.ts → SanctumKeySdk.types.d.ts} +3 -3
  77. package/build/src/SanctumKeySdk.types.d.ts.map +1 -0
  78. package/build/src/SanctumKeySdk.types.js +2 -0
  79. package/build/src/SanctumKeySdk.types.js.map +1 -0
  80. package/build/src/{TransfergratisSdkModule.d.ts → SanctumKeySdkModule.d.ts} +4 -4
  81. package/build/src/SanctumKeySdkModule.d.ts.map +1 -0
  82. package/build/src/{TransfergratisSdkModule.js → SanctumKeySdkModule.js} +2 -2
  83. package/build/src/SanctumKeySdkModule.js.map +1 -0
  84. package/build/src/{TransfergratisSdkModule.web.d.ts → SanctumKeySdkModule.web.d.ts} +4 -4
  85. package/build/src/SanctumKeySdkModule.web.d.ts.map +1 -0
  86. package/build/src/{TransfergratisSdkModule.web.js → SanctumKeySdkModule.web.js} +3 -3
  87. package/build/src/SanctumKeySdkModule.web.js.map +1 -0
  88. package/build/src/SanctumKeySdkView.d.ts +4 -0
  89. package/build/src/SanctumKeySdkView.d.ts.map +1 -0
  90. package/build/src/SanctumKeySdkView.js +7 -0
  91. package/build/src/SanctumKeySdkView.js.map +1 -0
  92. package/build/src/SanctumKeySdkView.web.d.ts +4 -0
  93. package/build/src/SanctumKeySdkView.web.d.ts.map +1 -0
  94. package/build/src/{TransfergratisSdkView.web.js → SanctumKeySdkView.web.js} +2 -2
  95. package/build/src/SanctumKeySdkView.web.js.map +1 -0
  96. package/build/src/api/axios.js +2 -2
  97. package/build/src/api/axios.js.map +1 -1
  98. package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
  99. package/build/src/components/EnhancedCameraView.js +66 -338
  100. package/build/src/components/EnhancedCameraView.js.map +1 -1
  101. package/build/src/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -1
  102. package/build/src/components/KYCElements/EmailVerificationTemplate.js +93 -15
  103. package/build/src/components/KYCElements/EmailVerificationTemplate.js.map +1 -1
  104. package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  105. package/build/src/components/KYCElements/IDCardCapture.js +167 -695
  106. package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
  107. package/build/src/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -1
  108. package/build/src/components/KYCElements/PhoneVerificationTemplate.js +269 -40
  109. package/build/src/components/KYCElements/PhoneVerificationTemplate.js.map +1 -1
  110. package/build/src/components/KYCElements/SelfieCapture.d.ts +1 -1
  111. package/build/src/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  112. package/build/src/components/KYCElements/SelfieCapture.js +130 -192
  113. package/build/src/components/KYCElements/SelfieCapture.js.map +1 -1
  114. package/build/src/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  115. package/build/src/components/KYCElements/SelfieCaptureTemplate.js +131 -433
  116. package/build/src/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  117. package/build/src/components/NativeCameraView.js +1 -1
  118. package/build/src/components/NativeCameraView.js.map +1 -1
  119. package/build/src/components/OverLay/IdCard.d.ts +3 -2
  120. package/build/src/components/OverLay/IdCard.d.ts.map +1 -1
  121. package/build/src/components/OverLay/IdCard.js +149 -141
  122. package/build/src/components/OverLay/IdCard.js.map +1 -1
  123. package/build/src/components/OverLay/SelfieOverlay.d.ts +2 -1
  124. package/build/src/components/OverLay/SelfieOverlay.d.ts.map +1 -1
  125. package/build/src/components/OverLay/SelfieOverlay.js +37 -95
  126. package/build/src/components/OverLay/SelfieOverlay.js.map +1 -1
  127. package/build/src/components/OverLay/type.d.ts +1 -0
  128. package/build/src/components/OverLay/type.d.ts.map +1 -1
  129. package/build/src/components/OverLay/type.js.map +1 -1
  130. package/build/src/components/Svgs/scanningLine.d.ts +2 -1
  131. package/build/src/components/Svgs/scanningLine.d.ts.map +1 -1
  132. package/build/src/components/Svgs/scanningLine.js +55 -51
  133. package/build/src/components/Svgs/scanningLine.js.map +1 -1
  134. package/build/src/config/KYCConfig.js +1 -1
  135. package/build/src/config/KYCConfig.js.map +1 -1
  136. package/build/src/config/allowedDomains.js +6 -6
  137. package/build/src/config/allowedDomains.js.map +1 -1
  138. package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  139. package/build/src/hooks/useTemplateKYCFlow.js +37 -38
  140. package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
  141. package/build/src/index.d.ts +3 -3
  142. package/build/src/index.d.ts.map +1 -1
  143. package/build/src/index.js +3 -3
  144. package/build/src/index.js.map +1 -1
  145. package/build/src/modules/api/CardAuthentification.d.ts +0 -5
  146. package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
  147. package/build/src/modules/api/CardAuthentification.js +114 -116
  148. package/build/src/modules/api/CardAuthentification.js.map +1 -1
  149. package/build/src/modules/api/KYCService.d.ts +11 -1
  150. package/build/src/modules/api/KYCService.d.ts.map +1 -1
  151. package/build/src/modules/api/KYCService.js +101 -38
  152. package/build/src/modules/api/KYCService.js.map +1 -1
  153. package/build/src/modules/camera/NativeCameraModule.js +17 -17
  154. package/build/src/modules/camera/NativeCameraModule.js.map +1 -1
  155. package/expo-module.config.json +2 -2
  156. package/ios/TransfergratisSdk.podspec +2 -2
  157. package/ios/TransfergratisSdkModule.swift +12 -12
  158. package/package.json +9 -7
  159. package/src/App.tsx +2 -2
  160. package/src/{TransfergratisSdk.types.ts → SanctumKeySdk.types.ts} +2 -2
  161. package/src/{TransfergratisSdkModule.ts → SanctumKeySdkModule.ts} +3 -3
  162. package/src/{TransfergratisSdkModule.web.ts → SanctumKeySdkModule.web.ts} +3 -3
  163. package/src/SanctumKeySdkView.tsx +11 -0
  164. package/src/{TransfergratisSdkView.web.tsx → SanctumKeySdkView.web.tsx} +2 -2
  165. package/src/api/axios.ts +2 -2
  166. package/src/components/EnhancedCameraView.tsx +81 -400
  167. package/src/components/KYCElements/EmailVerificationTemplate.tsx +115 -26
  168. package/src/components/KYCElements/IDCardCapture.tsx +228 -868
  169. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +328 -60
  170. package/src/components/KYCElements/SelfieCapture.tsx +184 -213
  171. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +330 -662
  172. package/src/components/NativeCameraView.tsx +1 -1
  173. package/src/components/OverLay/IdCard.tsx +218 -217
  174. package/src/components/OverLay/SelfieOverlay.tsx +56 -134
  175. package/src/components/OverLay/type.ts +1 -0
  176. package/src/components/Svgs/scanningLine.tsx +71 -72
  177. package/src/config/KYCConfig.ts +1 -1
  178. package/src/config/allowedDomains.ts +6 -6
  179. package/src/hooks/useTemplateKYCFlow.tsx +45 -39
  180. package/src/i18n/README.md +1 -1
  181. package/src/index.ts +3 -3
  182. package/src/modules/api/CardAuthentification.ts +202 -200
  183. package/src/modules/api/KYCService.ts +168 -53
  184. package/src/modules/camera/NativeCameraModule.ts +17 -17
  185. package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$1$5$1.class +0 -0
  186. package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$1$5$2.class +0 -0
  187. package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$View$1.class +0 -0
  188. package/build/src/TransfergratisSdk.types.d.ts.map +0 -1
  189. package/build/src/TransfergratisSdk.types.js +0 -2
  190. package/build/src/TransfergratisSdk.types.js.map +0 -1
  191. package/build/src/TransfergratisSdkModule.d.ts.map +0 -1
  192. package/build/src/TransfergratisSdkModule.js.map +0 -1
  193. package/build/src/TransfergratisSdkModule.web.d.ts.map +0 -1
  194. package/build/src/TransfergratisSdkModule.web.js.map +0 -1
  195. package/build/src/TransfergratisSdkView.d.ts +0 -4
  196. package/build/src/TransfergratisSdkView.d.ts.map +0 -1
  197. package/build/src/TransfergratisSdkView.js +0 -7
  198. package/build/src/TransfergratisSdkView.js.map +0 -1
  199. package/build/src/TransfergratisSdkView.web.d.ts +0 -4
  200. package/build/src/TransfergratisSdkView.web.d.ts.map +0 -1
  201. package/build/src/TransfergratisSdkView.web.js.map +0 -1
  202. package/src/TransfergratisSdkView.tsx +0 -11
  203. /package/android/build/.transforms/{532c0e65d82f446633d0a7dab2772198 → ab90740579f5bd05b27b4343ada2d1c9}/results.bin +0 -0
  204. /package/android/build/.transforms/{532c0e65d82f446633d0a7dab2772198 → ab90740579f5bd05b27b4343ada2d1c9}/transformed/classes/classes_dex/classes.dex +0 -0
  205. /package/android/build/.transforms/{f62cb96b2d1f78ca96ab35932dd530dc → c9d62bb333688ab562f51958998d5a48}/results.bin +0 -0
  206. /package/android/build/{intermediates/javac/debug/compileDebugJavaWithJavac/classes/kyc/transfergratis/com/BuildConfig.class → tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/BuildConfig.class.uniqueId0} +0 -0
@@ -1,9 +1,10 @@
1
- import React, { useState } from 'react';
2
- import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert } from 'react-native';
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert, Pressable, Modal, FlatList } from 'react-native';
3
3
  import { TemplateComponent, LocalizedText } from '../../types/KYC.types';
4
4
  import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
5
5
  import { useI18n } from '../../hooks/useI18n';
6
6
  import { Button } from '../ui/Button';
7
+ import kycService, { errorMessage } from '../../modules/api/KYCService';
7
8
 
8
9
  interface PhoneVerificationTemplateProps {
9
10
  component: TemplateComponent;
@@ -14,6 +15,20 @@ interface PhoneVerificationTemplateProps {
14
15
  }
15
16
 
16
17
  type VerificationStep = 'phone' | 'otp';
18
+ const CODE_LENGTH = 6;
19
+
20
+ // Easily expand this list with any countries your app supports
21
+ const COUNTRY_CODES = [
22
+ { code: '+254', label: '🇰🇪 Kenya (+254)' },
23
+ { code: '+255', label: '🇹🇿 Tanzania (+255)' },
24
+ { code: '+256', label: '🇺🇬 Uganda (+256)' },
25
+ { code: '+250', label: '🇷🇼 Rwanda (+250)' },
26
+ { code: '+234', label: '🇳🇬 Nigeria (+234)' },
27
+ { code: '+27', label: '🇿🇦 South Africa (+27)' },
28
+ { code: '+44', label: '🇬🇧 UK (+44)' },
29
+ { code: '+1', label: '🇺🇸 US/Canada (+1)' },
30
+ { code: '+91', label: '🇮🇳 India (+91)' },
31
+ ];
17
32
 
18
33
  export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps> = ({
19
34
  component,
@@ -21,27 +36,43 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
21
36
  onValueChange,
22
37
  error: propError,
23
38
  }) => {
24
- const { actions, getLocalizedText } = useTemplateKYCFlowContext();
39
+ const { actions, getLocalizedText, state, apiKey } = useTemplateKYCFlowContext();
25
40
  const { t } = useI18n();
26
- // const config = component.config as PhoneVerificationConfig;
41
+
42
+ const auth = apiKey ? { apiKey } : (state.session?.token ? { token: state.session.token } : undefined);
43
+ const sessionId = state.session?.session_id || '';
27
44
 
28
45
  // State
29
46
  const [step, setStep] = useState<VerificationStep>('phone');
47
+
48
+ // Split phone state into code and number
49
+ const [countryCode, setCountryCode] = useState('+254');
30
50
  const [phone, setPhone] = useState('');
51
+ const [showCountryPicker, setShowCountryPicker] = useState(false);
52
+
31
53
  const [otp, setOtp] = useState('');
32
54
  const [localError, setLocalError] = useState<string | null>(null);
33
55
  const [isSimulating, setIsSimulating] = useState(false);
34
56
 
57
+ const inputRef = useRef<TextInput>(null);
58
+
35
59
  const title = getLocalizedText(component.labels as LocalizedText);
36
60
  const instructions = getLocalizedText(component.instructions as LocalizedText);
37
61
 
38
- // Determine button text based on step
39
62
  const verifyButtonText = getLocalizedText((component.ui as any).buttonText) || t('common.verify') || 'Verify';
40
63
  const sendButtonText = t('common.sendCode') || 'Send Verification Code';
41
64
  const buttonText = step === 'phone' ? sendButtonText : verifyButtonText;
42
65
 
43
- const handleSendCode = () => {
44
- if (!phone || phone.length < 5) {
66
+ // --- AUTO SUBMIT LOGIC ---
67
+ useEffect(() => {
68
+ if (otp.length === CODE_LENGTH && step === 'otp' && !isSimulating) {
69
+ handleVerifyCode();
70
+ }
71
+ }, [otp]);
72
+
73
+ const handleSendCode = async () => {
74
+ const trimmedPhone = phone.trim();
75
+ if (!trimmedPhone || trimmedPhone.length < 5) {
45
76
  setLocalError(t('errors.invalidPhone') || 'Please enter a valid phone number');
46
77
  return;
47
78
  }
@@ -49,43 +80,58 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
49
80
  setLocalError(null);
50
81
  setIsSimulating(true);
51
82
 
52
- // Simulate API call to send code
53
- setTimeout(() => {
54
- setIsSimulating(false);
83
+ // Combine code and number for the API
84
+ const fullPhoneNumber = `${countryCode}${trimmedPhone}`;
85
+
86
+ try {
87
+ await kycService.sendWhatsAppVerificationCode(sessionId, fullPhoneNumber, auth);
55
88
  setStep('otp');
56
- }, 1500);
89
+ setTimeout(() => inputRef.current?.focus(), 100);
90
+ } catch (err: any) {
91
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send verification code');
92
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
93
+ } finally {
94
+ setIsSimulating(false);
95
+ }
57
96
  };
58
97
 
59
- const handleVerifyCode = () => {
60
- if (!otp || otp.length < 4) {
98
+ const handleVerifyCode = async () => {
99
+ if (!otp || otp.length < CODE_LENGTH) {
61
100
  setLocalError(t('errors.invalidCode') || 'Please enter the 6-digit code');
62
101
  return;
63
102
  }
64
103
 
65
104
  setLocalError(null);
66
105
  setIsSimulating(true);
106
+
107
+ const fullPhoneNumber = `${countryCode}${phone.trim()}`;
67
108
 
68
- // Simulate verification API
69
- setTimeout(() => {
109
+ try {
110
+ await kycService.verifyWhatsAppCode(sessionId, otp.trim(), fullPhoneNumber, auth);
111
+
112
+ const data = { phone: fullPhoneNumber, otp, verified: true };
113
+ onValueChange(data);
114
+ actions.nextComponent(data);
115
+ } catch (err: any) {
116
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.wrongCode') || 'Invalid verification code');
117
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
118
+ setOtp('');
119
+ inputRef.current?.focus();
120
+ } finally {
70
121
  setIsSimulating(false);
71
-
72
- // Mock validation logic
73
- if (otp === '123456') {
74
- onValueChange({ phone, otp, verified: true });
75
- actions.nextComponent();
76
- } else {
77
- setLocalError(t('errors.wrongCode') || 'Invalid verification code. Try 123456');
78
- }
79
- }, 1500);
122
+ }
80
123
  };
81
124
 
82
125
  const onChangePhone = (text: string) => {
83
- setPhone(text);
126
+ // Strip out any non-numeric characters the user might type (like spaces or dashes)
127
+ const cleaned = text.replace(/[^0-9]/g, '');
128
+ setPhone(cleaned);
84
129
  if (localError) setLocalError(null);
85
130
  };
86
131
 
87
132
  const onChangeOtp = (text: string) => {
88
- setOtp(text);
133
+ const cleaned = text.replace(/[^0-9]/g, '');
134
+ setOtp(cleaned);
89
135
  if (localError) setLocalError(null);
90
136
  };
91
137
 
@@ -95,40 +141,86 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
95
141
  setLocalError(null);
96
142
  };
97
143
 
144
+ const renderOtpBoxes = () => {
145
+ const boxes = new Array(CODE_LENGTH).fill(0);
146
+ return (
147
+ <Pressable style={styles.otpBoxesContainer} onPress={() => inputRef.current?.focus()}>
148
+ {boxes.map((_, index) => {
149
+ const digit = otp[index] || '';
150
+ const isCurrent = index === otp.length;
151
+ const isFilled = index < otp.length;
152
+
153
+ return (
154
+ <View
155
+ key={index}
156
+ style={[
157
+ styles.otpBox,
158
+ isCurrent && styles.otpBoxActive,
159
+ isFilled && styles.otpBoxFilled
160
+ ]}
161
+ >
162
+ <Text style={styles.otpBoxText}>{digit}</Text>
163
+ </View>
164
+ );
165
+ })}
166
+ </Pressable>
167
+ );
168
+ };
169
+
98
170
  return (
99
171
  <View style={styles.container}>
100
172
  <Text style={styles.title}>{title}</Text>
101
173
  <Text style={styles.instructions}>
102
- {step === 'phone' ? instructions : (t('kyc.enterCodeSent') || `Please enter the code sent to ${phone}`)}
174
+ {step === 'phone' ? instructions : (t('kyc.enterCodeSent') || `Please enter the code sent to ${countryCode} ${phone}`)}
103
175
  </Text>
104
176
 
105
177
  <View style={styles.contentContainer}>
106
178
  {step === 'phone' ? (
107
179
  <View style={styles.inputContainer}>
108
180
  <Text style={styles.label}>{t('common.phone') || 'Phone Number'}</Text>
109
- <TextInput
110
- style={styles.input}
111
- placeholder="+1234567890"
112
- value={phone}
113
- onChangeText={onChangePhone}
114
- keyboardType="phone-pad"
115
- autoComplete="tel"
116
- editable={!isSimulating}
117
- />
181
+
182
+ {/* 🚨 UPGRADED PHONE INPUT ROW */}
183
+ <View style={styles.phoneInputRow}>
184
+ <TouchableOpacity
185
+ style={styles.countryPickerBtn}
186
+ onPress={() => setShowCountryPicker(true)}
187
+ disabled={isSimulating}
188
+ >
189
+ <Text style={styles.countryPickerText}>{countryCode}</Text>
190
+ <Text style={styles.dropdownIcon}>▼</Text>
191
+ </TouchableOpacity>
192
+
193
+ <TextInput
194
+ style={styles.phoneInput}
195
+ placeholder="712 345 678"
196
+ value={phone}
197
+ onChangeText={onChangePhone}
198
+ keyboardType="phone-pad"
199
+ autoComplete="tel"
200
+ editable={!isSimulating}
201
+ />
202
+ </View>
118
203
  </View>
119
204
  ) : (
120
205
  <View style={styles.inputContainer}>
121
206
  <Text style={styles.label}>{t('common.verificationCode') || 'Verification Code'}</Text>
122
- <TextInput
123
- style={styles.input}
124
- placeholder="123456"
125
- value={otp}
126
- onChangeText={onChangeOtp}
127
- keyboardType="number-pad"
128
- maxLength={6}
129
- editable={!isSimulating}
130
- />
131
- <TouchableOpacity onPress={handleBackToPhone} style={styles.changeLink}>
207
+
208
+ <View style={styles.otpWrapper}>
209
+ {renderOtpBoxes()}
210
+ <TextInput
211
+ ref={inputRef}
212
+ style={styles.hiddenInput}
213
+ value={otp}
214
+ onChangeText={onChangeOtp}
215
+ keyboardType="number-pad"
216
+ maxLength={CODE_LENGTH}
217
+ editable={!isSimulating}
218
+ textContentType="oneTimeCode"
219
+ caretHidden={true}
220
+ />
221
+ </View>
222
+
223
+ <TouchableOpacity onPress={handleBackToPhone} style={styles.changeLink} disabled={isSimulating}>
132
224
  <Text style={styles.changeText}>{t('common.back') || 'Change number'}</Text>
133
225
  </TouchableOpacity>
134
226
  </View>
@@ -144,18 +236,29 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
144
236
  style={styles.button}
145
237
  disabled={
146
238
  isSimulating ||
147
- (step === 'phone' ? !phone : !otp)
239
+ (step === 'phone' ? !phone : otp.length < CODE_LENGTH)
148
240
  }
149
241
  />
150
242
 
151
243
  {step === 'otp' && (
152
244
  <TouchableOpacity
153
- onPress={() => {
154
- // Resend logic
155
- Alert.alert(
156
- t('common.codeResent') || 'Code Resent',
157
- t('common.codeResentMessage', { email: phone }) || 'Code resent to ' + phone
158
- );
245
+ onPress={async () => {
246
+ if (isSimulating) return;
247
+ setLocalError(null);
248
+ setIsSimulating(true);
249
+ const fullPhoneNumber = `${countryCode}${phone.trim()}`;
250
+ try {
251
+ await kycService.sendWhatsAppVerificationCode(sessionId, fullPhoneNumber, auth);
252
+ Alert.alert(
253
+ t('common.codeResent') || 'Code Resent',
254
+ t('common.codeResentMessage', { email: fullPhoneNumber }) || 'Code resent to ' + fullPhoneNumber
255
+ );
256
+ } catch (err: any) {
257
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send code');
258
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
259
+ } finally {
260
+ setIsSimulating(false);
261
+ }
159
262
  }}
160
263
  style={styles.resendButton}
161
264
  disabled={isSimulating}
@@ -164,6 +267,45 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
164
267
  </TouchableOpacity>
165
268
  )}
166
269
  </View>
270
+
271
+ {/* 🚨 COUNTRY PICKER MODAL */}
272
+ <Modal
273
+ visible={showCountryPicker}
274
+ animationType="slide"
275
+ transparent={true}
276
+ onRequestClose={() => setShowCountryPicker(false)}
277
+ >
278
+ <TouchableOpacity
279
+ style={styles.modalOverlay}
280
+ activeOpacity={1}
281
+ onPress={() => setShowCountryPicker(false)}
282
+ >
283
+ <View style={styles.modalContent}>
284
+ <View style={styles.modalHeader}>
285
+ <Text style={styles.modalTitle}>Select Country</Text>
286
+ <TouchableOpacity onPress={() => setShowCountryPicker(false)}>
287
+ <Text style={styles.modalClose}>✕</Text>
288
+ </TouchableOpacity>
289
+ </View>
290
+ <FlatList
291
+ data={COUNTRY_CODES}
292
+ keyExtractor={(item) => item.code}
293
+ renderItem={({ item }) => (
294
+ <TouchableOpacity
295
+ style={styles.countryItem}
296
+ onPress={() => {
297
+ setCountryCode(item.code);
298
+ setShowCountryPicker(false);
299
+ }}
300
+ >
301
+ <Text style={styles.countryItemLabel}>{item.label}</Text>
302
+ {countryCode === item.code && <Text style={styles.checkMark}>✓</Text>}
303
+ </TouchableOpacity>
304
+ )}
305
+ />
306
+ </View>
307
+ </TouchableOpacity>
308
+ </Modal>
167
309
  </View>
168
310
  );
169
311
  };
@@ -195,9 +337,7 @@ const styles = StyleSheet.create({
195
337
  lineHeight: 24,
196
338
  textAlign: 'center',
197
339
  },
198
- contentContainer: {
199
- // width: '100%',
200
- },
340
+ contentContainer: {},
201
341
  inputContainer: {
202
342
  marginBottom: 24,
203
343
  },
@@ -208,7 +348,37 @@ const styles = StyleSheet.create({
208
348
  marginBottom: 8,
209
349
  marginLeft: 4,
210
350
  },
211
- input: {
351
+
352
+ // --- Phone Row Styles ---
353
+ phoneInputRow: {
354
+ flexDirection: 'row',
355
+ alignItems: 'center',
356
+ gap: 10, // Space between picker and input
357
+ },
358
+ countryPickerBtn: {
359
+ flexDirection: 'row',
360
+ alignItems: 'center',
361
+ justifyContent: 'space-between',
362
+ borderWidth: 1,
363
+ borderColor: '#e0e0e0',
364
+ paddingHorizontal: 12,
365
+ paddingVertical: 16,
366
+ borderRadius: 12,
367
+ backgroundColor: '#f8f9fa',
368
+ minWidth: 90,
369
+ },
370
+ countryPickerText: {
371
+ fontSize: 16,
372
+ color: '#333',
373
+ fontWeight: '600',
374
+ },
375
+ dropdownIcon: {
376
+ fontSize: 12,
377
+ color: '#666',
378
+ marginLeft: 8,
379
+ },
380
+ phoneInput: {
381
+ flex: 1, // Takes up remaining space
212
382
  borderWidth: 1,
213
383
  borderColor: '#e0e0e0',
214
384
  padding: 16,
@@ -217,6 +387,104 @@ const styles = StyleSheet.create({
217
387
  backgroundColor: '#f8f9fa',
218
388
  color: '#333',
219
389
  },
390
+
391
+ // --- Country Modal Styles ---
392
+ modalOverlay: {
393
+ flex: 1,
394
+ backgroundColor: 'rgba(0,0,0,0.5)',
395
+ justifyContent: 'flex-end',
396
+ },
397
+ modalContent: {
398
+ backgroundColor: 'white',
399
+ borderTopLeftRadius: 24,
400
+ borderTopRightRadius: 24,
401
+ maxHeight: '60%',
402
+ paddingBottom: 40,
403
+ },
404
+ modalHeader: {
405
+ flexDirection: 'row',
406
+ justifyContent: 'space-between',
407
+ alignItems: 'center',
408
+ padding: 20,
409
+ borderBottomWidth: 1,
410
+ borderBottomColor: '#f0f0f0',
411
+ },
412
+ modalTitle: {
413
+ fontSize: 18,
414
+ fontWeight: '700',
415
+ color: '#1a1a1a',
416
+ },
417
+ modalClose: {
418
+ fontSize: 20,
419
+ color: '#666',
420
+ padding: 5,
421
+ },
422
+ countryItem: {
423
+ flexDirection: 'row',
424
+ justifyContent: 'space-between',
425
+ alignItems: 'center',
426
+ paddingVertical: 16,
427
+ paddingHorizontal: 24,
428
+ borderBottomWidth: 1,
429
+ borderBottomColor: '#f8f9fa',
430
+ },
431
+ countryItemLabel: {
432
+ fontSize: 16,
433
+ color: '#333',
434
+ },
435
+ checkMark: {
436
+ color: '#2DBD60',
437
+ fontSize: 18,
438
+ fontWeight: 'bold',
439
+ },
440
+
441
+ // --- OTP Styles ---
442
+ otpWrapper: {
443
+ position: 'relative',
444
+ width: '100%',
445
+ height: 60,
446
+ },
447
+ otpBoxesContainer: {
448
+ flexDirection: 'row',
449
+ justifyContent: 'space-between',
450
+ alignItems: 'center',
451
+ width: '100%',
452
+ height: '100%',
453
+ },
454
+ otpBox: {
455
+ width: '14%',
456
+ aspectRatio: 1,
457
+ borderWidth: 1,
458
+ borderColor: '#e0e0e0',
459
+ borderRadius: 12,
460
+ backgroundColor: '#f8f9fa',
461
+ justifyContent: 'center',
462
+ alignItems: 'center',
463
+ },
464
+ otpBoxFilled: {
465
+ borderColor: '#9ca3af',
466
+ backgroundColor: '#ffffff',
467
+ },
468
+ otpBoxActive: {
469
+ borderColor: '#2DBD60',
470
+ borderWidth: 2,
471
+ backgroundColor: '#ffffff',
472
+ },
473
+ otpBoxText: {
474
+ fontSize: 24,
475
+ fontWeight: '700',
476
+ color: '#1a1a1a',
477
+ },
478
+ hiddenInput: {
479
+ position: 'absolute',
480
+ top: 0,
481
+ left: 0,
482
+ width: '100%',
483
+ height: '100%',
484
+ opacity: 0,
485
+ },
486
+
487
+ // --- General Styles ---
220
488
  errorText: {
221
489
  color: '#dc2626',
222
490
  marginBottom: 16,
@@ -230,11 +498,11 @@ const styles = StyleSheet.create({
230
498
  button: {
231
499
  height: 50,
232
500
  borderRadius: 12,
233
- width: "100%"
501
+ width: "100%",
234
502
  },
235
503
  changeLink: {
236
504
  alignSelf: 'flex-end',
237
- marginTop: 8,
505
+ marginTop: 12,
238
506
  },
239
507
  changeText: {
240
508
  color: '#2DBD60',
@@ -250,4 +518,4 @@ const styles = StyleSheet.create({
250
518
  fontSize: 14,
251
519
  textDecorationLine: 'underline',
252
520
  },
253
- });
521
+ });