@deuna/react-native-sdk 2.0.5 → 2.1.0-beta.2

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 (111) hide show
  1. package/DeunaWalletsModule.podspec +19 -0
  2. package/README.md +141 -2
  3. package/android/build.gradle +43 -0
  4. package/android/src/main/AndroidManifest.xml +8 -0
  5. package/android/src/main/java/com/deuna/wallets/DeunaWalletsModule.kt +103 -0
  6. package/app.plugin.js +1 -0
  7. package/expo-module.config.json +10 -0
  8. package/ios/DeunaWalletsModule.swift +56 -0
  9. package/lib/module/DeunaSDK.js +44 -3
  10. package/lib/module/DeunaSDK.js.map +1 -1
  11. package/lib/module/components/DeunaWebView.js +3 -0
  12. package/lib/module/components/DeunaWebView.js.map +1 -1
  13. package/lib/module/components/DeunaWidget.js +1 -0
  14. package/lib/module/components/DeunaWidget.js.map +1 -1
  15. package/lib/module/controllers/BaseWebViewController.js +17 -4
  16. package/lib/module/controllers/BaseWebViewController.js.map +1 -1
  17. package/lib/module/controllers/ElementsWidgetController.js +9 -1
  18. package/lib/module/controllers/ElementsWidgetController.js.map +1 -1
  19. package/lib/module/index.js +1 -0
  20. package/lib/module/index.js.map +1 -1
  21. package/lib/module/plugin/index.js +25 -0
  22. package/lib/module/plugin/index.js.map +1 -0
  23. package/lib/module/plugin/withAndroidGooglePay.js +20 -0
  24. package/lib/module/plugin/withAndroidGooglePay.js.map +1 -0
  25. package/lib/module/plugin/withIosApplePay.js +10 -0
  26. package/lib/module/plugin/withIosApplePay.js.map +1 -0
  27. package/lib/module/plugin/withIosFmtFix.js +34 -0
  28. package/lib/module/plugin/withIosFmtFix.js.map +1 -0
  29. package/lib/module/types/base.js +16 -0
  30. package/lib/module/types/base.js.map +1 -1
  31. package/lib/module/types/envs.js +12 -0
  32. package/lib/module/types/envs.js.map +1 -1
  33. package/lib/module/wallets/WalletModule.js +218 -0
  34. package/lib/module/wallets/WalletModule.js.map +1 -0
  35. package/lib/module/wallets/index.js +4 -0
  36. package/lib/module/wallets/index.js.map +1 -0
  37. package/lib/module/wallets/tokenize/applePay.js +24 -0
  38. package/lib/module/wallets/tokenize/applePay.js.map +1 -0
  39. package/lib/module/wallets/tokenize/googlePay.js +24 -0
  40. package/lib/module/wallets/tokenize/googlePay.js.map +1 -0
  41. package/lib/module/wallets/tokenize/index.js +12 -0
  42. package/lib/module/wallets/tokenize/index.js.map +1 -0
  43. package/lib/module/wallets/types.js +4 -0
  44. package/lib/module/wallets/types.js.map +1 -0
  45. package/lib/module/wallets/vaultApi.js +121 -0
  46. package/lib/module/wallets/vaultApi.js.map +1 -0
  47. package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts +12 -3
  48. package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts.map +1 -1
  49. package/lib/typescript/deuna-sdk-react-native/src/components/DeunaWebView.d.ts +1 -0
  50. package/lib/typescript/deuna-sdk-react-native/src/components/DeunaWebView.d.ts.map +1 -1
  51. package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts +7 -0
  52. package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts.map +1 -1
  53. package/lib/typescript/deuna-sdk-react-native/src/controllers/ElementsWidgetController.d.ts.map +1 -1
  54. package/lib/typescript/deuna-sdk-react-native/src/index.d.ts +1 -0
  55. package/lib/typescript/deuna-sdk-react-native/src/index.d.ts.map +1 -1
  56. package/lib/typescript/deuna-sdk-react-native/src/plugin/index.d.ts +8 -0
  57. package/lib/typescript/deuna-sdk-react-native/src/plugin/index.d.ts.map +1 -0
  58. package/lib/typescript/deuna-sdk-react-native/src/plugin/withAndroidGooglePay.d.ts +3 -0
  59. package/lib/typescript/deuna-sdk-react-native/src/plugin/withAndroidGooglePay.d.ts.map +1 -0
  60. package/lib/typescript/deuna-sdk-react-native/src/plugin/withIosApplePay.d.ts +5 -0
  61. package/lib/typescript/deuna-sdk-react-native/src/plugin/withIosApplePay.d.ts.map +1 -0
  62. package/lib/typescript/deuna-sdk-react-native/src/plugin/withIosFmtFix.d.ts +3 -0
  63. package/lib/typescript/deuna-sdk-react-native/src/plugin/withIosFmtFix.d.ts.map +1 -0
  64. package/lib/typescript/deuna-sdk-react-native/src/types/base.d.ts +78 -14
  65. package/lib/typescript/deuna-sdk-react-native/src/types/base.d.ts.map +1 -1
  66. package/lib/typescript/deuna-sdk-react-native/src/types/envs.d.ts +12 -0
  67. package/lib/typescript/deuna-sdk-react-native/src/types/envs.d.ts.map +1 -1
  68. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts +1 -1
  69. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts.map +1 -1
  70. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts +11 -0
  71. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts.map +1 -1
  72. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts +3 -3
  73. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts.map +1 -1
  74. package/lib/typescript/deuna-sdk-react-native/src/wallets/WalletModule.d.ts +29 -0
  75. package/lib/typescript/deuna-sdk-react-native/src/wallets/WalletModule.d.ts.map +1 -0
  76. package/lib/typescript/deuna-sdk-react-native/src/wallets/index.d.ts +3 -0
  77. package/lib/typescript/deuna-sdk-react-native/src/wallets/index.d.ts.map +1 -0
  78. package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/applePay.d.ts +2 -0
  79. package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/applePay.d.ts.map +1 -0
  80. package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/googlePay.d.ts +2 -0
  81. package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/googlePay.d.ts.map +1 -0
  82. package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/index.d.ts +3 -0
  83. package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/index.d.ts.map +1 -0
  84. package/lib/typescript/deuna-sdk-react-native/src/wallets/types.d.ts +15 -0
  85. package/lib/typescript/deuna-sdk-react-native/src/wallets/types.d.ts.map +1 -0
  86. package/lib/typescript/deuna-sdk-react-native/src/wallets/vaultApi.d.ts +38 -0
  87. package/lib/typescript/deuna-sdk-react-native/src/wallets/vaultApi.d.ts.map +1 -0
  88. package/package.json +14 -2
  89. package/react-native.config.js +8 -0
  90. package/src/DeunaSDK.ts +65 -4
  91. package/src/components/DeunaWebView.tsx +4 -0
  92. package/src/components/DeunaWidget.tsx +2 -0
  93. package/src/controllers/BaseWebViewController.ts +15 -4
  94. package/src/controllers/ElementsWidgetController.ts +16 -1
  95. package/src/index.tsx +1 -0
  96. package/src/plugin/index.ts +29 -0
  97. package/src/plugin/withAndroidGooglePay.ts +15 -0
  98. package/src/plugin/withIosApplePay.ts +10 -0
  99. package/src/plugin/withIosFmtFix.ts +49 -0
  100. package/src/types/base.ts +92 -14
  101. package/src/types/envs.ts +14 -0
  102. package/src/types/helpers/urlConfig.ts +1 -1
  103. package/src/types/interfaces/callbacks.ts +15 -0
  104. package/src/types/interfaces/initWidgetBase.ts +3 -3
  105. package/src/wallets/WalletModule.ts +255 -0
  106. package/src/wallets/index.ts +13 -0
  107. package/src/wallets/tokenize/applePay.ts +22 -0
  108. package/src/wallets/tokenize/googlePay.ts +27 -0
  109. package/src/wallets/tokenize/index.ts +12 -0
  110. package/src/wallets/types.ts +17 -0
  111. package/src/wallets/vaultApi.ts +229 -0
