@stripe/stripe-react-native 0.57.3 → 0.58.0

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 (122) hide show
  1. package/android/gradle.properties +1 -1
  2. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementView.kt +0 -3
  3. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +5 -3
  4. package/android/src/main/java/com/reactnativestripesdk/NavigationBarView.kt +12 -1
  5. package/android/src/main/java/com/reactnativestripesdk/PaymentElementConfig.kt +18 -0
  6. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetManager.kt +5 -5
  7. package/android/src/main/java/com/reactnativestripesdk/StripeAbstractComposeView.kt +17 -5
  8. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +9 -2
  9. package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +0 -2
  10. package/android/src/test/java/com/reactnativestripesdk/PaymentElementConfigTest.kt +138 -1
  11. package/ios/ConnectAccountOnboarding/ConnectAccountOnboardingView.swift +13 -19
  12. package/ios/StripeSdkImpl+Embedded.swift +4 -1
  13. package/ios/StripeSdkImpl+PaymentSheet.swift +28 -1
  14. package/ios/StripeSdkImpl.swift +26 -2
  15. package/jest/mock.js +6 -0
  16. package/jest/setup.js +30 -0
  17. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  18. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  19. package/lib/commonjs/components/AddressSheet.js +1 -1
  20. package/lib/commonjs/components/AddressSheet.js.map +1 -1
  21. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  22. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  23. package/lib/commonjs/components/CardField.js +1 -1
  24. package/lib/commonjs/components/CardField.js.map +1 -1
  25. package/lib/commonjs/components/CardForm.js +1 -1
  26. package/lib/commonjs/components/CardForm.js.map +1 -1
  27. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  28. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  29. package/lib/commonjs/components/StripeContainer.js +1 -1
  30. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  31. package/lib/commonjs/connect/Components.js +1 -1
  32. package/lib/commonjs/connect/Components.js.map +1 -1
  33. package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
  34. package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
  35. package/lib/commonjs/connect/EmbeddedComponent.js +9 -4
  36. package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
  37. package/lib/commonjs/connect/ModalCloseButton.js +1 -1
  38. package/lib/commonjs/connect/ModalCloseButton.js.map +1 -1
  39. package/lib/commonjs/connect/NavigationBar.js +1 -1
  40. package/lib/commonjs/connect/NavigationBar.js.map +1 -1
  41. package/lib/commonjs/helpers.js +1 -1
  42. package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
  43. package/lib/commonjs/specs/NativeAddressSheet.js +1 -1
  44. package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
  45. package/lib/commonjs/specs/NativeAuBECSDebitForm.js +1 -1
  46. package/lib/commonjs/specs/NativeCardField.js +1 -1
  47. package/lib/commonjs/specs/NativeCardField.js.map +1 -1
  48. package/lib/commonjs/specs/NativeCardForm.js +1 -1
  49. package/lib/commonjs/specs/NativeCardForm.js.map +1 -1
  50. package/lib/commonjs/specs/NativeConnectAccountOnboardingView.js +1 -1
  51. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
  52. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  53. package/lib/commonjs/specs/NativeGooglePayButton.js +1 -1
  54. package/lib/commonjs/specs/NativeNavigationBar.js +1 -1
  55. package/lib/commonjs/specs/NativeStripeContainer.js +1 -1
  56. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  57. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  58. package/lib/commonjs/types/FinancialConnections.js.map +1 -1
  59. package/lib/commonjs/types/PaymentSheet.js +1 -1
  60. package/lib/commonjs/types/PaymentSheet.js.map +1 -1
  61. package/lib/module/components/AddToWalletButton.js +1 -1
  62. package/lib/module/components/AddToWalletButton.js.map +1 -1
  63. package/lib/module/components/AddressSheet.js +1 -1
  64. package/lib/module/components/AddressSheet.js.map +1 -1
  65. package/lib/module/components/AuBECSDebitForm.js +1 -1
  66. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  67. package/lib/module/components/CardField.js +1 -1
  68. package/lib/module/components/CardField.js.map +1 -1
  69. package/lib/module/components/CardForm.js +1 -1
  70. package/lib/module/components/CardForm.js.map +1 -1
  71. package/lib/module/components/PlatformPayButton.js +1 -1
  72. package/lib/module/components/PlatformPayButton.js.map +1 -1
  73. package/lib/module/components/StripeContainer.js +1 -1
  74. package/lib/module/components/StripeContainer.js.map +1 -1
  75. package/lib/module/connect/Components.js +1 -1
  76. package/lib/module/connect/Components.js.map +1 -1
  77. package/lib/module/connect/ConnectComponentsProvider.js +1 -1
  78. package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
  79. package/lib/module/connect/EmbeddedComponent.js +9 -4
  80. package/lib/module/connect/EmbeddedComponent.js.map +1 -1
  81. package/lib/module/connect/ModalCloseButton.js +1 -1
  82. package/lib/module/connect/ModalCloseButton.js.map +1 -1
  83. package/lib/module/connect/NavigationBar.js +1 -1
  84. package/lib/module/connect/NavigationBar.js.map +1 -1
  85. package/lib/module/helpers.js +1 -1
  86. package/lib/module/specs/NativeAddToWalletButton.js +1 -1
  87. package/lib/module/specs/NativeAddressSheet.js +1 -1
  88. package/lib/module/specs/NativeApplePayButton.js +1 -1
  89. package/lib/module/specs/NativeAuBECSDebitForm.js +1 -1
  90. package/lib/module/specs/NativeCardField.js +1 -1
  91. package/lib/module/specs/NativeCardField.js.map +1 -1
  92. package/lib/module/specs/NativeCardForm.js +1 -1
  93. package/lib/module/specs/NativeCardForm.js.map +1 -1
  94. package/lib/module/specs/NativeConnectAccountOnboardingView.js +1 -1
  95. package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
  96. package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  97. package/lib/module/specs/NativeGooglePayButton.js +1 -1
  98. package/lib/module/specs/NativeNavigationBar.js +1 -1
  99. package/lib/module/specs/NativeStripeContainer.js +1 -1
  100. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  101. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  102. package/lib/module/types/FinancialConnections.js.map +1 -1
  103. package/lib/module/types/PaymentSheet.js +1 -1
  104. package/lib/module/types/PaymentSheet.js.map +1 -1
  105. package/lib/typescript/src/connect/Components.d.ts.map +1 -1
  106. package/lib/typescript/src/connect/EmbeddedComponent.d.ts.map +1 -1
  107. package/lib/typescript/src/connect/connectTypes.d.ts +5 -1
  108. package/lib/typescript/src/connect/connectTypes.d.ts.map +1 -1
  109. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +6 -1
  110. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
  111. package/lib/typescript/src/types/FinancialConnections.d.ts +2 -0
  112. package/lib/typescript/src/types/FinancialConnections.d.ts.map +1 -1
  113. package/lib/typescript/src/types/PaymentSheet.d.ts +30 -0
  114. package/lib/typescript/src/types/PaymentSheet.d.ts.map +1 -1
  115. package/package.json +4 -1
  116. package/src/connect/Components.tsx +18 -11
  117. package/src/connect/EmbeddedComponent.tsx +213 -2
  118. package/src/connect/connectTypes.ts +5 -1
  119. package/src/types/EmbeddedPaymentElement.tsx +6 -1
  120. package/src/types/FinancialConnections.ts +2 -0
  121. package/src/types/PaymentSheet.ts +32 -0
  122. package/stripe-react-native.podspec +1 -1
