@transfergratis/react-native-sdk 0.1.23 → 0.1.25

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 (207) hide show
  1. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +12 -5
  2. package/android/build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar +0 -0
  3. package/android/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt +0 -0
  4. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
  5. package/android/build/intermediates/incremental/debug-mergeJavaRes/merge-state +0 -0
  6. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +61 -59
  7. package/android/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-transfergratis-react-native-sdk.jar +0 -0
  8. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +12 -5
  9. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  10. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  11. package/android/build/outputs/aar/transfergratis-react-native-sdk-debug.aar +0 -0
  12. package/android/build/outputs/logs/manifest-merger-debug-report.txt +26 -34
  13. package/android/src/main/AndroidManifest.xml +13 -5
  14. package/build/components/EnhancedCameraView.d.ts.map +1 -1
  15. package/build/components/EnhancedCameraView.js +26 -3
  16. package/build/components/EnhancedCameraView.js.map +1 -1
  17. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  18. package/build/components/EnhancedCameraView.web.js +21 -0
  19. package/build/components/EnhancedCameraView.web.js.map +1 -1
  20. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts +12 -0
  21. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts.map +1 -0
  22. package/build/components/KYCElements/AdditionalDocumentsTemplate.js +283 -0
  23. package/build/components/KYCElements/AdditionalDocumentsTemplate.js.map +1 -0
  24. package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
  25. package/build/components/KYCElements/CameraCapture.js +4 -3
  26. package/build/components/KYCElements/CameraCapture.js.map +1 -1
  27. package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
  28. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  29. package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
  30. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  31. package/build/components/KYCElements/EmailVerificationTemplate.d.ts +12 -0
  32. package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -0
  33. package/build/components/KYCElements/EmailVerificationTemplate.js +193 -0
  34. package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -0
  35. package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
  36. package/build/components/KYCElements/FileUpload.js +5 -4
  37. package/build/components/KYCElements/FileUpload.js.map +1 -1
  38. package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
  39. package/build/components/KYCElements/FileUploadTemplate.js +5 -4
  40. package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
  41. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  42. package/build/components/KYCElements/IDCardCapture.js +356 -227
  43. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  44. package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  45. package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
  46. package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  47. package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -0
  48. package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
  49. package/build/components/KYCElements/OrientationVideoCapture.js +5 -4
  50. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  51. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -0
  52. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
  53. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +5 -4
  54. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  55. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -0
  56. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
  57. package/build/components/KYCElements/OrientationVideoCaptureFinal.js +5 -4
  58. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  59. package/build/components/KYCElements/PersonalInformationTemplate.d.ts +12 -0
  60. package/build/components/KYCElements/PersonalInformationTemplate.d.ts.map +1 -0
  61. package/build/components/KYCElements/PersonalInformationTemplate.js +120 -0
  62. package/build/components/KYCElements/PersonalInformationTemplate.js.map +1 -0
  63. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts +12 -0
  64. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -0
  65. package/build/components/KYCElements/PhoneVerificationTemplate.js +185 -0
  66. package/build/components/KYCElements/PhoneVerificationTemplate.js.map +1 -0
  67. package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  68. package/build/components/KYCElements/SelfieCapture.js +4 -3
  69. package/build/components/KYCElements/SelfieCapture.js.map +1 -1
  70. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  71. package/build/components/KYCElements/SelfieCaptureTemplate.js +189 -42
  72. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  73. package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
  74. package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
  75. package/build/components/KYCElements/WelcomeTemplate.js +243 -0
  76. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
  77. package/build/components/TemplateKYCExample.d.ts +8 -2
  78. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  79. package/build/components/TemplateKYCExample.js +10 -97
  80. package/build/components/TemplateKYCExample.js.map +1 -1
  81. package/build/components/TemplateKYCFlowRefactored.d.ts +6 -1
  82. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  83. package/build/components/TemplateKYCFlowRefactored.js +108 -11
  84. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  85. package/build/components/example/DynamicTemplateExample.d.ts +10 -0
  86. package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
  87. package/build/components/example/DynamicTemplateExample.js +241 -0
  88. package/build/components/example/DynamicTemplateExample.js.map +1 -0
  89. package/build/config/KYCConfig.d.ts +14 -0
  90. package/build/config/KYCConfig.d.ts.map +1 -0
  91. package/build/config/KYCConfig.js +26 -0
  92. package/build/config/KYCConfig.js.map +1 -0
  93. package/build/config/allowedDomains.d.ts +30 -0
  94. package/build/config/allowedDomains.d.ts.map +1 -0
  95. package/build/config/allowedDomains.js +112 -0
  96. package/build/config/allowedDomains.js.map +1 -0
  97. package/build/hooks/useOrientationVideo.d.ts +2 -1
  98. package/build/hooks/useOrientationVideo.d.ts.map +1 -1
  99. package/build/hooks/useOrientationVideo.js +3 -3
  100. package/build/hooks/useOrientationVideo.js.map +1 -1
  101. package/build/hooks/useTemplateKYCFlow.d.ts +6 -1
  102. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  103. package/build/hooks/useTemplateKYCFlow.js +317 -34
  104. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  105. package/build/hooks/useTemplateLoader.d.ts +14 -0
  106. package/build/hooks/useTemplateLoader.d.ts.map +1 -0
  107. package/build/hooks/useTemplateLoader.js +85 -0
  108. package/build/hooks/useTemplateLoader.js.map +1 -0
  109. package/build/i18n/en/index.d.ts +49 -0
  110. package/build/i18n/en/index.d.ts.map +1 -1
  111. package/build/i18n/en/index.js +50 -1
  112. package/build/i18n/en/index.js.map +1 -1
  113. package/build/i18n/fr/index.d.ts +35 -0
  114. package/build/i18n/fr/index.d.ts.map +1 -1
  115. package/build/i18n/fr/index.js +36 -1
  116. package/build/i18n/fr/index.js.map +1 -1
  117. package/build/index.d.ts +6 -0
  118. package/build/index.d.ts.map +1 -1
  119. package/build/index.js +10 -0
  120. package/build/index.js.map +1 -1
  121. package/build/modules/api/CardAuthentification.d.ts +24 -3
  122. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  123. package/build/modules/api/CardAuthentification.js +69 -10
  124. package/build/modules/api/CardAuthentification.js.map +1 -1
  125. package/build/modules/api/KYCService.d.ts +7 -7
  126. package/build/modules/api/KYCService.d.ts.map +1 -1
  127. package/build/modules/api/KYCService.js +108 -39
  128. package/build/modules/api/KYCService.js.map +1 -1
  129. package/build/modules/api/SelfieVerification.d.ts +3 -1
  130. package/build/modules/api/SelfieVerification.d.ts.map +1 -1
  131. package/build/modules/api/SelfieVerification.js +17 -1
  132. package/build/modules/api/SelfieVerification.js.map +1 -1
  133. package/build/modules/api/TemplateService.d.ts +44 -0
  134. package/build/modules/api/TemplateService.d.ts.map +1 -0
  135. package/build/modules/api/TemplateService.js +145 -0
  136. package/build/modules/api/TemplateService.js.map +1 -0
  137. package/build/types/KYC.types.d.ts +265 -4
  138. package/build/types/KYC.types.d.ts.map +1 -1
  139. package/build/types/KYC.types.js +15 -0
  140. package/build/types/KYC.types.js.map +1 -1
  141. package/build/types/env.types.d.ts +13 -0
  142. package/build/types/env.types.d.ts.map +1 -0
  143. package/build/types/env.types.js +2 -0
  144. package/build/types/env.types.js.map +1 -0
  145. package/build/utils/cropByObb.d.ts +1 -0
  146. package/build/utils/cropByObb.d.ts.map +1 -1
  147. package/build/utils/cropByObb.js +70 -0
  148. package/build/utils/cropByObb.js.map +1 -1
  149. package/build/utils/deviceDetection.d.ts +6 -0
  150. package/build/utils/deviceDetection.d.ts.map +1 -0
  151. package/build/utils/deviceDetection.js +12 -0
  152. package/build/utils/deviceDetection.js.map +1 -0
  153. package/build/utils/platformAlert.d.ts +20 -0
  154. package/build/utils/platformAlert.d.ts.map +1 -0
  155. package/build/utils/platformAlert.js +67 -0
  156. package/build/utils/platformAlert.js.map +1 -0
  157. package/build/utils/template-transformer.d.ts +10 -0
  158. package/build/utils/template-transformer.d.ts.map +1 -0
  159. package/build/utils/template-transformer.js +365 -0
  160. package/build/utils/template-transformer.js.map +1 -0
  161. package/build/web/WebKYCEntry.d.ts.map +1 -1
  162. package/build/web/WebKYCEntry.js +158 -32
  163. package/build/web/WebKYCEntry.js.map +1 -1
  164. package/package.json +1 -1
  165. package/plugin/build/withVisionCamera.js +3 -4
  166. package/plugin/src/withVisionCamera.js +3 -4
  167. package/plugin/src/withVisionCamera.ts +3 -4
  168. package/src/components/EnhancedCameraView.tsx +31 -2
  169. package/src/components/EnhancedCameraView.web.tsx +24 -0
  170. package/src/components/KYCElements/AdditionalDocumentsTemplate.tsx +346 -0
  171. package/src/components/KYCElements/CameraCapture.tsx +4 -3
  172. package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
  173. package/src/components/KYCElements/EmailVerificationTemplate.tsx +264 -0
  174. package/src/components/KYCElements/FileUpload.tsx +5 -4
  175. package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
  176. package/src/components/KYCElements/IDCardCapture.tsx +397 -254
  177. package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
  178. package/src/components/KYCElements/OrientationVideoCapture.tsx +6 -3
  179. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +6 -3
  180. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +6 -3
  181. package/src/components/KYCElements/PersonalInformationTemplate.tsx +158 -0
  182. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +253 -0
  183. package/src/components/KYCElements/SelfieCapture.tsx +4 -3
  184. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +201 -44
  185. package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
  186. package/src/components/TemplateKYCExample.tsx +37 -108
  187. package/src/components/TemplateKYCFlowRefactored.tsx +148 -12
  188. package/src/components/example/DynamicTemplateExample.tsx +289 -0
  189. package/src/config/KYCConfig.ts +34 -0
  190. package/src/config/allowedDomains.ts +133 -0
  191. package/src/hooks/useOrientationVideo.ts +5 -4
  192. package/src/hooks/useTemplateKYCFlow.tsx +347 -32
  193. package/src/hooks/useTemplateLoader.ts +102 -0
  194. package/src/i18n/en/index.ts +53 -2
  195. package/src/i18n/fr/index.ts +37 -1
  196. package/src/index.ts +14 -0
  197. package/src/modules/api/CardAuthentification.ts +76 -11
  198. package/src/modules/api/KYCService.ts +129 -45
  199. package/src/modules/api/SelfieVerification.ts +25 -3
  200. package/src/modules/api/TemplateService.ts +167 -0
  201. package/src/types/KYC.types.ts +331 -3
  202. package/src/types/env.types.ts +13 -0
  203. package/src/utils/cropByObb.ts +83 -3
  204. package/src/utils/deviceDetection.ts +11 -0
  205. package/src/utils/platformAlert.ts +86 -0
  206. package/src/utils/template-transformer.ts +445 -0
  207. package/src/web/WebKYCEntry.tsx +199 -50