@@ -2,6 +2,20 @@ export type ClosedAction = "userAction" | "systemAction";
2
2
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
3
3
  export type Json = Record<string, any>;
4
4
 
5
+ export interface WalletPaymentData {
6
+ provider: "GOOGLE_PAY" | "APPLE_PAY";
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ paymentMethodData: Record<string, any>;
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ shippingAddress?: Record<string, any>;
11
+ email?: string;
12
+ }
13
+
14
+ export interface WalletPaymentAuthResult {
15
+ status: "SUCCESS" | "ERROR";
16
+ error?: string;
17
+ }
18
+
5
19
  export interface OnError {
6
20
  type: string;
7
21
  metadata: {
@@ -36,6 +50,7 @@ export interface PaymentWidgetCallbacks extends BaseCallbacks {
36
50
  onCardBinDetected?: (data: OnCardBinDetected) => void;
37
51
  onInstallmentSelected?: (data: OnInstallmentSelectedPayload) => void;
38
52
  onPaymentProcessing?: () => void;
53
+ onPaymentAuthorized?: (data: WalletPaymentData) => Promise<WalletPaymentAuthResult>;
39
54
  }
40
55
 
41
56
  export interface ElementsWidgetCallbacks extends BaseCallbacks {
@@ -11,9 +11,9 @@ type DeepPartial<T> = {
11
11
 
12
12
  export type Language = "es" | "pt" | "en";
13
13
 
14
- interface UserInfo {
15
- firstName: string;
16
- lastName: string;
14
+ export interface UserInfo {
15
+ firstName?: string;
16
+ lastName?: string;
17
17
  email: string;
18
18
  }
19
19
 
@@ -0,0 +1,255 @@
1
+ import type { EventSubscription as Subscription } from 'expo-modules-core';
2
+ import type {
3
+ InitElementsParams,
4
+ WalletProvider,
5
+ WalletsError,
6
+ } from './types';
7
+ import type { UserInfo } from '../types/interfaces/initWidgetBase';
8
+ import type {
9
+ GetWalletsAvailableParams,
10
+ WalletElementConfig,
11
+ } from '../types/base';
12
+ import { fetchVaultResult } from './vaultApi';
13
+ import { api as API_URLS } from '../types/envs';
14
+ import { buildTokenizeBody } from './tokenize';
15
+
16
+ // Lazy-load expo-modules-core so the SDK still works on environments without it.
17
+ function getNativeWallets() {
18
+ try {
19
+ const { requireNativeModule } = require('expo-modules-core');
20
+ return requireNativeModule('DeunaWallets');
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ function getEmitter() {
27
+ try {
28
+ const { EventEmitter } = require('expo-modules-core');
29
+ const mod = getNativeWallets();
30
+ return mod ? new EventEmitter(mod) : null;
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+
36
+ function isWalletElementConfig(params: GetWalletsAvailableParams): params is WalletElementConfig {
37
+ return 'GOOGLE_PAY' in params || 'APPLE_PAY' in params;
38
+ }
39
+
40
+ const WALLET_PROVIDER_MAP: Record<string, WalletProvider> = {
41
+ GOOGLE_PAY: 'google_pay',
42
+ APPLE_PAY: 'apple_pay',
43
+ };
44
+
45
+ /**
46
+ * Returns the list of wallet providers available on the current device for the given order.
47
+ *
48
+ * Two flows:
49
+ * - WalletElementConfig: credentials provided directly, no vault fetch.
50
+ * Native module checks device availability only.
51
+ * - { orderToken?, userInfo? }: fetches vault API to get providers + credentials,
52
+ * then native module filters by device availability.
53
+ */
54
+ export async function getWalletsAvailable(
55
+ publicApiKey: string,
56
+ environment: string,
57
+ params: GetWalletsAvailableParams
58
+ ): Promise<WalletProvider[]> {
59
+ const mod = getNativeWallets();
60
+ if (!mod) {
61
+ console.log('❌ [Wallets] No native module available');
62
+ return [];
63
+ }
64
+
65
+ if (isWalletElementConfig(params)) {
66
+ const providers = Object.keys(params).filter(
67
+ (k): k is keyof WalletElementConfig => k === 'GOOGLE_PAY' || k === 'APPLE_PAY'
68
+ );
69
+ console.log('[Wallets] WalletElementConfig flow — providers:', providers);
70
+
71
+ const available: string[] = await mod.checkAvailableProviders({
72
+ providers: providers.map((p) => WALLET_PROVIDER_MAP[p]),
73
+ environment,
74
+ });
75
+ console.log('[Wallets] checkAvailableProviders result:', available);
76
+
77
+ return available as WalletProvider[];
78
+ }
79
+
80
+ const { orderToken, userInfo } = params as { orderToken?: string; userInfo?: Partial<UserInfo> };
81
+ console.log('[Wallets] vault flow — orderToken:', orderToken, 'environment:', environment);
82
+
83
+ const vaultResult = await fetchVaultResult(environment, publicApiKey, orderToken, userInfo);
84
+ console.log('[Wallets] vault result — providers:', vaultResult.providers, 'hasUserToken:', !!vaultResult.userToken);
85
+
86
+ if (vaultResult.providers.length === 0) {
87
+ console.log('❌ [Wallets] No providers from vault API');
88
+ return [];
89
+ }
90
+
91
+ const available: string[] = await mod.checkAvailableProviders({
92
+ providers: vaultResult.providers,
93
+ environment,
94
+ });
95
+ console.log('[Wallets] checkAvailableProviders result:', available);
96
+
97
+ return available as WalletProvider[];
98
+ }
99
+
100
+ /**
101
+ * Launches the native wallet payment sheet for the given provider.
102
+ * - Resolves with `undefined` on payment success (tokenization complete if userToken present).
103
+ * - Resolves with `"closed"` when the user dismisses without paying.
104
+ * - Rejects with a `WalletsError` on failure.
105
+ *
106
+ * Credentials are re-used from a prior getWalletsAvailable call if available,
107
+ * otherwise the vault API is refetched.
108
+ */
109
+ export async function initElements(
110
+ params: InitElementsParams,
111
+ walletConfig?: WalletElementConfig
112
+ ): Promise<Record<string, unknown> | 'closed'> {
113
+ const mod = getNativeWallets();
114
+ if (!mod)
115
+ throw new Error(
116
+ 'DeunaWallets native module is not available on this platform. ' +
117
+ 'Ensure expo-modules-core is installed and the native module is linked.'
118
+ );
119
+
120
+ const { orderToken, publicApiKey, environment, walletProvider, userInfo } = params;
121
+
122
+ let credentials: Record<string, unknown> | undefined;
123
+ let userToken: string | undefined;
124
+ let userId: string | undefined;
125
+
126
+ if (walletConfig) {
127
+ const configKey = walletProvider === 'apple_pay' ? 'APPLE_PAY' : 'GOOGLE_PAY';
128
+ credentials = walletConfig[configKey] as Record<string, unknown> | undefined;
129
+ }
130
+
131
+ if (!credentials) {
132
+ console.log('[Wallets] initElements vault fetch — orderToken:', orderToken, 'hasUserInfo:', !!userInfo, 'email:', (userInfo as any)?.email);
133
+ const vaultResult = await fetchVaultResult(environment, publicApiKey, orderToken, userInfo);
134
+ console.log('[Wallets] initElements vault result — hasUserToken:', !!vaultResult.userToken, 'hasUserId:', !!vaultResult.userId, 'providers:', vaultResult.providers);
135
+ credentials = vaultResult.credentials[walletProvider] as Record<string, unknown> | undefined;
136
+ userToken = vaultResult.userToken;
137
+ userId = vaultResult.userId;
138
+ }
139
+
140
+ if (!credentials) {
141
+ throw {
142
+ code: 'WALLET_UNAVAILABLE',
143
+ message: `No credentials for ${walletProvider}`,
144
+ };
145
+ }
146
+
147
+ if (!userToken || !userId) {
148
+ throw { code: 'MISSING_USER_AUTH', message: 'userToken or userId is missing — cannot tokenize wallet payment.' };
149
+ }
150
+
151
+ console.log('[Wallets] launchWallet →', walletProvider, 'credentials keys:', Object.keys(credentials));
152
+
153
+ let result: Record<string, unknown> | 'closed';
154
+ try {
155
+ result = await mod.launchWallet({
156
+ provider: walletProvider,
157
+ credentials,
158
+ environment,
159
+ });
160
+ } catch (nativeErr: any) {
161
+ console.log('❌ [Wallets] launchWallet native error:', nativeErr?.message ?? JSON.stringify(nativeErr));
162
+ throw nativeErr;
163
+ }
164
+
165
+ if (result === 'closed') return 'closed';
166
+
167
+ console.log('[Wallets] launchWallet raw result:', JSON.stringify(result, null, 2));
168
+
169
+ return await tokenizeCard({
170
+ environment,
171
+ publicApiKey,
172
+ userId,
173
+ userToken,
174
+ provider: walletProvider,
175
+ paymentData: (result as Record<string, unknown>).paymentData,
176
+ });
177
+ }
178
+
179
+ async function tokenizeCard(params: {
180
+ environment: string;
181
+ publicApiKey: string;
182
+ userId: string;
183
+ userToken: string;
184
+ provider: WalletProvider;
185
+ paymentData: unknown;
186
+ }): Promise<Record<string, unknown>> {
187
+ const { environment, publicApiKey, userId, userToken, provider, paymentData } = params;
188
+ const base = API_URLS[environment as keyof typeof API_URLS] ?? API_URLS.develop;
189
+ const url = `${base}/users/${encodeURIComponent(userId)}/cards`;
190
+
191
+ const body = buildTokenizeBody(provider, paymentData);
192
+
193
+ console.log(`[Wallets] tokenizeCard → POST ${url} provider=${provider}`);
194
+ console.log('[Wallets] tokenizeCard body:', JSON.stringify(body, null, 2));
195
+
196
+ let response: Response;
197
+ try {
198
+ response = await fetch(url, {
199
+ method: 'POST',
200
+ headers: {
201
+ 'Content-Type': 'application/json',
202
+ Authorization: `Bearer ${userToken}`,
203
+ 'x-api-key': publicApiKey,
204
+ },
205
+ body: JSON.stringify(body),
206
+ });
207
+ } catch (networkErr: any) {
208
+ console.log(`❌ [Wallets] tokenizeCard network error: ${networkErr?.message ?? networkErr}`);
209
+ throw { code: 'NETWORK_ERROR', message: networkErr?.message ?? 'Network request failed' };
210
+ }
211
+
212
+ const responseText = await response.text().catch(() => '');
213
+ console.log(`[Wallets] tokenizeCard response: status=${response.status} body=${responseText}`);
214
+
215
+ let json: Record<string, unknown> = {};
216
+ try { json = JSON.parse(responseText); } catch { /* non-JSON body */ }
217
+
218
+ if (json?.error) {
219
+ const errPayload = json.error as Record<string, unknown>;
220
+ console.log(`❌ [Wallets] tokenizeCard API error: code=${errPayload.code} message=${errPayload.message}`);
221
+ throw {
222
+ code: errPayload.code ?? 'TOKENIZATION_ERROR',
223
+ message: errPayload.message ?? 'Card tokenization returned an error.',
224
+ };
225
+ }
226
+
227
+ if (!response.ok) {
228
+ console.log(`❌ [Wallets] tokenizeCard failed: HTTP ${response.status}`);
229
+ throw { code: 'TOKENIZATION_ERROR', message: `Tokenization failed: ${response.status}` };
230
+ }
231
+
232
+ console.log(`✅ [Wallets] tokenizeCard success`);
233
+ return json;
234
+ }
235
+
236
+ export function addWalletSuccessListener(
237
+ listener: (data: Record<string, unknown>) => void
238
+ ): Subscription | null {
239
+ const emitter = getEmitter();
240
+ return emitter?.addListener('onWalletSuccess', listener) ?? null;
241
+ }
242
+
243
+ export function addWalletErrorListener(
244
+ listener: (error: WalletsError) => void
245
+ ): Subscription | null {
246
+ const emitter = getEmitter();
247
+ return emitter?.addListener('onWalletError', listener) ?? null;
248
+ }
249
+
250
+ export function addWalletClosedListener(
251
+ listener: (payload: { action: 'userAction' | 'systemAction' }) => void
252
+ ): Subscription | null {
253
+ const emitter = getEmitter();
254
+ return emitter?.addListener('onWalletClosed', listener) ?? null;
255
+ }
@@ -0,0 +1,13 @@
1
+ export {
2
+ getWalletsAvailable,
3
+ initElements,
4
+ addWalletSuccessListener,
5
+ addWalletErrorListener,
6
+ addWalletClosedListener,
7
+ } from './WalletModule';
8
+
9
+ export type {
10
+ WalletProvider,
11
+ WalletsError,
12
+ InitElementsParams,
13
+ } from './types';
@@ -0,0 +1,22 @@
1
+ export function buildApplePayTokenizeBody(paymentData: unknown): Record<string, unknown> {
2
+ const pd = (paymentData ?? {}) as Record<string, unknown>;
3
+ const header = (pd['header'] ?? {}) as Record<string, unknown>;
4
+
5
+ return {
6
+ credential_source: 'apple_pay',
7
+ credential_source_config: {
8
+ type: 'apple_pay',
9
+ values: {
10
+ system: pd['version'] ?? '',
11
+ merchant_transaction_id: header['transactionId'] ?? '',
12
+ encrypted_data: pd['data'] ?? '',
13
+ encryption_header: {
14
+ signature: pd['signature'] ?? '',
15
+ public_key_hash: header['publicKeyHash'] ?? '',
16
+ ephemeral_public_key: header['ephemeralPublicKey'] ?? '',
17
+ },
18
+ src_cx_flow_id: 'mobile',
19
+ },
20
+ },
21
+ };
22
+ }
@@ -0,0 +1,27 @@
1
+ export function buildGooglePayTokenizeBody(paymentData: unknown): Record<string, unknown> {
2
+ const pd = (paymentData ?? {}) as Record<string, unknown>;
3
+
4
+ const rawPMD = pd['paymentMethodData'];
5
+ console.log(`[Wallets] paymentMethodData type=${typeof rawPMD} value=${JSON.stringify(rawPMD)}`);
6
+
7
+ const paymentMethodData = (rawPMD ?? {}) as Record<string, unknown>;
8
+
9
+ const rawTD = paymentMethodData['tokenizationData'];
10
+ console.log(`[Wallets] tokenizationData type=${typeof rawTD} value=${JSON.stringify(rawTD)}`);
11
+
12
+ const tokenizationData = (rawTD ?? {}) as Record<string, unknown>;
13
+ const encryptedData = (tokenizationData['token'] ?? '') as string;
14
+
15
+ console.log(`[Wallets] token type=${typeof tokenizationData['token']} length=${encryptedData.length}`);
16
+
17
+ return {
18
+ credential_source: 'google_pay',
19
+ credential_source_config: {
20
+ type: 'google_pay',
21
+ values: {
22
+ encrypted_data: encryptedData,
23
+ src_cx_flow_id: 'mobile',
24
+ },
25
+ },
26
+ };
27
+ }
@@ -0,0 +1,12 @@
1
+ import type { WalletProvider } from '../types';
2
+ import { buildGooglePayTokenizeBody } from './googlePay';
3
+ import { buildApplePayTokenizeBody } from './applePay';
4
+
5
+ const tokenizeBodyBuilders: Record<WalletProvider, (paymentData: unknown) => Record<string, unknown>> = {
6
+ google_pay: buildGooglePayTokenizeBody,
7
+ apple_pay: buildApplePayTokenizeBody,
8
+ };
9
+
10
+ export function buildTokenizeBody(provider: WalletProvider, paymentData: unknown): Record<string, unknown> {
11
+ return tokenizeBodyBuilders[provider](paymentData);
12
+ }
@@ -0,0 +1,17 @@
1
+ import type { Environment } from '../types/base';
2
+ import type { UserInfo } from '../types/interfaces/initWidgetBase';
3
+
4
+ export type WalletProvider = 'apple_pay' | 'google_pay';
5
+
6
+ export interface WalletsError {
7
+ code: string;
8
+ message: string;
9
+ }
10
+
11
+ export interface InitElementsParams {
12
+ orderToken: string;
13
+ publicApiKey: string;
14
+ environment: Environment;
15
+ walletProvider: WalletProvider;
16
+ userInfo?: Partial<UserInfo>;
17
+ }
@@ -0,0 +1,229 @@
1
+ import { ELEMENTS_URLS } from '../types/helpers/buildElementsLink';
2
+ import type { UserInfo } from '../types/interfaces/initWidgetBase';
3
+
4
+ const DEFAULT_CARD_NETWORKS = ['VISA', 'MASTERCARD', 'AMEX', 'DISCOVER'];
5
+ const DEFAULT_AUTH_METHODS_APPLE = ['CRYPTOGRAM_3DS'];
6
+ const DEFAULT_AUTH_METHODS_GOOGLE = ['PAN_ONLY', 'CRYPTOGRAM_3DS'];
7
+
8
+ export interface ApplePayCredentials {
9
+ merchantIdentifier: string;
10
+ displayName: string;
11
+ supportedNetworks: string[];
12
+ merchantCapabilities: string[];
13
+ transactionInfo?: {
14
+ amount: string;
15
+ currencyCode: string;
16
+ countryCode: string;
17
+ label: string;
18
+ };
19
+ credentialId?: string;
20
+ }
21
+
22
+ export interface GooglePayCredentials {
23
+ merchantId: string;
24
+ merchantName: string;
25
+ gateway: string;
26
+ gatewayMerchantId: string;
27
+ tokenizationType: 'PAYMENT_GATEWAY' | 'DIRECT';
28
+ publicKey?: string;
29
+ allowedCardNetworks: string[];
30
+ allowedAuthMethods: string[];
31
+ transactionInfo?: {
32
+ totalPrice: string;
33
+ currencyCode: string;
34
+ countryCode: string;
35
+ };
36
+ }
37
+
38
+ export type WalletCredentials = ApplePayCredentials | GooglePayCredentials;
39
+
40
+ export interface VaultFetchResult {
41
+ providers: string[];
42
+ credentials: Record<string, WalletCredentials>;
43
+ userToken?: string;
44
+ userId?: string;
45
+ }
46
+
47
+ export async function fetchVaultResult(
48
+ environment: string,
49
+ publicApiKey: string,
50
+ orderToken?: string,
51
+ userInfo?: Partial<UserInfo>
52
+ ): Promise<VaultFetchResult> {
53
+ const base =
54
+ ELEMENTS_URLS[environment as keyof typeof ELEMENTS_URLS] ??
55
+ ELEMENTS_URLS.develop;
56
+ const url = orderToken
57
+ ? `${base}/api/vault?orderToken=${encodeURIComponent(orderToken)}`
58
+ : `${base}/api/vault`;
59
+
60
+ const body = userInfo ? buildUserInfoBody(userInfo) : undefined;
61
+ console.log(
62
+ '[Wallets] fetchVaultResult — url:',
63
+ url,
64
+ 'body:',
65
+ JSON.stringify(body)
66
+ );
67
+
68
+ const response = await fetch(url, {
69
+ method: 'POST',
70
+ headers: { 'x-api-key': publicApiKey, 'Content-Type': 'application/json' },
71
+ body: body ? JSON.stringify(body) : undefined,
72
+ });
73
+
74
+ if (!response.ok) {
75
+ console.error('[Wallets] Vault API error:', response.status);
76
+ console.error('[Wallets] Vault API error message:', response.statusText);
77
+ console.error('[Wallets] Vault API error response:', response);
78
+ throw {
79
+ code: 'VAULT_FETCH_FAILED',
80
+ message: `Vault API error: ${response.status}`,
81
+ };
82
+ }
83
+
84
+ return parseVaultResponse(await response.json());
85
+ }
86
+
87
+ function parseVaultResponse(json: Record<string, unknown>): VaultFetchResult {
88
+ console.log(
89
+ '[Wallets] parseVaultResponse result:',
90
+ JSON.stringify(json, null, 2)
91
+ );
92
+ const paymentMethods = (json['paymentMethods'] as unknown[]) ?? [];
93
+ const checkout = json['checkout'] as Record<string, unknown> | undefined;
94
+ const merchant = checkout?.['merchant'] as
95
+ | Record<string, unknown>
96
+ | undefined;
97
+ const order = (checkout?.['order'] as Record<string, unknown> | undefined)?.[
98
+ 'order'
99
+ ] as Record<string, unknown> | undefined;
100
+ const userAuthData = (
101
+ json['userAuthResponse'] as Record<string, unknown> | undefined
102
+ )?.['data'] as Record<string, unknown> | undefined;
103
+
104
+ const credentialParser = {
105
+ apple_pay: parseApplePayCredentials,
106
+ google_pay: parseGooglePayCredentials,
107
+ };
108
+
109
+ const providers: string[] = [];
110
+ const credentials: Record<string, WalletCredentials> = {};
111
+
112
+ for (const rawMethod of paymentMethods) {
113
+ const method = rawMethod as Record<string, unknown>;
114
+ const processorName = method['processor_name'] as string | undefined;
115
+ if (!processorName || providers.includes(processorName)) continue;
116
+
117
+ providers.push(processorName);
118
+
119
+ const parse =
120
+ credentialParser[processorName as keyof typeof credentialParser];
121
+ if (parse) {
122
+ credentials[processorName] = parse(method, merchant, order);
123
+ }
124
+ }
125
+
126
+ return {
127
+ providers,
128
+ credentials,
129
+ userToken:
130
+ (userAuthData?.['user_token'] as string | undefined) || undefined,
131
+ userId: (userAuthData?.['user_id'] as string | undefined) || undefined,
132
+ };
133
+ }
134
+
135
+ function parseApplePayCredentials(
136
+ method: Record<string, unknown>,
137
+ merchant: Record<string, unknown> | undefined,
138
+ order: Record<string, unknown> | undefined
139
+ ): ApplePayCredentials {
140
+ const extra = (method['extra_params'] as Record<string, unknown>) ?? {};
141
+ const displayName =
142
+ (extra['merchant_name'] as string | undefined) ??
143
+ (merchant?.['name'] as string | undefined) ??
144
+ '';
145
+
146
+ return {
147
+ merchantIdentifier:
148
+ (extra['mobile_merchant_id'] as string | undefined) ?? '',
149
+ displayName,
150
+ supportedNetworks:
151
+ (extra['allowed_card_networks'] as string[] | undefined) ??
152
+ DEFAULT_CARD_NETWORKS,
153
+ merchantCapabilities:
154
+ (extra['allowed_auth_methods'] as string[] | undefined) ??
155
+ DEFAULT_AUTH_METHODS_APPLE,
156
+ transactionInfo: buildApplePayTransactionInfo(order, merchant, displayName),
157
+ credentialId: (method['id'] as string | undefined) || undefined,
158
+ };
159
+ }
160
+
161
+ function buildApplePayTransactionInfo(
162
+ order: Record<string, unknown> | undefined,
163
+ merchant: Record<string, unknown> | undefined,
164
+ label: string
165
+ ): ApplePayCredentials['transactionInfo'] {
166
+ const currency = order?.['currency'] as string | undefined;
167
+ const country = merchant?.['country'] as string | undefined;
168
+ if (!currency || !country) return undefined;
169
+ const totalCents = (order?.['total_amount'] as number | undefined) ?? 0;
170
+ return {
171
+ amount: (totalCents / 100).toFixed(2),
172
+ currencyCode: currency.toUpperCase(),
173
+ countryCode: country.toUpperCase(),
174
+ label,
175
+ };
176
+ }
177
+
178
+ function parseGooglePayCredentials(
179
+ method: Record<string, unknown>,
180
+ merchant: Record<string, unknown> | undefined,
181
+ order: Record<string, unknown> | undefined
182
+ ): GooglePayCredentials {
183
+ const creds = (method['credentials'] as Record<string, unknown>) ?? {};
184
+ const extra = (method['extra_params'] as Record<string, unknown>) ?? {};
185
+ const gateway = (extra['gateway'] as string | undefined) ?? '';
186
+
187
+ return {
188
+ merchantId: (extra['mobile_merchant_id'] as string | undefined) ?? '',
189
+ merchantName: (merchant?.['name'] as string | undefined) ?? '',
190
+ gateway,
191
+ gatewayMerchantId:
192
+ (extra['mobile_merchant_id'] as string | undefined) ?? '',
193
+ tokenizationType:
194
+ gateway.toUpperCase() === 'DIRECT' ? 'DIRECT' : 'PAYMENT_GATEWAY',
195
+ publicKey: (creds['public_api_key'] as string | undefined) || undefined,
196
+ allowedCardNetworks:
197
+ (extra['allowed_card_networks'] as string[] | undefined) ??
198
+ DEFAULT_CARD_NETWORKS,
199
+ allowedAuthMethods:
200
+ (extra['allowed_auth_methods'] as string[] | undefined) ??
201
+ DEFAULT_AUTH_METHODS_GOOGLE,
202
+ transactionInfo: buildGooglePayTransactionInfo(order, merchant),
203
+ };
204
+ }
205
+
206
+ function buildGooglePayTransactionInfo(
207
+ order: Record<string, unknown> | undefined,
208
+ merchant: Record<string, unknown> | undefined
209
+ ): GooglePayCredentials['transactionInfo'] {
210
+ const currency = order?.['currency'] as string | undefined;
211
+ const country = merchant?.['country'] as string | undefined;
212
+ if (!currency || !country) return undefined;
213
+ const totalCents = (order?.['total_amount'] as number | undefined) ?? 0;
214
+ return {
215
+ totalPrice: (totalCents / 100).toFixed(2),
216
+ currencyCode: currency.toUpperCase(),
217
+ countryCode: country.toUpperCase(),
218
+ };
219
+ }
220
+
221
+ function buildUserInfoBody(
222
+ userInfo: Partial<UserInfo>
223
+ ): Record<string, unknown> | undefined {
224
+ if (!userInfo.email) return undefined;
225
+ const body: Record<string, unknown> = { email: userInfo.email };
226
+ if (userInfo.firstName) body['firstName'] = userInfo.firstName;
227
+ if (userInfo.lastName) body['lastName'] = userInfo.lastName;
228
+ return body;
229
+ }