@@ -3,4 +3,4 @@ StripeSdk_compileSdkVersion=30
3
3
  StripeSdk_targetSdkVersion=28
4
4
  StripeSdk_minSdkVersion=21
5
5
  # Keep StripeSdk_stripeVersion in sync with https://github.com/stripe/stripe-identity-react-native/blob/main/android/gradle.properties
6
- StripeSdk_stripeVersion=22.2.+
6
+ StripeSdk_stripeVersion=22.7.+
@@ -29,7 +29,6 @@ import com.stripe.android.model.PaymentMethod
29
29
  import com.stripe.android.paymentelement.CustomPaymentMethodResult
30
30
  import com.stripe.android.paymentelement.CustomPaymentMethodResultHandler
31
31
  import com.stripe.android.paymentelement.EmbeddedPaymentElement
32
- import com.stripe.android.paymentelement.ExperimentalCustomPaymentMethodsApi
33
32
  import com.stripe.android.paymentelement.rememberEmbeddedPaymentElement
34
33
  import com.stripe.android.paymentsheet.CreateIntentResult
35
34
  import com.stripe.android.paymentsheet.PaymentSheet
@@ -44,7 +43,6 @@ enum class RowSelectionBehaviorType {
44
43
  ImmediateAction,
45
44
  }
46
45
 