@@ -0,0 +1,86 @@
1
+ import { Platform, Alert } from 'react-native';
2
+
3
+ export interface AlertButton {
4
+ text?: string;
5
+ onPress?: () => void;
6
+ style?: 'default' | 'cancel' | 'destructive';
7
+ }
8
+
9
+ /**
10
+ * Platform-aware alert helper that works on both native and web platforms
11
+ * On web, uses window.confirm/window.alert since Alert.alert is not supported
12
+ * On native, uses the standard Alert.alert
13
+ */
14
+ export const showAlert = (
15
+ title: string,
16
+ message?: string,
17
+ buttons?: AlertButton[]
18
+ ): void => {
19
+ if (Platform.OS === 'web') {
20
+ // Web implementation using native browser dialogs
21
+ const fullMessage = message ? `${title}\n\n${message}` : title;
22
+
23
+ if (buttons && buttons.length > 1) {
24
+
25
+ // Multiple buttons - use confirm dialog
26
+ const confirmed = window.confirm(fullMessage);
27
+
28
+ if (confirmed) {
29
+ // Find and call the primary/positive button callback
30
+ const confirmButton = buttons.find(
31
+ btn => btn.style !== 'cancel' && btn.onPress
32
+ );
33
+ confirmButton?.onPress?.();
34
+ } else {
35
+ // Find and call the cancel button callback
36
+ const cancelButton = buttons.find(btn => btn.style === 'cancel');
37
+ cancelButton?.onPress?.();
38
+ }
39
+ } else if (buttons && buttons.length === 1) {
40
+ // Single button - use alert dialog
41
+ window.alert(fullMessage);
42
+ buttons[0]?.onPress?.();
43
+ } else {
44
+ // No buttons - just show alert
45
+ window.alert(fullMessage);
46
+ }
47
+ } else {
48
+ // Native implementation - use standard Alert.alert
49
+ Alert.alert(title, message, buttons);
50
+ }
51
+ };
52
+
53
+ /**
54
+ * Simplified confirm dialog for yes/no scenarios
55
+ */
56
+ export const showConfirm = (
57
+ title: string,
58
+ message: string,
59
+ onConfirm: () => void,
60
+ onCancel?: () => void
61
+ ): void => {
62
+ showAlert(title, message, [
63
+ {
64
+ text: 'Cancel',
65
+ style: 'cancel',
66
+ onPress: onCancel,
67
+ },
68
+ {
69
+ text: 'OK',
70
+ onPress: onConfirm,
71
+ },
72
+ ]);
73
+ };
74
+
75
+ /**
76
+ * Simple alert with just an OK button
77
+ */
78
+ export const showSimpleAlert = (title: string, message?: string, onPress?: () => void): void => {
79
+ showAlert(title, message, [
80
+ {
81
+ text: 'OK',
82
+ onPress,
83
+ },
84
+ ]);
85
+ };
86
+
@@ -0,0 +1,445 @@
1
+ import {
2
+ BackendKYCTemplate,
3
+ BackendTemplateComponent,
4
+ KYCTemplate,
5
+ TemplateComponent,
6
+ LocalizedText,
7
+ ComponentUI,
8
+ IDCardConfig,
9
+ LocationConfig,
10
+ SelfieConfig,
11
+ FileUploadConfig,
12
+ CountrySelectionConfig,
13
+ ComponentConfig,
14
+ WelcomeConfig,
15
+ GovernmentDocumentType,
16
+ GovernmentIdConfig,
17
+ } from '../types/KYC.types';
18
+ import { logger } from './logger';
19
+
20
+ /**
21
+ * Hash a string to a number (simple hash function)
22
+ */
23
+ function hashStringToNumber(str: string): number {
24
+ let hash = 0;
25
+ for (let i = 0; i < str.length; i++) {
26
+ const char = str.charCodeAt(i);
27
+ hash = ((hash << 5) - hash) + char;
28
+ hash = hash & hash; // Convert to 32-bit integer
29
+ }
30
+ return Math.abs(hash);
31
+ }
32
+
33
+ /**
34
+ * Map backend component types to SDK component types
35
+ */
36
+ const COMPONENT_TYPE_MAPPING: Record<string, TemplateComponent['type']> = {
37
+ 'welcome': 'welcome',
38
+ 'government-id': 'id_card',
39
+ 'selfie-capture': 'selfie',
40
+ 'location-capture': 'location',
41
+ 'review-submit': 'review_submit',
42
+ 'verification-progress': 'verification_progress',
43
+ 'file-upload': 'file_upload',
44
+ 'country-selection': 'country_selection',
45
+ 'email-verification': 'email_verification',
46
+ 'phone-verification': 'phone_verification',
47
+ 'personal-information': 'personal_information',
48
+ 'additional-documents': 'additional_documents',
49
+ };
50
+
51
+ /**
52
+ * Transform backend translations to SDK labels and instructions
53
+ */
54
+ function transformTranslations(
55
+ translations: BackendTemplateComponent['translations'],
56
+ defaultLanguage: string = 'en'
57
+ ): { labels: LocalizedText; instructions: LocalizedText } {
58
+ const labels: LocalizedText = { en: '', fr: '' };
59
+ const instructions: LocalizedText = { en: '', fr: '' };
60
+
61
+ // Extract title as label
62
+ if (translations.en?.title) labels.en = translations.en.title;
63
+ if (translations.fr?.title) labels.fr = translations.fr.title;
64
+
65
+ // Extract instructions
66
+ if (translations.en?.instructions) instructions.en = translations.en.instructions;
67
+ if (translations.fr?.instructions) instructions.fr = translations.fr.instructions;
68
+
69
+ // Fallback to description if instructions not available
70
+ if (!instructions.en && translations.en?.description) {
71
+ instructions.en = translations.en.description;
72
+ }
73
+ if (!instructions.fr && translations.fr?.description) {
74
+ instructions.fr = translations.fr.description;
75
+ }
76
+
77
+ // Support other languages
78
+ Object.keys(translations).forEach((lang) => {
79
+ if (lang !== 'en' && lang !== 'fr' && translations[lang]) {
80
+ labels[lang] = translations[lang]?.title || '';
81
+ instructions[lang] = translations[lang]?.instructions || translations[lang]?.description || '';
82
+ }
83
+ });
84
+
85
+ return { labels, instructions };
86
+ }
87
+
88
+ /**
89
+ * Transform UI configuration
90
+ */
91
+ function transformUI(translations: BackendTemplateComponent['translations']): ComponentUI {
92
+ const buttonText: LocalizedText = { en: '', fr: '' };
93
+
94
+ if (translations.en?.buttonText) buttonText.en = translations.en.buttonText;
95
+ if (translations.fr?.buttonText) buttonText.fr = translations.fr.buttonText;
96
+
97
+ // Support other languages
98
+ Object.keys(translations).forEach((lang) => {
99
+ if (lang !== 'en' && lang !== 'fr' && translations[lang]?.buttonText) {
100
+ buttonText[lang] = translations[lang]!.buttonText!;
101
+ }
102
+ });
103
+
104
+ return {
105
+ buttonText,
106
+ themeColor: '#2DBD60', // Default theme color
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Transform welcome config
112
+ */
113
+ function transformWelcomeConfig(config: any): WelcomeConfig {
114
+ return {
115
+ subtitle: config.subtitle,
116
+ buttonText: config.buttonText,
117
+ requirements: config.requirements || [],
118
+ estimatedTime: config.estimatedTime,
119
+ consentOptions: config.consentOptions || {
120
+ showPrivacyPolicy: true,
121
+ showTermsOfService: true,
122
+ showMarketingConsent: false,
123
+ },
124
+ welcomeMessage: config.welcomeMessage,
125
+ showEstimatedTime: config.showEstimatedTime !== false,
126
+ } as WelcomeConfig;
127
+ }
128
+
129
+ /**
130
+ * Transform government ID config to IDCardConfig
131
+ */
132
+ function transformGovernmentIdConfig(config: any): IDCardConfig {
133
+ const documentTypes: string[] = config.documentTypes || ['passport', 'nationalId'];
134
+ const sides: string[] = [];
135
+
136
+ if (config.requiredSides === 'front-back' || config.requiredSides === 'both') {
137
+ sides.push('front', 'back');
138
+ } else if (config.requiredSides === 'front') {
139
+ sides.push('front');
140
+ } else if (config.requiredSides === 'back') {
141
+ sides.push('back');
142
+ } else {
143
+ // Default to both sides
144
+ sides.push('front', 'back');
145
+ }
146
+
147
+ const idCardConfig: IDCardConfig = {
148
+ sides,
149
+ allowed_formats: ['jpg', 'jpeg', 'png'],
150
+ max_size_mb: 10,
151
+ document_types: documentTypes.map((dt: string) => {
152
+ const mapping: Record<string, any> = {
153
+ 'passport': 'passport',
154
+ 'nationalId': 'national_id',
155
+ 'driversLicense': 'drivers_licence',
156
+ 'identityCard': 'identity_card',
157
+ };
158
+ return mapping[dt] || dt;
159
+ }) as any[],
160
+ };
161
+
162
+ // Add bbox configs if available
163
+ if (config.cameraOverlay?.bbox) {
164
+ const bbox = config.cameraOverlay.bbox;
165
+ idCardConfig.bbox_configs = {} as Record<GovernmentDocumentType, {
166
+ xMin: number;
167
+ yMin: number;
168
+ xMax: number;
169
+ yMax: number;
170
+ borderColor?: string;
171
+ borderWidth?: number;
172
+ cornerRadius?: number;
173
+ }>;
174
+ documentTypes.forEach((dt: string) => {
175
+ const docType = dt as GovernmentDocumentType;
176
+ (idCardConfig.bbox_configs as any)[docType] = {
177
+ xMin: bbox.xMin || 20,
178
+ yMin: bbox.yMin || 140,
179
+ xMax: bbox.xMax || 370,
180
+ yMax: bbox.yMax || 340,
181
+ borderColor: bbox.borderColor || '#2DBD60',
182
+ borderWidth: bbox.borderWidth || 3,
183
+ cornerRadius: bbox.cornerRadius || 8,
184
+ };
185
+ });
186
+ }
187
+
188
+ // Store additional backend-specific config in a way that can be accessed
189
+ (idCardConfig as any).authenticationMethods = config.authenticationMethods;
190
+ (idCardConfig as any).documentTypesByCountry = config.documentTypesByCountry;
191
+ (idCardConfig as any).selectedCountries = config.selectedCountries;
192
+ (idCardConfig as any).instructionsByDocumentType = config.instructionsByDocumentType;
193
+ (idCardConfig as any).cameraSettings = config.cameraSettings;
194
+ (idCardConfig as any).validationRules = config.validationRules;
195
+
196
+ return idCardConfig;
197
+ }
198
+
199
+ /**
200
+ * Transform location capture config
201
+ */
202
+ function transformLocationConfig(config: any): LocationConfig {
203
+ return {
204
+ accuracy: config.accuracy || 'high',
205
+ required: config.required !== false,
206
+ } as LocationConfig;
207
+ }
208
+
209
+ /**
210
+ * Transform selfie capture config
211
+ */
212
+ function transformSelfieConfig(config: any): SelfieConfig {
213
+ return {
214
+ liveness_check: config.livenessEnabled !== false,
215
+ max_attempts: config.maxAttempts || 5,
216
+ orientations: config.orientations || ['center'],
217
+ } as SelfieConfig;
218
+ }
219
+
220
+ /**
221
+ * Transform component config based on type
222
+ */
223
+ function transformComponentConfig(
224
+ type: TemplateComponent['type'],
225
+ backendConfig: any
226
+ ): ComponentConfig {
227
+ switch (type) {
228
+ case 'welcome':
229
+ return transformWelcomeConfig(backendConfig);
230
+ case 'id_card':
231
+ return transformGovernmentIdConfig(backendConfig);
232
+ case 'location':
233
+ return transformLocationConfig(backendConfig);
234
+ case 'selfie':
235
+ return transformSelfieConfig(backendConfig);
236
+ case 'file_upload':
237
+ return {
238
+ allowed_formats: backendConfig.allowed_formats || ['jpg', 'jpeg', 'png', 'pdf'],
239
+ max_size_mb: backendConfig.max_size_mb || 10,
240
+ required: backendConfig.required !== false,
241
+ } as FileUploadConfig;
242
+ case 'country_selection':
243
+ return {
244
+ allowed_countries: backendConfig.allowed_countries || [],
245
+ default_country: backendConfig.default_country || '',
246
+ required: backendConfig.required !== false,
247
+ } as CountrySelectionConfig;
248
+ case 'email_verification':
249
+ return backendConfig as any;
250
+ case 'phone_verification':
251
+ return backendConfig as any;
252
+ case 'personal_information':
253
+ return backendConfig as any;
254
+ case 'additional_documents':
255
+ return backendConfig as any;
256
+ default:
257
+ return backendConfig as ComponentConfig;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Transform a single backend component to SDK component
263
+ */
264
+ function transformComponent(
265
+ backendComponent: BackendTemplateComponent,
266
+ componentIndex: number
267
+ ): TemplateComponent {
268
+ const mappedType = COMPONENT_TYPE_MAPPING[backendComponent.type];
269
+
270
+ if (!mappedType) {
271
+ logger.warn(`Unknown component type: ${backendComponent.type}, defaulting to initialization`);
272
+ }
273
+
274
+ const componentId = hashStringToNumber(backendComponent.id);
275
+ const { labels, instructions } = transformTranslations(backendComponent.translations);
276
+ const ui = transformUI(backendComponent.translations);
277
+ const config = transformComponentConfig(mappedType || 'initialization', backendComponent.config);
278
+
279
+ // For components with multiple sides (like id_card), create nested structure
280
+ let finalLabels: LocalizedText | Record<string, LocalizedText> = labels;
281
+ let finalInstructions: LocalizedText | Record<string, LocalizedText> = instructions;
282
+ let finalUI: ComponentUI | Record<string, ComponentUI> = ui;
283
+
284
+ if (mappedType === 'id_card' && (backendComponent.config as GovernmentIdConfig).requiredSides === 'front-back') {
285
+ // Create separate labels/instructions for front and back
286
+ finalLabels = {
287
+ front: {
288
+ en: backendComponent.translations.en?.title || labels.en,
289
+ fr: backendComponent.translations.fr?.title || labels.fr,
290
+ },
291
+ back: {
292
+ en: `${labels.en} (Back)`,
293
+ fr: `${labels.fr} (Verso)`,
294
+ },
295
+ };
296
+ finalInstructions = {
297
+ front: instructions,
298
+ back: instructions,
299
+ };
300
+ finalUI = {
301
+ front: ui,
302
+ back: ui,
303
+ };
304
+ }
305
+
306
+ return {
307
+ id: componentId,
308
+ type: mappedType || 'initialization',
309
+ order: backendComponent.order,
310
+ labels: finalLabels,
311
+ instructions: finalInstructions,
312
+ ui: finalUI,
313
+ config,
314
+ required: backendComponent.required !== false,
315
+ } as TemplateComponent;
316
+ }
317
+
318
+ /**
319
+ * Transform backend template to SDK template format
320
+ */
321
+ export function transformBackendTemplateToSDK(
322
+ backendTemplate: BackendKYCTemplate
323
+ ): KYCTemplate {
324
+ try {
325
+ // Validate backend template
326
+ if (!backendTemplate.id || !backendTemplate.name || !Array.isArray(backendTemplate.components)) {
327
+ throw new Error('Invalid backend template structure');
328
+ }
329
+
330
+ // Sort components by order
331
+ const sortedComponents = [...backendTemplate.components].sort(
332
+ (a, b) => a.order - b.order
333
+ );
334
+
335
+ // Transform each component
336
+ const transformedComponents = sortedComponents.map((comp, index) =>
337
+ transformComponent(comp, index)
338
+ );
339
+
340
+ // Auto-create country_selection component before id_card if government-id has selection data
341
+ // Check if country_selection already exists in backend template
342
+ const hasCountrySelectionInBackend = sortedComponents.some(c => (c as any).type === 'country-selection');
343
+
344
+ if (!hasCountrySelectionInBackend) {
345
+ // Find the first government-id component with selection data
346
+ const govIdWithSelection = sortedComponents.find(backendComp =>
347
+ backendComp.type === 'government-id' &&
348
+ backendComp.config && (
349
+ (backendComp.config as any).selectedCountries ||
350
+ (backendComp.config as any).documentTypesByCountry
351
+ )
352
+ );
353
+
354
+ if (govIdWithSelection) {
355
+ // Find the corresponding transformed id_card component
356
+ const transformedIdCard = transformedComponents.find(c =>
357
+ hashStringToNumber(govIdWithSelection.id) === c.id
358
+ );
359
+
360
+ if (transformedIdCard) {
361
+ // Create a country_selection component before id_card
362
+ const countrySelectionComponent: TemplateComponent = {
363
+ id: hashStringToNumber(`${govIdWithSelection.id}-country-selection`),
364
+ type: 'country_selection',
365
+ order: transformedIdCard.order - 0.5, // Insert before id_card
366
+ labels: {
367
+ en: govIdWithSelection.translations.en?.title || 'Select Country and Document',
368
+ fr: govIdWithSelection.translations.fr?.title || 'Sélectionnez le pays et le document',
369
+ },
370
+ instructions: {
371
+ en: 'Please select your country and document type',
372
+ fr: 'Veuillez sélectionner votre pays et le type de document',
373
+ },
374
+ ui: {
375
+ themeColor: '#2DBD60',
376
+ buttonText: {
377
+ en: 'Continue',
378
+ fr: 'Continuer',
379
+ },
380
+ } as ComponentUI,
381
+ config: {
382
+ allowed_countries: (govIdWithSelection.config as any).selectedCountries || [],
383
+ default_country: '',
384
+ required: true,
385
+ } as CountrySelectionConfig,
386
+ };
387
+
388
+ // Insert country_selection before id_card in transformedComponents
389
+ const idCardIndex = transformedComponents.indexOf(transformedIdCard);
390
+ transformedComponents.splice(idCardIndex, 0, countrySelectionComponent);
391
+ }
392
+ }
393
+ }
394
+
395
+ // Re-assign order values sequentially after insertion
396
+ transformedComponents.forEach((comp, index) => {
397
+ comp.order = index + 1;
398
+ });
399
+
400
+ // Create SDK template
401
+ const sdkTemplate: KYCTemplate = {
402
+ id: hashStringToNumber(backendTemplate.id),
403
+ name: backendTemplate.name as LocalizedText,
404
+ description: backendTemplate.description || {
405
+ en: '',
406
+ fr: '',
407
+ },
408
+ version: backendTemplate.version || '1.0.0',
409
+ components: transformedComponents,
410
+ };
411
+
412
+ logger.log(`Template transformed: ${backendTemplate.id} -> ${sdkTemplate.id} with ${transformedComponents.length} components`);
413
+
414
+ return sdkTemplate;
415
+ } catch (error: any) {
416
+ logger.error('Error transforming template:', error);
417
+ throw new Error(`Failed to transform template: ${error.message}`);
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Validate transformed template
423
+ */
424
+ export function validateTransformedTemplate(template: KYCTemplate): boolean {
425
+ if (!template.id || !template.name || !Array.isArray(template.components)) {
426
+ return false;
427
+ }
428
+
429
+ if (!template.name.en || !template.name.fr) {
430
+ return false;
431
+ }
432
+
433
+ // Validate each component
434
+ for (const component of template.components) {
435
+ if (!component.id || !component.type || typeof component.order !== 'number') {
436
+ return false;
437
+ }
438
+
439
+ if (!component.labels || !component.instructions || !component.config) {
440
+ return false;
441
+ }
442
+ }
443
+
444
+ return true;
445
+ }