@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.
- package/DeunaWalletsModule.podspec +19 -0
- package/README.md +141 -2
- package/android/build.gradle +43 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/com/deuna/wallets/DeunaWalletsModule.kt +103 -0
- package/app.plugin.js +1 -0
- package/expo-module.config.json +10 -0
- package/ios/DeunaWalletsModule.swift +56 -0
- package/lib/module/DeunaSDK.js +44 -3
- package/lib/module/DeunaSDK.js.map +1 -1
- package/lib/module/components/DeunaWebView.js +3 -0
- package/lib/module/components/DeunaWebView.js.map +1 -1
- package/lib/module/components/DeunaWidget.js +1 -0
- package/lib/module/components/DeunaWidget.js.map +1 -1
- package/lib/module/controllers/BaseWebViewController.js +17 -4
- package/lib/module/controllers/BaseWebViewController.js.map +1 -1
- package/lib/module/controllers/ElementsWidgetController.js +9 -1
- package/lib/module/controllers/ElementsWidgetController.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/plugin/index.js +25 -0
- package/lib/module/plugin/index.js.map +1 -0
- package/lib/module/plugin/withAndroidGooglePay.js +20 -0
- package/lib/module/plugin/withAndroidGooglePay.js.map +1 -0
- package/lib/module/plugin/withIosApplePay.js +10 -0
- package/lib/module/plugin/withIosApplePay.js.map +1 -0
- package/lib/module/plugin/withIosFmtFix.js +34 -0
- package/lib/module/plugin/withIosFmtFix.js.map +1 -0
- package/lib/module/types/base.js +16 -0
- package/lib/module/types/base.js.map +1 -1
- package/lib/module/types/envs.js +12 -0
- package/lib/module/types/envs.js.map +1 -1
- package/lib/module/wallets/WalletModule.js +218 -0
- package/lib/module/wallets/WalletModule.js.map +1 -0
- package/lib/module/wallets/index.js +4 -0
- package/lib/module/wallets/index.js.map +1 -0
- package/lib/module/wallets/tokenize/applePay.js +24 -0
- package/lib/module/wallets/tokenize/applePay.js.map +1 -0
- package/lib/module/wallets/tokenize/googlePay.js +24 -0
- package/lib/module/wallets/tokenize/googlePay.js.map +1 -0
- package/lib/module/wallets/tokenize/index.js +12 -0
- package/lib/module/wallets/tokenize/index.js.map +1 -0
- package/lib/module/wallets/types.js +4 -0
- package/lib/module/wallets/types.js.map +1 -0
- package/lib/module/wallets/vaultApi.js +121 -0
- package/lib/module/wallets/vaultApi.js.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts +12 -3
- package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/components/DeunaWebView.d.ts +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/components/DeunaWebView.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts +7 -0
- package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/controllers/ElementsWidgetController.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/index.d.ts +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/index.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/plugin/index.d.ts +8 -0
- package/lib/typescript/deuna-sdk-react-native/src/plugin/index.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/plugin/withAndroidGooglePay.d.ts +3 -0
- package/lib/typescript/deuna-sdk-react-native/src/plugin/withAndroidGooglePay.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/plugin/withIosApplePay.d.ts +5 -0
- package/lib/typescript/deuna-sdk-react-native/src/plugin/withIosApplePay.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/plugin/withIosFmtFix.d.ts +3 -0
- package/lib/typescript/deuna-sdk-react-native/src/plugin/withIosFmtFix.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/types/base.d.ts +78 -14
- package/lib/typescript/deuna-sdk-react-native/src/types/base.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/types/envs.d.ts +12 -0
- package/lib/typescript/deuna-sdk-react-native/src/types/envs.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts +11 -0
- package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts +3 -3
- package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts.map +1 -1
- package/lib/typescript/deuna-sdk-react-native/src/wallets/WalletModule.d.ts +29 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/WalletModule.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/index.d.ts +3 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/index.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/applePay.d.ts +2 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/applePay.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/googlePay.d.ts +2 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/googlePay.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/index.d.ts +3 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/tokenize/index.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/types.d.ts +15 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/types.d.ts.map +1 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/vaultApi.d.ts +38 -0
- package/lib/typescript/deuna-sdk-react-native/src/wallets/vaultApi.d.ts.map +1 -0
- package/package.json +14 -2
- package/react-native.config.js +8 -0
- package/src/DeunaSDK.ts +65 -4
- package/src/components/DeunaWebView.tsx +4 -0
- package/src/components/DeunaWidget.tsx +2 -0
- package/src/controllers/BaseWebViewController.ts +15 -4
- package/src/controllers/ElementsWidgetController.ts +16 -1
- package/src/index.tsx +1 -0
- package/src/plugin/index.ts +29 -0
- package/src/plugin/withAndroidGooglePay.ts +15 -0
- package/src/plugin/withIosApplePay.ts +10 -0
- package/src/plugin/withIosFmtFix.ts +49 -0
- package/src/types/base.ts +92 -14
- package/src/types/envs.ts +14 -0
- package/src/types/helpers/urlConfig.ts +1 -1
- package/src/types/interfaces/callbacks.ts +15 -0
- package/src/types/interfaces/initWidgetBase.ts +3 -3
- package/src/wallets/WalletModule.ts +255 -0
- package/src/wallets/index.ts +13 -0
- package/src/wallets/tokenize/applePay.ts +22 -0
- package/src/wallets/tokenize/googlePay.ts +27 -0
- package/src/wallets/tokenize/index.ts +12 -0
- package/src/wallets/types.ts +17 -0
- 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 {
|
|
@@ -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,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
|
+
}
|