47
- @OptIn(ExperimentalCustomPaymentMethodsApi::class)
48
46
  class EmbeddedPaymentElementView(
49
47
  context: Context,
50
48
  ) : StripeAbstractComposeView(context) {
@@ -77,7 +75,6 @@ class EmbeddedPaymentElementView(
77
75
  }
78
76
 
79
77
  @SuppressLint("RestrictedApi")
80
- @OptIn(ExperimentalCustomPaymentMethodsApi::class)
81
78
  @Composable
82
79
  override fun Content() {
83
80
  val type by remember { rowSelectionBehaviorType }
@@ -24,7 +24,7 @@ import com.reactnativestripesdk.utils.mapToPreferredNetworks
24
24
  import com.reactnativestripesdk.utils.parseCustomPaymentMethods
25
25
  import com.stripe.android.ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi
26
26
  import com.stripe.android.paymentelement.EmbeddedPaymentElement
27
- import com.stripe.android.paymentelement.ExperimentalCustomPaymentMethodsApi
27
+ import com.stripe.android.paymentsheet.CardFundingFilteringPrivatePreview
28
28
  import com.stripe.android.paymentsheet.PaymentSheet
29
29
  import org.json.JSONArray
30
30
  import org.json.JSONObject
@@ -98,7 +98,7 @@ class EmbeddedPaymentElementViewManager :
98
98
  @SuppressLint("RestrictedApi")
99
99
  @OptIn(
100
100
  ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi::class,
101
- ExperimentalCustomPaymentMethodsApi::class,
101
+ CardFundingFilteringPrivatePreview::class,
102
102
  )
103
103
  private fun parseElementConfiguration(
104
104
  map: ReadableMap,
@@ -157,7 +157,9 @@ class EmbeddedPaymentElementViewManager :
157
157
  ),
158
158
  ).allowsRemovalOfLastSavedPaymentMethod(allowsRemovalOfLastSavedPaymentMethod)
159
159
  .cardBrandAcceptance(mapToCardBrandAcceptance(map))
160
- .embeddedViewDisplaysMandateText(
160
+ .apply {
161
+ mapToAllowedCardFundingTypes(map)?.let { allowedCardFundingTypes(it) }
162
+ }.embeddedViewDisplaysMandateText(
161
163
  map.getBooleanOr("embeddedViewDisplaysMandateText", true),
162
164
  ).customPaymentMethods(
163
165
  parseCustomPaymentMethods(
@@ -2,7 +2,11 @@ package com.reactnativestripesdk
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.graphics.Color
5
+ import android.graphics.PorterDuff
6
+ import android.graphics.PorterDuffColorFilter
7
+ import android.os.Build
5
8
  import android.view.Gravity
9
+ import android.view.View.MeasureSpec
6
10
  import android.widget.FrameLayout
7
11
  import android.widget.ImageButton
8
12
  import android.widget.TextView
@@ -18,6 +22,7 @@ class NavigationBarView(
18
22
  ) : FrameLayout(context) {
19
23
  private val toolbar: Toolbar
20
24
  private val titleTextView: TextView
25
+ private val closeButton: ImageButton
21
26
  private var titleText: String? = null
22
27
 
23
28
  init {
@@ -53,7 +58,7 @@ class NavigationBarView(
53
58
  toolbar.addView(titleTextView, titleParams)
54
59
 
55
60
  // Create close button
56
- val closeButton =
61
+ closeButton =
57
62
  ImageButton(context).apply {
58
63
  setImageDrawable(
59
64
  context.resources.getDrawable(
@@ -61,6 +66,12 @@ class NavigationBarView(
61
66
  null,
62
67
  ),
63
68
  )
69
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
70
+ drawable?.setColorFilter(android.graphics.BlendModeColorFilter(Color.BLACK, android.graphics.BlendMode.SRC_IN))
71
+ } else {
72
+ @Suppress("DEPRECATION")
73
+ drawable?.setColorFilter(PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN))
74
+ }
64
75
  setBackgroundColor(Color.TRANSPARENT)
65
76
  setOnClickListener {
66
77
  dispatchCloseButtonPress()
@@ -8,6 +8,7 @@ import com.reactnativestripesdk.utils.getLongOr
8
8
  import com.reactnativestripesdk.utils.getStringList
9
9
  import com.reactnativestripesdk.utils.isEmpty
10
10
  import com.stripe.android.paymentelement.PaymentMethodOptionsSetupFutureUsagePreview
11
+ import com.stripe.android.paymentsheet.CardFundingFilteringPrivatePreview
11
12
  import com.stripe.android.paymentsheet.PaymentSheet
12
13
 
13
14
  @Throws(PaymentSheetException::class)
@@ -226,3 +227,20 @@ internal fun mapToCardBrandCategory(brand: String): PaymentSheet.CardBrandAccept
226
227
  "discover" -> PaymentSheet.CardBrandAcceptance.BrandCategory.Discover
227
228
  else -> null
228
229
  }
230
+
231
+ @OptIn(CardFundingFilteringPrivatePreview::class)
232
+ internal fun mapToAllowedCardFundingTypes(params: ReadableMap?): List<PaymentSheet.CardFundingType>? {
233
+ val cardFundingFiltering = params?.getMap("cardFundingFiltering") ?: return null
234
+ val allowedTypes = cardFundingFiltering.getStringList("allowedCardFundingTypes") ?: return null
235
+
236
+ return allowedTypes
237
+ .mapNotNull { type ->
238
+ when (type) {
239
+ "debit" -> PaymentSheet.CardFundingType.Debit
240
+ "credit" -> PaymentSheet.CardFundingType.Credit
241
+ "prepaid" -> PaymentSheet.CardFundingType.Prepaid
242
+ "unknown" -> PaymentSheet.CardFundingType.Unknown
243
+ else -> null
244
+ }
245
+ }.ifEmpty { null }
246
+ }
@@ -45,8 +45,8 @@ import com.stripe.android.paymentelement.ConfirmCustomPaymentMethodCallback
45
45
  import com.stripe.android.paymentelement.CreateIntentWithConfirmationTokenCallback
46
46
  import com.stripe.android.paymentelement.CustomPaymentMethodResult
47
47
  import com.stripe.android.paymentelement.CustomPaymentMethodResultHandler
48
- import com.stripe.android.paymentelement.ExperimentalCustomPaymentMethodsApi
49
48
  import com.stripe.android.paymentelement.PaymentMethodOptionsSetupFutureUsagePreview
49
+ import com.stripe.android.paymentsheet.CardFundingFilteringPrivatePreview
50
50
  import com.stripe.android.paymentsheet.CreateIntentCallback
51
51
  import com.stripe.android.paymentsheet.CreateIntentResult
52
52
  import com.stripe.android.paymentsheet.PaymentOptionResultCallback
@@ -67,7 +67,7 @@ import kotlin.coroutines.resume
67
67
  @OptIn(
68
68
  ReactNativeSdkInternal::class,
69
69
  ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi::class,
70
- ExperimentalCustomPaymentMethodsApi::class,
70
+ CardFundingFilteringPrivatePreview::class,
71
71
  )
72
72
  class PaymentSheetManager(
73
73
  context: ReactApplicationContext,
@@ -88,7 +88,6 @@ class PaymentSheetManager(
88
88
  private var keepJsAwake: KeepJsAwakeTask? = null
89
89
 
90
90
  @SuppressLint("RestrictedApi")
91
- @OptIn(ExperimentalCustomPaymentMethodsApi::class)
92
91
  override fun onCreate() {
93
92
  val activity = getCurrentActivityOrResolveWithError(initPromise) ?: return
94
93
  val merchantDisplayName = arguments.getString("merchantDisplayName").orEmpty()
@@ -285,7 +284,9 @@ class PaymentSheetManager(
285
284
  mapToPreferredNetworks(arguments.getIntegerList("preferredNetworks")),
286
285
  ).allowsRemovalOfLastSavedPaymentMethod(allowsRemovalOfLastSavedPaymentMethod)
287
286
  .cardBrandAcceptance(mapToCardBrandAcceptance(arguments))
288
- .customPaymentMethods(parseCustomPaymentMethods(arguments.getMap("customPaymentMethodConfiguration")))
287
+ .apply {
288
+ mapToAllowedCardFundingTypes(arguments)?.let { allowedCardFundingTypes(it) }
289
+ }.customPaymentMethods(parseCustomPaymentMethods(arguments.getMap("customPaymentMethodConfiguration")))
289
290
 
290
291
  primaryButtonLabel?.let { configurationBuilder.primaryButtonLabel(it) }
291
292
  paymentMethodOrder?.let { configurationBuilder.paymentMethodOrder(it) }
@@ -498,7 +499,6 @@ class PaymentSheetManager(
498
499
  } ?: run { resolvePresentPromise(map) }
499
500
  }
500
501
 
501
- @OptIn(ExperimentalCustomPaymentMethodsApi::class)
502
502
  override fun onConfirmCustomPaymentMethod(
503
503
  customPaymentMethod: PaymentSheet.CustomPaymentMethod,
504
504
  billingDetails: PaymentMethod.BillingDetails,
@@ -69,6 +69,8 @@ abstract class StripeAbstractComposeView(
69
69
 
70
70
  private var innerComposeView: InnerComposeView? = null
71
71
  private var isLifecycleSetup = false
72
+ private var activityLifecycleOwner: LifecycleOwner? = null
73
+ private var activityLifecycleObserver: LifecycleEventObserver? = null
72
74
 
73
75
  // Create a lifecycle this is tied to the activity, but that we can manually
74
76
  // update to DESTROYED state when the view is dropped.
@@ -113,24 +115,34 @@ abstract class StripeAbstractComposeView(
113
115
  return
114
116
  }
115
117
 
116
- ((context as? ReactContext)?.currentActivity as? LifecycleOwner?)?.let {
118
+ ((context as? ReactContext)?.currentActivity as? LifecycleOwner?)?.let { owner ->
117
119
  isLifecycleSetup = true
120
+ activityLifecycleOwner = owner
118
121
 
119
122
  // Setup the lifecycle to match the activity.
120
- it.lifecycle.addObserver(
123
+ val observer =
121
124
  object : LifecycleEventObserver {
122
125
  override fun onStateChanged(
123
126
  source: LifecycleOwner,
124
127
  event: Lifecycle.Event,
125
128
  ) {
126
- lifecycleRegistry.handleLifecycleEvent(event)
129
+ if (lifecycleRegistry.currentState != Lifecycle.State.DESTROYED) {
130
+ lifecycleRegistry.handleLifecycleEvent(event)
131
+ }
127
132
  }
128
- },
129
- )
133
+ }
134
+ activityLifecycleObserver = observer
135
+ owner.lifecycle.addObserver(observer)
130
136
  }
131
137
  }
132
138
 
133
139
  fun handleOnDropViewInstance() {
140
+ activityLifecycleObserver?.let { observer ->
141
+ activityLifecycleOwner?.lifecycle?.removeObserver(observer)
142
+ }
143
+ activityLifecycleObserver = null
144
+ activityLifecycleOwner = null
145
+
134
146
  if (lifecycleRegistry.currentState.isAtLeast(Lifecycle.State.CREATED)) {
135
147
  lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
136
148
  }
@@ -1072,6 +1072,10 @@ class StripeSdkModule(
1072
1072
  promise.resolve(createMissingInitError())
1073
1073
  return
1074
1074
  }
1075
+
1076
+ // Use connectedAccountId from params if provided, otherwise fall back to global stripeAccountId
1077
+ val accountId = getValOr(params, "connectedAccountId", null) ?: stripeAccountId
1078
+
1075
1079
  unregisterStripeUIManager(financialConnectionsSheetManager)
1076
1080
  financialConnectionsSheetManager =
1077
1081
  FinancialConnectionsSheetManager(
@@ -1079,7 +1083,7 @@ class StripeSdkModule(
1079
1083
  clientSecret,
1080
1084
  FinancialConnectionsSheetManager.Mode.ForToken,
1081
1085
  publishableKey,
1082
- stripeAccountId,
1086
+ accountId,
1083
1087
  ).also {
1084
1088
  registerStripeUIManager(it)
1085
1089
  it.present(promise)
@@ -1097,6 +1101,9 @@ class StripeSdkModule(
1097
1101
  return
1098
1102
  }
1099
1103
 
1104
+ // Use connectedAccountId from params if provided, otherwise fall back to global stripeAccountId
1105
+ val accountId = getValOr(params, "connectedAccountId", null) ?: stripeAccountId
1106
+
1100
1107
  unregisterStripeUIManager(financialConnectionsSheetManager)
1101
1108
  financialConnectionsSheetManager =
1102
1109
  FinancialConnectionsSheetManager(
@@ -1104,7 +1111,7 @@ class StripeSdkModule(
1104
1111
  clientSecret,
1105
1112
  FinancialConnectionsSheetManager.Mode.ForSession,
1106
1113
  publishableKey,
1107
- stripeAccountId,
1114
+ accountId,
1108
1115
  ).also {
1109
1116
  registerStripeUIManager(it)
1110
1117
  it.present(promise)
@@ -25,7 +25,6 @@ import com.stripe.android.model.StripeIntent
25
25
  import com.stripe.android.model.StripeIntent.NextActionData
26
26
  import com.stripe.android.model.StripeIntent.NextActionType
27
27
  import com.stripe.android.model.Token
28
- import com.stripe.android.paymentelement.ExperimentalCustomPaymentMethodsApi
29
28
  import com.stripe.android.paymentsheet.PaymentSheet
30
29
  import java.lang.IllegalArgumentException
31
30
 
@@ -1036,7 +1035,6 @@ private fun Map<String, Any?>.toReadableMap(): ReadableMap {
1036
1035
  return writableMap
1037
1036
  }
1038
1037
 
1039
- @OptIn(ExperimentalCustomPaymentMethodsApi::class)
1040
1038
  @SuppressLint("RestrictedApi")
1041
1039
  internal fun parseCustomPaymentMethods(customPaymentMethodConfig: ReadableMap?): List<PaymentSheet.CustomPaymentMethod> {
1042
1040
  if (customPaymentMethodConfig == null) {
@@ -4,16 +4,18 @@ import com.reactnativestripesdk.utils.PaymentSheetException
4
4
  import com.reactnativestripesdk.utils.readableArrayOf
5
5
  import com.reactnativestripesdk.utils.readableMapOf
6
6
  import com.stripe.android.paymentelement.PaymentMethodOptionsSetupFutureUsagePreview
7
+ import com.stripe.android.paymentsheet.CardFundingFilteringPrivatePreview
7
8
  import com.stripe.android.paymentsheet.PaymentSheet
8
9
  import org.junit.Assert.assertEquals
9
10
  import org.junit.Assert.assertNotNull
10
11
  import org.junit.Assert.assertNull
12
+ import org.junit.Assert.assertTrue
11
13
  import org.junit.Test
12
14
  import org.junit.runner.RunWith
13
15
  import org.robolectric.RobolectricTestRunner
14
16
 
15
17
  @RunWith(RobolectricTestRunner::class)
16
- @OptIn(PaymentMethodOptionsSetupFutureUsagePreview::class)
18
+ @OptIn(PaymentMethodOptionsSetupFutureUsagePreview::class, CardFundingFilteringPrivatePreview::class)
17
19
  class PaymentElementConfigTest {
18
20
  // ============================================
19
21
  // buildIntentConfiguration Tests
@@ -973,4 +975,139 @@ class PaymentElementConfigTest {
973
975
  val result = buildBillingDetailsCollectionConfiguration(params)
974
976
  assertNotNull(result)
975
977
  }
978
+
979
+ // ============================================
980
+ // mapToAllowedCardFundingTypes Tests
981
+ // ============================================
982
+
983
+ @Test
984
+ fun mapToAllowedCardFundingTypes_NullParams_ReturnsNull() {
985
+ val result = mapToAllowedCardFundingTypes(null)
986
+ assertNull(result)
987
+ }
988
+
989
+ @Test
990
+ fun mapToAllowedCardFundingTypes_NoCardFundingFiltering_ReturnsNull() {
991
+ val params =
992
+ readableMapOf(
993
+ "someOtherKey" to "value",
994
+ )
995
+ val result = mapToAllowedCardFundingTypes(params)
996
+ assertNull(result)
997
+ }
998
+
999
+ @Test
1000
+ fun mapToAllowedCardFundingTypes_DebitOnly_ReturnsList() {
1001
+ val params =
1002
+ readableMapOf(
1003
+ "cardFundingFiltering" to
1004
+ readableMapOf(
1005
+ "allowedCardFundingTypes" to readableArrayOf("debit"),
1006
+ ),
1007
+ )
1008
+ val result = mapToAllowedCardFundingTypes(params)
1009
+ assertNotNull(result)
1010
+ assertEquals(1, result?.size)
1011
+ assertEquals(PaymentSheet.CardFundingType.Debit, result?.get(0))
1012
+ }
1013
+
1014
+ @Test
1015
+ fun mapToAllowedCardFundingTypes_CreditOnly_ReturnsList() {
1016
+ val params =
1017
+ readableMapOf(
1018
+ "cardFundingFiltering" to
1019
+ readableMapOf(
1020
+ "allowedCardFundingTypes" to readableArrayOf("credit"),
1021
+ ),
1022
+ )
1023
+ val result = mapToAllowedCardFundingTypes(params)
1024
+ assertNotNull(result)
1025
+ assertEquals(1, result?.size)
1026
+ assertEquals(PaymentSheet.CardFundingType.Credit, result?.get(0))
1027
+ }
1028
+
1029
+ @Test
1030
+ fun mapToAllowedCardFundingTypes_MultipleTypes_ReturnsList() {
1031
+ val params =
1032
+ readableMapOf(
1033
+ "cardFundingFiltering" to
1034
+ readableMapOf(
1035
+ "allowedCardFundingTypes" to readableArrayOf("debit", "credit", "prepaid"),
1036
+ ),
1037
+ )
1038
+ val result = mapToAllowedCardFundingTypes(params)
1039
+ assertNotNull(result)
1040
+ assertEquals(3, result?.size)
1041
+ assertTrue(result!!.contains(PaymentSheet.CardFundingType.Debit))
1042
+ assertTrue(result.contains(PaymentSheet.CardFundingType.Credit))
1043
+ assertTrue(result.contains(PaymentSheet.CardFundingType.Prepaid))
1044
+ }
1045
+
1046
+ @Test
1047
+ fun mapToAllowedCardFundingTypes_AllFourTypes_ReturnsList() {
1048
+ val params =
1049
+ readableMapOf(
1050
+ "cardFundingFiltering" to
1051
+ readableMapOf(
1052
+ "allowedCardFundingTypes" to readableArrayOf("debit", "credit", "prepaid", "unknown"),
1053
+ ),
1054
+ )
1055
+ val result = mapToAllowedCardFundingTypes(params)
1056
+ assertNotNull(result)
1057
+ assertEquals(4, result?.size)
1058
+ }
1059
+
1060
+ @Test
1061
+ fun mapToAllowedCardFundingTypes_EmptyArray_ReturnsNull() {
1062
+ val params =
1063
+ readableMapOf(
1064
+ "cardFundingFiltering" to
1065
+ readableMapOf(
1066
+ "allowedCardFundingTypes" to readableArrayOf(),
1067
+ ),
1068
+ )
1069
+ val result = mapToAllowedCardFundingTypes(params)
1070
+ assertNull(result)
1071
+ }
1072
+
1073
+ @Test
1074
+ fun mapToAllowedCardFundingTypes_InvalidTypes_Filtered() {
1075
+ val params =
1076
+ readableMapOf(
1077
+ "cardFundingFiltering" to
1078
+ readableMapOf(
1079
+ "allowedCardFundingTypes" to readableArrayOf("invalid", "debit", "not_a_type"),
1080
+ ),
1081
+ )
1082
+ val result = mapToAllowedCardFundingTypes(params)
1083
+ assertNotNull(result)
1084
+ assertEquals(1, result?.size)
1085
+ assertEquals(PaymentSheet.CardFundingType.Debit, result?.get(0))
1086
+ }
1087
+
1088
+ @Test
1089
+ fun mapToAllowedCardFundingTypes_OnlyInvalidTypes_ReturnsNull() {
1090
+ val params =
1091
+ readableMapOf(
1092
+ "cardFundingFiltering" to
1093
+ readableMapOf(
1094
+ "allowedCardFundingTypes" to readableArrayOf("invalid", "not_valid"),
1095
+ ),
1096
+ )
1097
+ val result = mapToAllowedCardFundingTypes(params)
1098
+ assertNull(result)
1099
+ }
1100
+
1101
+ @Test
1102
+ fun mapToAllowedCardFundingTypes_MissingAllowedCardFundingTypes_ReturnsNull() {
1103
+ val params =
1104
+ readableMapOf(
1105
+ "cardFundingFiltering" to
1106
+ readableMapOf(
1107
+ "someOtherKey" to "value",
1108
+ ),
1109
+ )
1110
+ val result = mapToAllowedCardFundingTypes(params)
1111
+ assertNull(result)
1112
+ }
976
1113
  }
@@ -21,6 +21,7 @@ public class ConnectAccountOnboardingView: UIView {
21
21
  override public init(frame: CGRect) {
22
22
  super.init(frame: frame)
23
23
  super.backgroundColor = .clear
24
+ self.isHidden = true // Hide until modal animation completes
24
25
  }
25
26
 
26
27
  required init?(coder: NSCoder) {
@@ -29,10 +30,7 @@ public class ConnectAccountOnboardingView: UIView {
29
30
 
30
31
  @objc public func didSetProps() {
31
32
  if visible && !wasVisible {
32
- // Delay presentation to ensure React Native has rendered children
33
- DispatchQueue.main.async { [weak self] in
34
- self?.presentModal()
35
- }
33
+ presentModal()
36
34
  wasVisible = true
37
35
  } else if !visible && wasVisible {
38
36
  dismissModal()
@@ -47,6 +45,9 @@ public class ConnectAccountOnboardingView: UIView {
47
45
  }
48
46
 
49
47
  private func presentModal() {
48
+ // Keep view hidden during modal presentation
49
+ self.isHidden = true
50
+
50
51
  // Create the view controller that wraps THIS view
51
52
  viewController = ConnectAccountOnboardingViewController()
52
53
  viewController?.title = title
@@ -56,9 +57,6 @@ public class ConnectAccountOnboardingView: UIView {
56
57
  self?.handleClose()
57
58
  }
58
59
 
59
- // Add this entire React Native view to the view controller
60
- viewController?.setReactContentView(self)
61
-
62
60
  // Wrap in a navigation controller
63
61
  navigationController = UINavigationController(rootViewController: viewController!)
64
62
  navigationController?.modalPresentationStyle = .fullScreen
@@ -66,7 +64,14 @@ public class ConnectAccountOnboardingView: UIView {
66
64
 
67
65
  // Find the presenting view controller and present
68
66
  let presenter = findViewControllerPresenter(from: RCTKeyWindow()?.rootViewController ?? UIViewController())
69
- presenter.present(navigationController!, animated: true)
67
+
68
+ // Present the empty modal first, then add React content after animation completes
69
+ presenter.present(navigationController!, animated: true) { [weak self] in
70
+ guard let self = self else { return }
71
+ // Add React content after presentation animation finishes
72
+ self.viewController?.setReactContentView(self)
73
+ self.isHidden = false // Show the view now that modal is presented
74
+ }
70
75
  }
71
76
 
72
77
  private func dismissModal() {
@@ -83,15 +88,4 @@ public class ConnectAccountOnboardingView: UIView {
83
88
  dismissModal()
84
89
  }
85
90
 
86
- override public func didMoveToSuperview() {
87
- super.didMoveToSuperview()
88
-
89
- // When added to native view hierarchy, ensure layout is triggered
90
- if let superview = superview {
91
- // Match the superview's bounds immediately
92
- self.frame = superview.bounds
93
- setNeedsLayout()
94
- layoutIfNeeded()
95
- }
96
- }
97
91
  }
@@ -6,7 +6,7 @@
6
6
  //
7
7
 
8
8
  import Foundation
9
- @_spi(EmbeddedPaymentElementPrivateBeta) @_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(CustomerSessionBetaAccess) @_spi(STP) @_spi(CustomPaymentMethodsBeta) import StripePaymentSheet
9
+ @_spi(EmbeddedPaymentElementPrivateBeta) @_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(CustomerSessionBetaAccess) @_spi(STP) @_spi(CustomPaymentMethodsBeta) @_spi(CardFundingFilteringPrivatePreview) import StripePaymentSheet
10
10
 
11
11
  @objc(StripeSdkImpl)
12
12
  extension StripeSdkImpl {
@@ -311,6 +311,9 @@ extension StripeSdkImpl {
311
311
  }
312
312
 
313
313
  configuration.cardBrandAcceptance = StripeSdkImpl.computeCardBrandAcceptance(params: params)
314
+ if let allowedCardFundingTypes = StripeSdkImpl.computeAllowedCardFundingTypes(params: params) {
315
+ configuration.allowedCardFundingTypes = allowedCardFundingTypes
316
+ }
314
317
 
315
318
  if let formSheetActionParams = params["formSheetAction"] as? NSDictionary,
316
319
  let actionType = formSheetActionParams["type"] as? String {
@@ -6,7 +6,7 @@
6
6
  //
7
7
 
8
8
  import Foundation
9
- @_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(CustomerSessionBetaAccess) @_spi(EmbeddedPaymentElementPrivateBeta) @_spi(STP) @_spi(PaymentMethodOptionsSetupFutureUsagePreview) @_spi(CustomPaymentMethodsBeta) @_spi(ConfirmationTokensPublicPreview) import StripePaymentSheet
9
+ @_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(CustomerSessionBetaAccess) @_spi(EmbeddedPaymentElementPrivateBeta) @_spi(STP) @_spi(PaymentMethodOptionsSetupFutureUsagePreview) @_spi(CustomPaymentMethodsBeta) @_spi(ConfirmationTokensPublicPreview) @_spi(CardFundingFilteringPrivatePreview) import StripePaymentSheet
10
10
 
11
11
  extension StripeSdkImpl {
12
12
  internal func buildPaymentSheetConfiguration(
@@ -136,6 +136,9 @@ extension StripeSdkImpl {
136
136
  }
137
137
 
138
138
  configuration.cardBrandAcceptance = StripeSdkImpl.computeCardBrandAcceptance(params: params)
139
+ if let allowedCardFundingTypes = StripeSdkImpl.computeAllowedCardFundingTypes(params: params) {
140
+ configuration.allowedCardFundingTypes = allowedCardFundingTypes
141
+ }
139
142
 
140
143
  // Parse custom payment method configuration
141
144
  if let customPaymentMethodConfig = params["customPaymentMethodConfiguration"] as? [String: Any] {
@@ -286,6 +289,30 @@ extension StripeSdkImpl {
286
289
  }
287
290
  }
288
291
 
292
+ internal static func computeAllowedCardFundingTypes(params: NSDictionary) -> PaymentSheet.CardFundingType? {
293
+ guard let cardFundingFiltering = params["cardFundingFiltering"] as? NSDictionary,
294
+ let allowedTypes = cardFundingFiltering["allowedCardFundingTypes"] as? [String] else {
295
+ return nil
296
+ }
297
+
298
+ var result: PaymentSheet.CardFundingType = []
299
+ for type in allowedTypes {
300
+ switch type {
301
+ case "debit":
302
+ result.insert(.debit)
303
+ case "credit":
304
+ result.insert(.credit)
305
+ case "prepaid":
306
+ result.insert(.prepaid)
307
+ case "unknown":
308
+ result.insert(.unknown)
309
+ default:
310
+ break
311
+ }
312
+ }
313
+ return result.isEmpty ? nil : result
314
+ }
315
+
289
316
  static func mapCaptureMethod(_ captureMethod: String?) -> PaymentSheet.IntentConfiguration.CaptureMethod {
290
317
  if let captureMethod = captureMethod {
291
318
  switch captureMethod {
@@ -1120,12 +1120,24 @@ public class StripeSdkImpl: NSObject, UIAdaptivePresentationControllerDelegate {
1120
1120
  self?.emitter?.emitOnFinancialConnectionsEvent(mappedEvent)
1121
1121
  }
1122
1122
 
1123
+ // Use connectedAccountId from params if provided
1124
+ let originalStripeAccount = STPAPIClient.shared.stripeAccount
1125
+ if let connectedAccountId = params["connectedAccountId"] as? String {
1126
+ STPAPIClient.shared.stripeAccount = connectedAccountId
1127
+ }
1128
+
1129
+ let wrappedResolve: RCTPromiseResolveBlock = { result in
1130
+ // Restore original stripeAccount after completion
1131
+ STPAPIClient.shared.stripeAccount = originalStripeAccount
1132
+ resolve(result)
1133
+ }
1134
+
1123
1135
  FinancialConnections.presentForToken(
1124
1136
  withClientSecret: clientSecret,
1125
1137
  returnURL: returnURL,
1126
1138
  configuration: configuration,
1127
1139
  onEvent: onEvent,
1128
- resolve: resolve
1140
+ resolve: wrappedResolve
1129
1141
  )
1130
1142
  }
1131
1143
 
@@ -1154,12 +1166,24 @@ public class StripeSdkImpl: NSObject, UIAdaptivePresentationControllerDelegate {
1154
1166
  self?.emitter?.emitOnFinancialConnectionsEvent(mappedEvent)
1155
1167
  }
1156
1168
 
1169
+ // Use connectedAccountId from params if provided
1170
+ let originalStripeAccount = STPAPIClient.shared.stripeAccount
1171
+ if let connectedAccountId = params["connectedAccountId"] as? String {
1172
+ STPAPIClient.shared.stripeAccount = connectedAccountId
1173
+ }
1174
+
1175
+ let wrappedResolve: RCTPromiseResolveBlock = { result in
1176
+ // Restore original stripeAccount after completion
1177
+ STPAPIClient.shared.stripeAccount = originalStripeAccount
1178
+ resolve(result)
1179
+ }
1180
+
1157
1181
  FinancialConnections.present(
1158
1182
  withClientSecret: clientSecret,
1159
1183
  returnURL: returnURL,
1160
1184
  configuration: configuration,
1161
1185
  onEvent: onEvent,
1162
- resolve: resolve
1186
+ resolve: wrappedResolve
1163
1187
  )
1164
1188
  }
1165
1189
 
package/jest/mock.js CHANGED
@@ -255,6 +255,12 @@ const StripeConstants = {
255
255
  Allowed: 'allowed',
256
256
  Disallowed: 'disallowed',
257
257
  },
258
+ CardFundingType: {
259
+ Debit: 'debit',
260
+ Credit: 'credit',
261
+ Prepaid: 'prepaid',
262
+ Unknown: 'unknown',
263
+ },
258
264
  CustomPaymentMethodResultStatus: {
259
265
  Completed: 'completed',
260
266
  Canceled: 'canceled',
package/jest/setup.js ADDED
@@ -0,0 +1,30 @@
1
+ /* eslint-disable no-undef */
2
+ /**
3
+ * Jest setup file to mock native modules
4
+ */
5
+
6
+ // Mock OnrampSdk TurboModule
7
+ jest.mock('../src/specs/NativeOnrampSdkModule', () => ({
8
+ __esModule: true,
9
+ default: {
10
+ initialise: jest.fn(),
11
+ configureOnramp: jest.fn(),
12
+ hasLinkAccount: jest.fn(),
13
+ registerLinkUser: jest.fn(),
14
+ registerWalletAddress: jest.fn(),
15
+ attachKycInfo: jest.fn(),
16
+ presentKycInfoVerification: jest.fn(),
17
+ updatePhoneNumber: jest.fn(),
18
+ authenticateUser: jest.fn(),
19
+ authenticateUserWithToken: jest.fn(),
20
+ verifyIdentity: jest.fn(),
21
+ collectPaymentMethod: jest.fn(),
22
+ provideCheckoutClientSecret: jest.fn(),
23
+ onCheckoutClientSecretRequested: null,
24
+ createCryptoPaymentToken: jest.fn(),
25
+ performCheckout: jest.fn(),
26
+ onrampAuthorize: jest.fn(),
27
+ getCryptoTokenDisplayData: jest.fn(),
28
+ logout: jest.fn(),
29
+ },
30
+ }));