@stripe/stripe-react-native 0.58.0 → 0.59.1
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/android/.idea/AndroidProjectSystem.xml +6 -0
- package/android/.idea/compiler.xml +6 -0
- package/android/.idea/gradle.xml +18 -0
- package/android/.idea/migrations.xml +10 -0
- package/android/.idea/misc.xml +10 -0
- package/android/.idea/runConfigurations.xml +17 -0
- package/android/.idea/vcs.xml +6 -0
- package/android/build.gradle +2 -0
- package/android/local.properties +8 -0
- package/android/src/main/AndroidManifest.xml +27 -1
- package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +2 -0
- package/android/src/main/java/com/reactnativestripesdk/EventEmitterCompat.kt +8 -0
- package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementConfig.kt +147 -0
- package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementView.kt +164 -0
- package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementViewManager.kt +65 -0
- package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +1 -1
- package/android/src/main/java/com/reactnativestripesdk/PaymentSheetManager.kt +46 -26
- package/android/src/main/java/com/reactnativestripesdk/StripeConnectDeepLinkInterceptorActivity.kt +77 -0
- package/android/src/main/java/com/reactnativestripesdk/StripeFileProvider.kt +17 -0
- package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +325 -22
- package/android/src/main/java/com/reactnativestripesdk/StripeSdkPackage.kt +1 -0
- package/android/src/main/java/com/reactnativestripesdk/customersheet/CustomerSheetManager.kt +3 -0
- package/android/src/main/java/com/reactnativestripesdk/utils/Errors.kt +8 -0
- package/android/src/main/res/xml/file_paths.xml +4 -0
- package/android/src/oldarch/java/com/facebook/react/viewmanagers/PaymentMethodMessagingElementViewManagerDelegate.java +36 -0
- package/android/src/oldarch/java/com/facebook/react/viewmanagers/PaymentMethodMessagingElementViewManagerInterface.java +18 -0
- package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +20 -0
- package/android/src/test/java/com/reactnativestripesdk/PaymentMethodMessagingElementConfigTest.kt +543 -0
- package/android/src/test/java/com/reactnativestripesdk/PaymentSheetManagerTest.kt +70 -0
- package/ios/CustomerSheet/CustomerSheetUtils.swift +4 -0
- package/ios/OldArch/StripeSdkEventEmitterCompat.h +2 -0
- package/ios/OldArch/StripeSdkEventEmitterCompat.m +13 -1
- package/ios/PaymentMethodMessagingElementConfig.swift +116 -0
- package/ios/PaymentMethodMessagingElementHandler.m +9 -0
- package/ios/PaymentMethodMessagingElementView.swift +139 -0
- package/ios/StripeSdk.mm +40 -0
- package/ios/StripeSdkEmitter.swift +2 -0
- package/ios/StripeSdkImpl+CustomerSheet.swift +1 -0
- package/ios/StripeSdkImpl+Embedded.swift +4 -0
- package/ios/StripeSdkImpl+PaymentSheet.swift +4 -0
- package/ios/StripeSdkImpl.swift +132 -0
- package/jest/mock.js +20 -0
- package/lib/commonjs/components/AddToWalletButton.js +1 -1
- package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
- package/lib/commonjs/components/AddressSheet.js +1 -1
- package/lib/commonjs/components/AddressSheet.js.map +1 -1
- package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
- package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
- package/lib/commonjs/components/CardField.js +1 -1
- package/lib/commonjs/components/CardField.js.map +1 -1
- package/lib/commonjs/components/CardForm.js +1 -1
- package/lib/commonjs/components/CardForm.js.map +1 -1
- package/lib/commonjs/components/PlatformPayButton.js +1 -1
- package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
- package/lib/commonjs/components/StripeContainer.js +1 -1
- package/lib/commonjs/components/StripeContainer.js.map +1 -1
- package/lib/commonjs/connect/Components.js +1 -1
- package/lib/commonjs/connect/Components.js.map +1 -1
- package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
- package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
- package/lib/commonjs/connect/EmbeddedComponent.js +5 -5
- package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
- package/lib/commonjs/connect/ModalCloseButton.js +1 -1
- package/lib/commonjs/connect/ModalCloseButton.js.map +1 -1
- package/lib/commonjs/connect/NavigationBar.js +1 -1
- package/lib/commonjs/connect/NavigationBar.js.map +1 -1
- package/lib/commonjs/connect/analytics/AnalyticsClient.js +2 -0
- package/lib/commonjs/connect/analytics/AnalyticsClient.js.map +1 -0
- package/lib/commonjs/connect/analytics/ComponentAnalyticsClient.js +2 -0
- package/lib/commonjs/connect/analytics/ComponentAnalyticsClient.js.map +1 -0
- package/lib/commonjs/connect/analytics/events.js +2 -0
- package/lib/commonjs/connect/analytics/events.js.map +1 -0
- package/lib/commonjs/connect/testUtils.js +2 -0
- package/lib/commonjs/connect/testUtils.js.map +1 -0
- package/lib/commonjs/events.js.map +1 -1
- package/lib/commonjs/functions.js +1 -1
- package/lib/commonjs/functions.js.map +1 -1
- package/lib/commonjs/helpers.js +1 -1
- package/lib/commonjs/hooks/useStripe.js +1 -1
- package/lib/commonjs/hooks/useStripe.js.map +1 -1
- package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
- package/lib/commonjs/specs/NativeAddressSheet.js +1 -1
- package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
- package/lib/commonjs/specs/NativeAuBECSDebitForm.js +1 -1
- package/lib/commonjs/specs/NativeCardField.js +1 -1
- package/lib/commonjs/specs/NativeCardField.js.map +1 -1
- package/lib/commonjs/specs/NativeCardForm.js +1 -1
- package/lib/commonjs/specs/NativeCardForm.js.map +1 -1
- package/lib/commonjs/specs/NativeConnectAccountOnboardingView.js +1 -1
- package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
- package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
- package/lib/commonjs/specs/NativeGooglePayButton.js +1 -1
- package/lib/commonjs/specs/NativeNavigationBar.js +1 -1
- package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js +2 -0
- package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js.map +1 -0
- package/lib/commonjs/specs/NativeStripeContainer.js +1 -1
- package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
- package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
- package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
- package/lib/commonjs/types/Errors.js +1 -1
- package/lib/commonjs/types/Errors.js.map +1 -1
- package/lib/commonjs/types/PaymentSheet.js.map +1 -1
- package/lib/commonjs/types/components/PaymentMethodMessagingElementComponent.js +2 -0
- package/lib/commonjs/types/components/PaymentMethodMessagingElementComponent.js.map +1 -0
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/module/components/AddToWalletButton.js +1 -1
- package/lib/module/components/AddToWalletButton.js.map +1 -1
- package/lib/module/components/AddressSheet.js +1 -1
- package/lib/module/components/AddressSheet.js.map +1 -1
- package/lib/module/components/AuBECSDebitForm.js +1 -1
- package/lib/module/components/AuBECSDebitForm.js.map +1 -1
- package/lib/module/components/CardField.js +1 -1
- package/lib/module/components/CardField.js.map +1 -1
- package/lib/module/components/CardForm.js +1 -1
- package/lib/module/components/CardForm.js.map +1 -1
- package/lib/module/components/PlatformPayButton.js +1 -1
- package/lib/module/components/PlatformPayButton.js.map +1 -1
- package/lib/module/components/StripeContainer.js +1 -1
- package/lib/module/components/StripeContainer.js.map +1 -1
- package/lib/module/connect/Components.js +1 -1
- package/lib/module/connect/Components.js.map +1 -1
- package/lib/module/connect/ConnectComponentsProvider.js +1 -1
- package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
- package/lib/module/connect/EmbeddedComponent.js +5 -5
- package/lib/module/connect/EmbeddedComponent.js.map +1 -1
- package/lib/module/connect/ModalCloseButton.js +1 -1
- package/lib/module/connect/ModalCloseButton.js.map +1 -1
- package/lib/module/connect/NavigationBar.js +1 -1
- package/lib/module/connect/NavigationBar.js.map +1 -1
- package/lib/module/connect/analytics/AnalyticsClient.js +2 -0
- package/lib/module/connect/analytics/AnalyticsClient.js.map +1 -0
- package/lib/module/connect/analytics/ComponentAnalyticsClient.js +2 -0
- package/lib/module/connect/analytics/ComponentAnalyticsClient.js.map +1 -0
- package/lib/module/connect/analytics/events.js +2 -0
- package/lib/module/connect/analytics/events.js.map +1 -0
- package/lib/module/connect/testUtils.js +2 -0
- package/lib/module/connect/testUtils.js.map +1 -0
- package/lib/module/events.js.map +1 -1
- package/lib/module/functions.js +1 -1
- package/lib/module/functions.js.map +1 -1
- package/lib/module/helpers.js +1 -1
- package/lib/module/hooks/useStripe.js +1 -1
- package/lib/module/hooks/useStripe.js.map +1 -1
- package/lib/module/specs/NativeAddToWalletButton.js +1 -1
- package/lib/module/specs/NativeAddressSheet.js +1 -1
- package/lib/module/specs/NativeApplePayButton.js +1 -1
- package/lib/module/specs/NativeAuBECSDebitForm.js +1 -1
- package/lib/module/specs/NativeCardField.js +1 -1
- package/lib/module/specs/NativeCardField.js.map +1 -1
- package/lib/module/specs/NativeCardForm.js +1 -1
- package/lib/module/specs/NativeCardForm.js.map +1 -1
- package/lib/module/specs/NativeConnectAccountOnboardingView.js +1 -1
- package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
- package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
- package/lib/module/specs/NativeGooglePayButton.js +1 -1
- package/lib/module/specs/NativeNavigationBar.js +1 -1
- package/lib/module/specs/NativePaymentMethodMessagingElement.js +2 -0
- package/lib/module/specs/NativePaymentMethodMessagingElement.js.map +1 -0
- package/lib/module/specs/NativeStripeContainer.js +1 -1
- package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
- package/lib/module/types/EmbeddedPaymentElement.js +1 -1
- package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
- package/lib/module/types/Errors.js +1 -1
- package/lib/module/types/Errors.js.map +1 -1
- package/lib/module/types/PaymentSheet.js.map +1 -1
- package/lib/module/types/components/PaymentMethodMessagingElementComponent.js +2 -0
- package/lib/module/types/components/PaymentMethodMessagingElementComponent.js.map +1 -0
- package/lib/module/types/index.js.map +1 -1
- package/lib/typescript/src/connect/Components.d.ts +91 -0
- package/lib/typescript/src/connect/Components.d.ts.map +1 -1
- package/lib/typescript/src/connect/ConnectComponentsProvider.d.ts +61 -0
- package/lib/typescript/src/connect/ConnectComponentsProvider.d.ts.map +1 -1
- package/lib/typescript/src/connect/EmbeddedComponent.d.ts.map +1 -1
- package/lib/typescript/src/connect/analytics/AnalyticsClient.d.ts +32 -0
- package/lib/typescript/src/connect/analytics/AnalyticsClient.d.ts.map +1 -0
- package/lib/typescript/src/connect/analytics/ComponentAnalyticsClient.d.ts +94 -0
- package/lib/typescript/src/connect/analytics/ComponentAnalyticsClient.d.ts.map +1 -0
- package/lib/typescript/src/connect/analytics/events.d.ts +215 -0
- package/lib/typescript/src/connect/analytics/events.d.ts.map +1 -0
- package/lib/typescript/src/connect/testUtils.d.ts +45 -0
- package/lib/typescript/src/connect/testUtils.d.ts.map +1 -0
- package/lib/typescript/src/events.d.ts +2 -0
- package/lib/typescript/src/events.d.ts.map +1 -1
- package/lib/typescript/src/functions.d.ts +13 -1
- package/lib/typescript/src/functions.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useStripe.d.ts +2 -1
- package/lib/typescript/src/hooks/useStripe.d.ts.map +1 -1
- package/lib/typescript/src/specs/NativePaymentMethodMessagingElement.d.ts +16 -0
- package/lib/typescript/src/specs/NativePaymentMethodMessagingElement.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +16 -1
- package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
- package/lib/typescript/src/types/CustomerSheet.d.ts +5 -0
- package/lib/typescript/src/types/CustomerSheet.d.ts.map +1 -1
- package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +5 -0
- package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
- package/lib/typescript/src/types/Errors.d.ts +4 -0
- package/lib/typescript/src/types/Errors.d.ts.map +1 -1
- package/lib/typescript/src/types/PaymentSheet.d.ts +5 -0
- package/lib/typescript/src/types/PaymentSheet.d.ts.map +1 -1
- package/lib/typescript/src/types/components/PaymentMethodMessagingElementComponent.d.ts +69 -0
- package/lib/typescript/src/types/components/PaymentMethodMessagingElementComponent.d.ts.map +1 -0
- package/lib/typescript/src/types/index.d.ts +8 -1
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/connect/Components.tsx +91 -0
- package/src/connect/ConnectComponentsProvider.tsx +69 -2
- package/src/connect/EmbeddedComponent.tsx +254 -30
- package/src/connect/analytics/AnalyticsClient.ts +75 -0
- package/src/connect/analytics/ComponentAnalyticsClient.ts +315 -0
- package/src/connect/analytics/events.ts +253 -0
- package/src/connect/testUtils.ts +37 -0
- package/src/events.ts +2 -0
- package/src/functions.ts +10 -0
- package/src/hooks/useStripe.tsx +8 -0
- package/src/specs/NativePaymentMethodMessagingElement.ts +25 -0
- package/src/specs/NativeStripeSdkModule.ts +21 -1
- package/src/types/CustomerSheet.ts +5 -0
- package/src/types/EmbeddedPaymentElement.tsx +5 -0
- package/src/types/Errors.ts +5 -0
- package/src/types/PaymentSheet.ts +6 -1
- package/src/types/components/PaymentMethodMessagingElementComponent.tsx +74 -0
- package/src/types/index.ts +11 -0
|
@@ -24,6 +24,39 @@ import { useConnectComponents } from './ConnectComponentsProvider';
|
|
|
24
24
|
export { NavigationBar } from './NavigationBar';
|
|
25
25
|
export type { NavigationBarProps } from './NavigationBar';
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* A full-screen modal component for Connect account onboarding.
|
|
29
|
+
* Guides connected accounts through the process of providing required information.
|
|
30
|
+
*
|
|
31
|
+
* **Platform differences:**
|
|
32
|
+
* - iOS: Uses native modal with UIKit navigation bar
|
|
33
|
+
* - Android: Uses React Native Modal with custom navigation bar
|
|
34
|
+
*
|
|
35
|
+
* @param props.title - Optional title displayed in navigation bar
|
|
36
|
+
* @param props.onExit - Required callback when user closes the onboarding flow
|
|
37
|
+
* @param props.onStepChange - Optional callback fired when onboarding step changes
|
|
38
|
+
* @param props.recipientTermsOfServiceUrl - URL for recipient terms of service
|
|
39
|
+
* @param props.fullTermsOfServiceUrl - URL for full terms of service
|
|
40
|
+
* @param props.privacyPolicyUrl - URL for privacy policy
|
|
41
|
+
* @param props.collectionOptions - Configuration for which account fields to collect
|
|
42
|
+
* @param props.onLoaderStart - Callback when component begins loading
|
|
43
|
+
* @param props.onLoadError - Callback when component fails to load
|
|
44
|
+
* @param props.onPageDidLoad - Callback when component finishes loading
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* <ConnectAccountOnboarding
|
|
49
|
+
* title="Complete your account setup"
|
|
50
|
+
* onExit={() => navigation.goBack()}
|
|
51
|
+
* onStepChange={({ step }) => console.log('Current step:', step)}
|
|
52
|
+
* collectionOptions={{
|
|
53
|
+
* fields: 'currently_due',
|
|
54
|
+
* futureRequirements: 'include'
|
|
55
|
+
* }}
|
|
56
|
+
* />
|
|
57
|
+
* ```
|
|
58
|
+
* @category Connect
|
|
59
|
+
*/
|
|
27
60
|
export function ConnectAccountOnboarding({
|
|
28
61
|
title,
|
|
29
62
|
onExit,
|
|
@@ -178,6 +211,28 @@ export function ConnectAccountOnboarding({
|
|
|
178
211
|
);
|
|
179
212
|
}
|
|
180
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Displays a list of payments for the connected account with optional filtering.
|
|
216
|
+
* Embed this component to show payment history and details.
|
|
217
|
+
*
|
|
218
|
+
* @param props.defaultFilters - Optional filters to apply to the payments list
|
|
219
|
+
* @param props.onLoaderStart - Callback when component begins loading
|
|
220
|
+
* @param props.onLoadError - Callback when component fails to load
|
|
221
|
+
* @param props.onPageDidLoad - Callback when component finishes loading
|
|
222
|
+
* @param props.style - Optional styling for the component container
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```tsx
|
|
226
|
+
* <ConnectPayments
|
|
227
|
+
* defaultFilters={{
|
|
228
|
+
* status: ['successful', 'pending'],
|
|
229
|
+
* date: { after: new Date('2024-01-01') }
|
|
230
|
+
* }}
|
|
231
|
+
* style={{ flex: 1 }}
|
|
232
|
+
* />
|
|
233
|
+
* ```
|
|
234
|
+
* @category Connect
|
|
235
|
+
*/
|
|
181
236
|
export function ConnectPayments({
|
|
182
237
|
defaultFilters,
|
|
183
238
|
onLoaderStart,
|
|
@@ -205,6 +260,21 @@ export function ConnectPayments({
|
|
|
205
260
|
);
|
|
206
261
|
}
|
|
207
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Displays payout information for the connected account.
|
|
265
|
+
* Shows payout history, schedules, and details.
|
|
266
|
+
*
|
|
267
|
+
* @param props.onLoaderStart - Callback when component begins loading
|
|
268
|
+
* @param props.onLoadError - Callback when component fails to load
|
|
269
|
+
* @param props.onPageDidLoad - Callback when component finishes loading
|
|
270
|
+
* @param props.style - Optional styling for the component container
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```tsx
|
|
274
|
+
* <ConnectPayouts style={{ flex: 1 }} />
|
|
275
|
+
* ```
|
|
276
|
+
* @category Connect
|
|
277
|
+
*/
|
|
208
278
|
export function ConnectPayouts({
|
|
209
279
|
onLoaderStart,
|
|
210
280
|
onLoadError,
|
|
@@ -222,6 +292,27 @@ export function ConnectPayouts({
|
|
|
222
292
|
);
|
|
223
293
|
}
|
|
224
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Displays detailed information for a specific payment.
|
|
297
|
+
* Typically used in a modal or detail view after selecting a payment from ConnectPayments.
|
|
298
|
+
*
|
|
299
|
+
* @param props.payment - The payment ID to display details for
|
|
300
|
+
* @param props.onClose - Required callback when user closes the detail view
|
|
301
|
+
* @param props.onLoaderStart - Callback when component begins loading
|
|
302
|
+
* @param props.onLoadError - Callback when component fails to load
|
|
303
|
+
* @param props.onPageDidLoad - Callback when component finishes loading
|
|
304
|
+
* @param props.style - Optional styling for the component container
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```tsx
|
|
308
|
+
* <ConnectPaymentDetails
|
|
309
|
+
* payment="py_1234567890"
|
|
310
|
+
* onClose={() => setShowDetails(false)}
|
|
311
|
+
* style={{ flex: 1 }}
|
|
312
|
+
* />
|
|
313
|
+
* ```
|
|
314
|
+
* @category Connect
|
|
315
|
+
*/
|
|
225
316
|
export function ConnectPaymentDetails({
|
|
226
317
|
payment,
|
|
227
318
|
onClose,
|
|
@@ -5,6 +5,8 @@ import type {
|
|
|
5
5
|
LoadConnectAndInitialize,
|
|
6
6
|
StripeConnectInstance,
|
|
7
7
|
} from './connectTypes';
|
|
8
|
+
import { AnalyticsClient } from './analytics/AnalyticsClient';
|
|
9
|
+
import { Constants } from '../functions';
|
|
8
10
|
|
|
9
11
|
class ConnectInstance implements StripeConnectInstance {
|
|
10
12
|
initParams: StripeConnectInitParams;
|
|
@@ -19,6 +21,29 @@ class ConnectInstance implements StripeConnectInstance {
|
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Creates a Connect instance for use with ConnectComponentsProvider.
|
|
26
|
+
* This instance manages the configuration and state for Connect embedded components.
|
|
27
|
+
*
|
|
28
|
+
* @param initParams - Initialization parameters including publishableKey and fetchClientSecret
|
|
29
|
+
* @returns A StripeConnectInstance that can be passed to ConnectComponentsProvider
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const connectInstance = loadConnectAndInitialize({
|
|
34
|
+
* publishableKey: 'pk_test_123',
|
|
35
|
+
* fetchClientSecret: async () => {
|
|
36
|
+
* const response = await fetch('/account_session');
|
|
37
|
+
* const { client_secret } = await response.json();
|
|
38
|
+
* return client_secret;
|
|
39
|
+
* },
|
|
40
|
+
* appearance: {
|
|
41
|
+
* variables: { colorPrimary: '#635BFF' }
|
|
42
|
+
* }
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
* @category Connect
|
|
46
|
+
*/
|
|
22
47
|
export const loadConnectAndInitialize: LoadConnectAndInitialize = (
|
|
23
48
|
initParams: StripeConnectInitParams
|
|
24
49
|
): StripeConnectInstance => {
|
|
@@ -34,6 +59,7 @@ export type ConnectComponentsPayload = {
|
|
|
34
59
|
appearance: StripeConnectInitParams['appearance'];
|
|
35
60
|
locale: StripeConnectInitParams['locale'];
|
|
36
61
|
connectInstance: ConnectInstance;
|
|
62
|
+
analyticsClient: AnalyticsClient;
|
|
37
63
|
};
|
|
38
64
|
|
|
39
65
|
const ConnectComponentsContext =
|
|
@@ -41,6 +67,42 @@ const ConnectComponentsContext =
|
|
|
41
67
|
|
|
42
68
|
ConnectComponentsContext.displayName = 'ConnectComponents';
|
|
43
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Context provider that makes Connect instance configuration available to embedded components.
|
|
72
|
+
* Wrap your Connect components with this provider to enable them to access the shared configuration.
|
|
73
|
+
*
|
|
74
|
+
* @param props.connectInstance - Instance created via loadConnectAndInitialize
|
|
75
|
+
* @param props.children - React components to render within the provider
|
|
76
|
+
* @returns JSX.Element
|
|
77
|
+
*
|
|
78
|
+
* @throws Error if connectInstance is not created via loadConnectAndInitialize
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```tsx
|
|
82
|
+
* function App() {
|
|
83
|
+
* const [connectInstance] = useState(() => {
|
|
84
|
+
* const fetchClientSecret = async () => {
|
|
85
|
+
* const response = await fetch('/account_session', { method: "POST" });
|
|
86
|
+
* const { client_secret: clientSecret } = await response.json();
|
|
87
|
+
* return clientSecret;
|
|
88
|
+
* };
|
|
89
|
+
*
|
|
90
|
+
* return loadConnectAndInitialize({
|
|
91
|
+
* publishableKey: 'pk_test_123',
|
|
92
|
+
* fetchClientSecret: fetchClientSecret,
|
|
93
|
+
* appearance,
|
|
94
|
+
* });
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* return (
|
|
98
|
+
* <ConnectComponentsProvider connectInstance={connectInstance}>
|
|
99
|
+
* <ConnectPayouts />
|
|
100
|
+
* </ConnectComponentsProvider>
|
|
101
|
+
* );
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
* @category Connect
|
|
105
|
+
*/
|
|
44
106
|
export const ConnectComponentsProvider = ({
|
|
45
107
|
children,
|
|
46
108
|
connectInstance,
|
|
@@ -59,6 +121,11 @@ export const ConnectComponentsProvider = ({
|
|
|
59
121
|
connectInstance.initParams.locale
|
|
60
122
|
);
|
|
61
123
|
|
|
124
|
+
// Initialize analytics client with native system info
|
|
125
|
+
const analyticsClient = useMemo(() => {
|
|
126
|
+
return new AnalyticsClient(Constants.SYSTEM_INFO);
|
|
127
|
+
}, []);
|
|
128
|
+
|
|
62
129
|
if (!connectInstance.onUpdate) {
|
|
63
130
|
connectInstance.onUpdate = (options: StripeConnectUpdateParams) => {
|
|
64
131
|
if (options.appearance) {
|
|
@@ -71,8 +138,8 @@ export const ConnectComponentsProvider = ({
|
|
|
71
138
|
}
|
|
72
139
|
|
|
73
140
|
const value = useMemo(
|
|
74
|
-
() => ({ connectInstance, locale, appearance }),
|
|
75
|
-
[connectInstance, locale, appearance]
|
|
141
|
+
() => ({ connectInstance, locale, appearance, analyticsClient }),
|
|
142
|
+
[connectInstance, locale, appearance, analyticsClient]
|
|
76
143
|
);
|
|
77
144
|
|
|
78
145
|
return (
|
|
@@ -26,6 +26,7 @@ import type {
|
|
|
26
26
|
StripeConnectInitParams,
|
|
27
27
|
} from './connectTypes';
|
|
28
28
|
import type { FinancialConnections } from '../types';
|
|
29
|
+
import { ComponentAnalyticsClient } from './analytics/ComponentAnalyticsClient';
|
|
29
30
|
|
|
30
31
|
const DEVELOPMENT_MODE = false;
|
|
31
32
|
const DEVELOPMENT_URL =
|
|
@@ -35,6 +36,12 @@ const BASE_URL = DEVELOPMENT_MODE ? DEVELOPMENT_URL : PRODUCTION_URL;
|
|
|
35
36
|
|
|
36
37
|
const sdkVersion = pjson.version;
|
|
37
38
|
|
|
39
|
+
// Android deep link polling configuration
|
|
40
|
+
// These constants control the polling mechanism that prevents Expo Router from dismissing screens
|
|
41
|
+
const POLLING_INTERVAL_MS = 500; // How often to check for pending deep links
|
|
42
|
+
const URL_DEDUPLICATION_TIMEOUT_MS = 1000; // How long to remember handled URLs
|
|
43
|
+
const DEEP_LINK_GRACE_PERIOD_MS = 500; // Time to wait for deep link after app resumes
|
|
44
|
+
|
|
38
45
|
// react-native-webview.html will only load versions in the format X.Y.Z
|
|
39
46
|
if (!/^\d+\.\d+\.\d+$/.test(sdkVersion)) {
|
|
40
47
|
throw new Error(
|
|
@@ -139,11 +146,18 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
139
146
|
WebView: typeof WebView | null;
|
|
140
147
|
} | null>(null);
|
|
141
148
|
|
|
142
|
-
// Store pending authenticated webview
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
149
|
+
// Store pending authenticated webview promises (for Android Custom Tabs)
|
|
150
|
+
// Uses a Map keyed by auth session ID to support multiple auth flows.
|
|
151
|
+
// Currently processes promises in FIFO order (first entry resolved first).
|
|
152
|
+
const pendingAuthWebViewPromises = useRef<
|
|
153
|
+
Map<
|
|
154
|
+
string,
|
|
155
|
+
{
|
|
156
|
+
callback: (id: string, url: string | null) => void;
|
|
157
|
+
timeoutId?: NodeJS.Timeout;
|
|
158
|
+
}
|
|
159
|
+
>
|
|
160
|
+
>(new Map());
|
|
147
161
|
|
|
148
162
|
// Store pending Financial Connections promise
|
|
149
163
|
const pendingFinancialConnectionsPromise = useRef<{
|
|
@@ -151,6 +165,10 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
151
165
|
cleanup: () => void;
|
|
152
166
|
} | null>(null);
|
|
153
167
|
|
|
168
|
+
// Track recently handled URLs to prevent duplicate processing
|
|
169
|
+
// This is needed because the SDK uses dual delivery paths (setIntent + direct emit)
|
|
170
|
+
const recentlyHandledUrls = useRef<Set<string>>(new Set());
|
|
171
|
+
|
|
154
172
|
const loadWebViewComponent = useCallback(async () => {
|
|
155
173
|
if (dynamicWebview) return;
|
|
156
174
|
|
|
@@ -170,16 +188,106 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
170
188
|
|
|
171
189
|
const appState = useRef<AppStateStatus>(AppState.currentState);
|
|
172
190
|
|
|
191
|
+
// Android deep link polling mechanism (Android-only)
|
|
192
|
+
//
|
|
193
|
+
// PROBLEM: On Android, completing auth in Custom Tabs causes a stripe-connect:// deep link
|
|
194
|
+
// that gets broadcast to React Native's Linking module, which triggers Expo Router and
|
|
195
|
+
// dismisses the current screen.
|
|
196
|
+
//
|
|
197
|
+
// SOLUTION: This polling mechanism intercepts the deep link before Expo Router receives it:
|
|
198
|
+
// 1. MainActivity.onNewIntent() captures stripe-connect:// URLs and stores them
|
|
199
|
+
// 2. This effect polls for pending URLs while auth is active
|
|
200
|
+
// 3. URLs are processed directly and never broadcast to Expo Router
|
|
201
|
+
// 4. Deduplication prevents the same URL from being processed twice
|
|
202
|
+
//
|
|
203
|
+
// This preserves the navigation stack and prevents unwanted screen dismissals.
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (Platform.OS !== 'android') return;
|
|
206
|
+
|
|
207
|
+
const pollInterval = setInterval(async () => {
|
|
208
|
+
if (pendingAuthWebViewPromises.current.size === 0) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
const pendingUrls =
|
|
214
|
+
await NativeStripeSdk.pollAndClearPendingStripeConnectUrls();
|
|
215
|
+
|
|
216
|
+
if (pendingUrls && pendingUrls.length > 0) {
|
|
217
|
+
pendingUrls.forEach((url: string) => {
|
|
218
|
+
if (url.startsWith('stripe-connect://')) {
|
|
219
|
+
// Deduplication: Skip if we've handled this exact URL
|
|
220
|
+
if (recentlyHandledUrls.current.has(url)) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Mark as handled
|
|
225
|
+
recentlyHandledUrls.current.add(url);
|
|
226
|
+
|
|
227
|
+
// Clear from the set after deduplication timeout
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
recentlyHandledUrls.current.delete(url);
|
|
230
|
+
}, URL_DEDUPLICATION_TIMEOUT_MS);
|
|
231
|
+
|
|
232
|
+
const firstEntry = pendingAuthWebViewPromises.current
|
|
233
|
+
.entries()
|
|
234
|
+
.next().value;
|
|
235
|
+
|
|
236
|
+
if (firstEntry) {
|
|
237
|
+
const [id, promiseData] = firstEntry;
|
|
238
|
+
|
|
239
|
+
// Clear the timeout if it exists
|
|
240
|
+
if (promiseData.timeoutId) {
|
|
241
|
+
clearTimeout(promiseData.timeoutId);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Remove from Map and invoke callback
|
|
245
|
+
pendingAuthWebViewPromises.current.delete(id);
|
|
246
|
+
promiseData.callback(id, url);
|
|
247
|
+
|
|
248
|
+
// Reset the Android flag
|
|
249
|
+
NativeStripeSdk.authWebViewDeepLinkHandled(id).catch(() => {
|
|
250
|
+
// Intentionally silent - flag reset is not critical
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
} catch (_error) {
|
|
257
|
+
// Intentionally silent - polling will retry on next interval
|
|
258
|
+
}
|
|
259
|
+
}, POLLING_INTERVAL_MS);
|
|
260
|
+
|
|
261
|
+
return () => {
|
|
262
|
+
clearInterval(pollInterval);
|
|
263
|
+
};
|
|
264
|
+
}, []);
|
|
265
|
+
|
|
266
|
+
// Handle app state changes to detect when user returns from auth flow
|
|
173
267
|
useEffect(() => {
|
|
174
268
|
const subscription = AppState.addEventListener('change', (nextAppState) => {
|
|
175
269
|
if (
|
|
176
270
|
appState.current.match(/inactive|background/) &&
|
|
177
271
|
nextAppState === 'active'
|
|
178
272
|
) {
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
273
|
+
if (pendingAuthWebViewPromises.current.size > 0) {
|
|
274
|
+
// Give the deep link handler time to process the URL
|
|
275
|
+
// If no deep link arrives within the grace period, assume the user cancelled
|
|
276
|
+
pendingAuthWebViewPromises.current.forEach((promiseData, id) => {
|
|
277
|
+
// Only set timeout if one doesn't already exist
|
|
278
|
+
if (!promiseData.timeoutId) {
|
|
279
|
+
const timeoutId = setTimeout(() => {
|
|
280
|
+
const stillPending = pendingAuthWebViewPromises.current.get(id);
|
|
281
|
+
if (stillPending) {
|
|
282
|
+
pendingAuthWebViewPromises.current.delete(id);
|
|
283
|
+
stillPending.callback(id, null);
|
|
284
|
+
}
|
|
285
|
+
}, DEEP_LINK_GRACE_PERIOD_MS);
|
|
286
|
+
|
|
287
|
+
// Store the timeout ID so we can clear it later if needed
|
|
288
|
+
promiseData.timeoutId = timeoutId;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
183
291
|
}
|
|
184
292
|
}
|
|
185
293
|
|
|
@@ -187,13 +295,23 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
187
295
|
});
|
|
188
296
|
|
|
189
297
|
return () => {
|
|
190
|
-
|
|
298
|
+
// Clear all pending timeouts and promises
|
|
299
|
+
// We intentionally want the latest ref value at cleanup time
|
|
300
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
301
|
+
const promises = pendingAuthWebViewPromises.current;
|
|
302
|
+
promises.forEach((promiseData, _id) => {
|
|
303
|
+
if (promiseData.timeoutId) {
|
|
304
|
+
clearTimeout(promiseData.timeoutId);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
promises.clear();
|
|
191
308
|
pendingFinancialConnectionsPromise.current?.cleanup();
|
|
192
309
|
subscription.remove();
|
|
193
310
|
};
|
|
194
311
|
}, []);
|
|
195
312
|
|
|
196
|
-
const { connectInstance, appearance, locale } =
|
|
313
|
+
const { connectInstance, appearance, locale, analyticsClient } =
|
|
314
|
+
useConnectComponents();
|
|
197
315
|
const { fonts, publishableKey, fetchClientSecret, overrides } =
|
|
198
316
|
connectInstance.initParams as StripeConnectInitParamsInternal;
|
|
199
317
|
|
|
@@ -207,6 +325,22 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
207
325
|
style,
|
|
208
326
|
} = props;
|
|
209
327
|
|
|
328
|
+
// Initialize component analytics client
|
|
329
|
+
const componentAnalytics = useMemo(
|
|
330
|
+
() =>
|
|
331
|
+
new ComponentAnalyticsClient(analyticsClient, {
|
|
332
|
+
publishableKey,
|
|
333
|
+
platformId: overrides?.platformId,
|
|
334
|
+
merchantId: overrides?.merchantId,
|
|
335
|
+
livemode:
|
|
336
|
+
typeof overrides?.livemode === 'boolean'
|
|
337
|
+
? overrides.livemode
|
|
338
|
+
: publishableKey?.startsWith('pk_live_'),
|
|
339
|
+
component,
|
|
340
|
+
}),
|
|
341
|
+
[analyticsClient, publishableKey, overrides, component]
|
|
342
|
+
);
|
|
343
|
+
|
|
210
344
|
const hashParams = {
|
|
211
345
|
component,
|
|
212
346
|
publicKey: publishableKey,
|
|
@@ -292,6 +426,21 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
292
426
|
return undefined;
|
|
293
427
|
}, [WebViewComponent]);
|
|
294
428
|
|
|
429
|
+
// Track component lifecycle events
|
|
430
|
+
useEffect(() => {
|
|
431
|
+
// Log component created
|
|
432
|
+
componentAnalytics.logComponentCreated();
|
|
433
|
+
}, [componentAnalytics]);
|
|
434
|
+
|
|
435
|
+
// Track component viewed (when web view is visible)
|
|
436
|
+
const [hasBeenViewed, setHasBeenViewed] = useState(false);
|
|
437
|
+
const handleLayout = useCallback(() => {
|
|
438
|
+
if (!hasBeenViewed) {
|
|
439
|
+
setHasBeenViewed(true);
|
|
440
|
+
componentAnalytics.logComponentViewed();
|
|
441
|
+
}
|
|
442
|
+
}, [hasBeenViewed, componentAnalytics]);
|
|
443
|
+
|
|
295
444
|
const handleAuthWebViewResult = (id: string, resultUrl: string | null) => {
|
|
296
445
|
ref.current?.injectJavaScript(`
|
|
297
446
|
(function() {
|
|
@@ -339,10 +488,16 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
339
488
|
|
|
340
489
|
const onMessageCallback = useCallback(
|
|
341
490
|
async (event: WebViewMessageEvent) => {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
data
|
|
345
|
-
}
|
|
491
|
+
let message: { type: string; data?: unknown };
|
|
492
|
+
try {
|
|
493
|
+
message = JSON.parse(event.nativeEvent.data);
|
|
494
|
+
} catch (error) {
|
|
495
|
+
componentAnalytics.logDeserializeMessageError(
|
|
496
|
+
'unknown',
|
|
497
|
+
error instanceof Error ? error : new Error(String(error))
|
|
498
|
+
);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
346
501
|
|
|
347
502
|
if (message.type === 'fetchClientSecret') {
|
|
348
503
|
const clientSecret = await fetchClientSecret().catch((error) => {
|
|
@@ -360,7 +515,13 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
360
515
|
// message.data is of type string
|
|
361
516
|
console.debug(`[EmbeddedComponent ${component}]: ${message.data}`);
|
|
362
517
|
} else if (message.type === 'pageDidLoad') {
|
|
518
|
+
const pageViewId = (message.data as { pageViewId?: string })
|
|
519
|
+
?.pageViewId;
|
|
520
|
+
componentAnalytics.logComponentWebPageLoaded(pageViewId);
|
|
363
521
|
onPageDidLoad?.();
|
|
522
|
+
} else if (message.type === 'componentLoaded') {
|
|
523
|
+
// Connect JS fully initialized
|
|
524
|
+
componentAnalytics.logComponentLoaded();
|
|
364
525
|
} else if (message.type === 'accountSessionClaimed') {
|
|
365
526
|
// message.data is of type {elementTagName: string, merchantId: string}
|
|
366
527
|
} else if (message.type === 'openFinancialConnections') {
|
|
@@ -488,7 +649,12 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
488
649
|
// remove the 'set' prefix and lowercase the first letter
|
|
489
650
|
const functionName =
|
|
490
651
|
setter.charAt(3).toLowerCase() + setter.substring(4);
|
|
491
|
-
callbacks?.[functionName]
|
|
652
|
+
if (callbacks?.[functionName]) {
|
|
653
|
+
callbacks[functionName](value);
|
|
654
|
+
} else {
|
|
655
|
+
// Unrecognized setter function
|
|
656
|
+
componentAnalytics.logUnrecognizedSetter(setter);
|
|
657
|
+
}
|
|
492
658
|
}
|
|
493
659
|
} else if (message.type === 'openAuthenticatedWebView') {
|
|
494
660
|
const { url, id } = message.data as { id: string; url: string };
|
|
@@ -501,22 +667,51 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
501
667
|
return;
|
|
502
668
|
}
|
|
503
669
|
|
|
670
|
+
// Log authenticated web view opened
|
|
671
|
+
componentAnalytics.logAuthenticatedWebViewOpened(id);
|
|
672
|
+
|
|
504
673
|
// On Android, we need to wait for the deep link callback
|
|
505
674
|
// On iOS, the promise resolves with the redirect URL
|
|
506
675
|
NativeStripeSdk.openAuthenticatedWebView(id, url)
|
|
507
676
|
.then((result) => {
|
|
508
677
|
if (Platform.OS === 'ios') {
|
|
509
678
|
// iOS returns the redirect URL directly
|
|
510
|
-
|
|
679
|
+
const resultUrl = result?.url ?? null;
|
|
680
|
+
if (resultUrl) {
|
|
681
|
+
componentAnalytics.logAuthenticatedWebViewRedirected(id);
|
|
682
|
+
} else {
|
|
683
|
+
componentAnalytics.logAuthenticatedWebViewCanceled(id);
|
|
684
|
+
}
|
|
685
|
+
handleAuthWebViewResult(id, resultUrl);
|
|
511
686
|
} else {
|
|
512
|
-
// Android: Store promise to be resolved by deep link listener
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
687
|
+
// Android: Store promise in Map to be resolved by deep link listener
|
|
688
|
+
pendingAuthWebViewPromises.current.set(id, {
|
|
689
|
+
callback: (authId: string, resultUrl: string | null) => {
|
|
690
|
+
if (resultUrl) {
|
|
691
|
+
componentAnalytics.logAuthenticatedWebViewRedirected(
|
|
692
|
+
authId
|
|
693
|
+
);
|
|
694
|
+
} else {
|
|
695
|
+
componentAnalytics.logAuthenticatedWebViewCanceled(authId);
|
|
696
|
+
}
|
|
697
|
+
handleAuthWebViewResult(authId, resultUrl);
|
|
698
|
+
},
|
|
699
|
+
});
|
|
517
700
|
}
|
|
518
701
|
})
|
|
519
|
-
.catch(
|
|
702
|
+
.catch((error) => {
|
|
703
|
+
if (__DEV__) {
|
|
704
|
+
console.error(
|
|
705
|
+
`[EmbeddedComponent] Error opening authenticated webview:`,
|
|
706
|
+
error
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
componentAnalytics.logAuthenticatedWebViewError(
|
|
710
|
+
id,
|
|
711
|
+
error instanceof Error ? error : new Error(String(error))
|
|
712
|
+
);
|
|
713
|
+
handleUnexpectedError(error);
|
|
714
|
+
});
|
|
520
715
|
} else {
|
|
521
716
|
// unhandled message
|
|
522
717
|
}
|
|
@@ -524,6 +719,7 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
524
719
|
[
|
|
525
720
|
callbacks,
|
|
526
721
|
component,
|
|
722
|
+
componentAnalytics,
|
|
527
723
|
fetchClientSecret,
|
|
528
724
|
handleUnexpectedError,
|
|
529
725
|
onLoadError,
|
|
@@ -534,19 +730,33 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
534
730
|
|
|
535
731
|
const onShouldStartLoadWithRequest = useCallback(
|
|
536
732
|
(event: ShouldStartLoadRequest) => {
|
|
537
|
-
const { url } = event;
|
|
733
|
+
const { url, navigationType } = event;
|
|
734
|
+
|
|
735
|
+
// Handle CSV export downloads
|
|
736
|
+
if (isCsvExportUrl(url)) {
|
|
737
|
+
NativeStripeSdk.downloadAndShareFile(url, null)
|
|
738
|
+
.then((result) => {
|
|
739
|
+
if (!result.success) {
|
|
740
|
+
console.warn('CSV export share failed:', result.error);
|
|
741
|
+
}
|
|
742
|
+
})
|
|
743
|
+
.catch((error) => {
|
|
744
|
+
handleUnexpectedError(error);
|
|
745
|
+
});
|
|
746
|
+
return false; // Block WebView navigation
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (navigationType !== 'click') return true;
|
|
538
750
|
|
|
539
751
|
// Allow navigation within allowed Stripe domains (matching iOS SDK behavior)
|
|
540
752
|
if (ALLOWED_STRIPE_HOSTS.some((host) => url.includes(host))) {
|
|
541
753
|
return true; // Allow in-WebView navigation
|
|
542
754
|
}
|
|
543
755
|
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
});
|
|
549
|
-
}
|
|
756
|
+
// Open external links in system browser
|
|
757
|
+
Linking.openURL(url).catch((error) => {
|
|
758
|
+
handleUnexpectedError(error);
|
|
759
|
+
});
|
|
550
760
|
|
|
551
761
|
return false; // Block in-WebView navigation for external links
|
|
552
762
|
},
|
|
@@ -582,6 +792,7 @@ export function EmbeddedComponent(props: EmbeddedComponentProps) {
|
|
|
582
792
|
injectedJavaScriptBeforeContentLoaded={'(function() {})();'}
|
|
583
793
|
onMessage={onMessageCallback}
|
|
584
794
|
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
|
|
795
|
+
onLayout={handleLayout}
|
|
585
796
|
// Camera/Media Permissions - matches iOS SDK behavior
|
|
586
797
|
mediaCapturePermissionGrantType="grantIfSameHostElsePrompt"
|
|
587
798
|
allowsInlineMediaPlayback={true}
|
|
@@ -616,3 +827,16 @@ function isValidUrl(url: string): boolean {
|
|
|
616
827
|
return false;
|
|
617
828
|
}
|
|
618
829
|
}
|
|
830
|
+
|
|
831
|
+
// Detects Stripe CSV export URLs
|
|
832
|
+
function isCsvExportUrl(url: string): boolean {
|
|
833
|
+
try {
|
|
834
|
+
const parsedUrl = new URL(url);
|
|
835
|
+
return (
|
|
836
|
+
parsedUrl.hostname.includes('stripe-data-exports') ||
|
|
837
|
+
parsedUrl.pathname.includes('stripe-data-exports')
|
|
838
|
+
);
|
|
839
|
+
} catch {
|
|
840
|
+
return false;
|
|
841
|
+
}
|
|
842
|
+
}
|