@stripe/stripe-react-native 0.47.1 → 0.49.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 (47) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/android/gradle.properties +1 -1
  3. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementView.kt +85 -65
  4. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +18 -2
  5. package/android/src/main/java/com/reactnativestripesdk/PaymentOptionDisplayDataMapper.kt +0 -2
  6. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +83 -3
  7. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +25 -0
  8. package/ios/PaymentSheetAppearance.swift +46 -1
  9. package/ios/StripeSdkImpl+PaymentSheet.swift +43 -5
  10. package/ios/StripeSdkImpl.swift +5 -1
  11. package/lib/commonjs/components/CustomerSheet.js +1 -1
  12. package/lib/commonjs/components/CustomerSheet.js.map +1 -1
  13. package/lib/commonjs/events.js.map +1 -1
  14. package/lib/commonjs/functions.js +1 -1
  15. package/lib/commonjs/functions.js.map +1 -1
  16. package/lib/commonjs/hooks/useStripe.js +1 -1
  17. package/lib/commonjs/hooks/useStripe.js.map +1 -1
  18. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  19. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  20. package/lib/commonjs/types/PaymentSheet.js +1 -1
  21. package/lib/commonjs/types/PaymentSheet.js.map +1 -1
  22. package/lib/module/components/CustomerSheet.js +1 -1
  23. package/lib/module/components/CustomerSheet.js.map +1 -1
  24. package/lib/module/events.js.map +1 -1
  25. package/lib/module/functions.js +1 -1
  26. package/lib/module/functions.js.map +1 -1
  27. package/lib/module/hooks/useStripe.js +1 -1
  28. package/lib/module/hooks/useStripe.js.map +1 -1
  29. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  30. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  31. package/lib/module/types/PaymentSheet.js +1 -1
  32. package/lib/module/types/PaymentSheet.js.map +1 -1
  33. package/lib/typescript/src/events.d.ts +1 -1
  34. package/lib/typescript/src/events.d.ts.map +1 -1
  35. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +27 -0
  36. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
  37. package/lib/typescript/src/types/PaymentIntent.d.ts +1 -1
  38. package/lib/typescript/src/types/PaymentIntent.d.ts.map +1 -1
  39. package/lib/typescript/src/types/PaymentSheet.d.ts +30 -2
  40. package/lib/typescript/src/types/PaymentSheet.d.ts.map +1 -1
  41. package/package-lock.json +14114 -0
  42. package/package.json +1 -1
  43. package/src/events.ts +1 -0
  44. package/src/types/EmbeddedPaymentElement.tsx +47 -2
  45. package/src/types/PaymentIntent.ts +1 -1
  46. package/src/types/PaymentSheet.ts +35 -1
  47. package/stripe-react-native.podspec +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # CHANGELOG
2
- ## 0.47.1 - 2025-29-2025
2
+
3
+ ## 0.49.0 - 2025-07-02
4
+
5
+ **Features**
6
+ - Added rowSelectionBehavior to `EmbeddedPaymentElementConfiguration` with `immediateAction` option
7
+ - Added `flatWithChevron` to `AppearanceParams.embeddedPaymentElement.rowConfig.style`
8
+ - Added `PaymentMethodOptions` to `PaymentMode` to enable setting payment method level setup future usage value
9
+ - Added `None` to `FutureUsage`
10
+
11
+ ## 0.48.0 - 2025-06-11
12
+
13
+ **Feature**
14
+ - Added `height` to `PrimaryButtonConfig.shapes`.
15
+ - Added `formInsetValues` to `AppearanceParams`.
16
+
17
+ ## 0.47.1 - 2025-05-29
3
18
 
4
19
  **Fixes**
5
20
  - Fixed EmbeddedPaymentElement unable to present view controllers after navigating back and forth between screens.
@@ -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=21.15.+
6
+ StripeSdk_stripeVersion=21.19.+
@@ -7,6 +7,7 @@ import androidx.compose.runtime.Composable
7
7
  import androidx.compose.runtime.LaunchedEffect
8
8
  import androidx.compose.runtime.getValue
9
9
  import androidx.compose.runtime.mutableIntStateOf
10
+ import androidx.compose.runtime.mutableStateOf
10
11
  import androidx.compose.runtime.remember
11
12
  import androidx.compose.runtime.setValue
12
13
  import androidx.compose.ui.Modifier
@@ -20,7 +21,6 @@ import com.facebook.react.uimanager.ThemedReactContext
20
21
  import com.reactnativestripesdk.utils.KeepJsAwakeTask
21
22
  import com.reactnativestripesdk.utils.mapFromPaymentMethod
22
23
  import com.stripe.android.paymentelement.EmbeddedPaymentElement
23
- import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
24
24
  import com.stripe.android.paymentelement.rememberEmbeddedPaymentElement
25
25
  import com.stripe.android.paymentsheet.CreateIntentResult
26
26
  import com.stripe.android.paymentsheet.PaymentSheet
@@ -29,7 +29,11 @@ import kotlinx.coroutines.channels.Channel
29
29
  import kotlinx.coroutines.flow.consumeAsFlow
30
30
  import toWritableMap
31
31
 
32
- @OptIn(ExperimentalEmbeddedPaymentElementApi::class)
32
+ enum class RowSelectionBehaviorType {
33
+ Default,
34
+ ImmediateAction,
35
+ }
36
+
33
37
  class EmbeddedPaymentElementView(
34
38
  context: Context,
35
39
  ) : StripeAbstractComposeView(context) {
@@ -47,75 +51,91 @@ class EmbeddedPaymentElementView(
47
51
  var latestIntentConfig: PaymentSheet.IntentConfiguration? = null
48
52
  var latestElementConfig: EmbeddedPaymentElement.Configuration? = null
49
53
 
54
+ val rowSelectionBehaviorType = mutableStateOf<RowSelectionBehaviorType?>(null)
55
+
50
56
  private val reactContext get() = context as ThemedReactContext
51
57
  private val events = Channel<Event>(Channel.UNLIMITED)
52
58
 
53
- private val builder by lazy {
54
- EmbeddedPaymentElement.Builder(
55
- createIntentCallback = { paymentMethod, shouldSavePaymentMethod ->
56
- val stripeSdkModule =
57
- try {
58
- requireStripeSdkModule()
59
- } catch (ex: IllegalArgumentException) {
60
- return@Builder CreateIntentResult.Failure(
61
- cause =
62
- Exception(
63
- "Tried to call confirmHandler, but no callback was found. Please file an issue: https://github.com/stripe/stripe-react-native/issues",
64
- ),
65
- displayMessage = "An unexpected error occurred",
66
- )
67
- }
68
-
69
- // Make sure that JS is active since the activity will be paused when stripe ui is presented.
70
- val keepJsAwakeTask = KeepJsAwakeTask(reactContext.reactApplicationContext).apply { start() }
71
-
72
- val params =
73
- Arguments.createMap().apply {
74
- putMap("paymentMethod", mapFromPaymentMethod(paymentMethod))
75
- putBoolean("shouldSavePaymentMethod", shouldSavePaymentMethod)
76
- }
77
-
78
- stripeSdkModule.emitOnConfirmHandlerCallback(params)
79
-
80
- val resultFromJavascript = stripeSdkModule.embeddedIntentCreationCallback.await()
81
- // reset the completable
82
- stripeSdkModule.embeddedIntentCreationCallback = CompletableDeferred()
83
-
84
- keepJsAwakeTask.stop()
85
-
86
- resultFromJavascript.getString("clientSecret")?.let {
87
- CreateIntentResult.Success(clientSecret = it)
88
- } ?: run {
89
- val errorMap = resultFromJavascript.getMap("error")
90
- CreateIntentResult.Failure(
91
- cause = Exception(errorMap?.getString("message")),
92
- displayMessage = errorMap?.getString("localizedMessage"),
93
- )
94
- }
95
- },
96
- resultCallback = { result ->
97
- val map =
98
- Arguments.createMap().apply {
99
- when (result) {
100
- is EmbeddedPaymentElement.Result.Completed -> {
101
- putString("status", "completed")
102
- }
103
- is EmbeddedPaymentElement.Result.Canceled -> {
104
- putString("status", "canceled")
59
+ @Composable
60
+ override fun Content() {
61
+ val type by remember { rowSelectionBehaviorType }
62
+ val builder =
63
+ remember(type) {
64
+ EmbeddedPaymentElement
65
+ .Builder(
66
+ createIntentCallback = { paymentMethod, shouldSavePaymentMethod ->
67
+ val stripeSdkModule =
68
+ try {
69
+ requireStripeSdkModule()
70
+ } catch (ex: IllegalArgumentException) {
71
+ return@Builder CreateIntentResult.Failure(
72
+ cause =
73
+ Exception(
74
+ "Tried to call confirmHandler, but no callback was found. Please file an issue: https://github.com/stripe/stripe-react-native/issues",
75
+ ),
76
+ displayMessage = "An unexpected error occurred",
77
+ )
78
+ }
79
+
80
+ // Make sure that JS is active since the activity will be paused when stripe ui is presented.
81
+ val keepJsAwakeTask =
82
+ KeepJsAwakeTask(reactContext.reactApplicationContext).apply { start() }
83
+
84
+ val params =
85
+ Arguments.createMap().apply {
86
+ putMap("paymentMethod", mapFromPaymentMethod(paymentMethod))
87
+ putBoolean("shouldSavePaymentMethod", shouldSavePaymentMethod)
88
+ }
89
+
90
+ stripeSdkModule.emitOnConfirmHandlerCallback(params)
91
+
92
+ val resultFromJavascript = stripeSdkModule.embeddedIntentCreationCallback.await()
93
+ // reset the completable
94
+ stripeSdkModule.embeddedIntentCreationCallback = CompletableDeferred()
95
+
96
+ keepJsAwakeTask.stop()
97
+
98
+ resultFromJavascript.getString("clientSecret")?.let {
99
+ CreateIntentResult.Success(clientSecret = it)
100
+ } ?: run {
101
+ val errorMap = resultFromJavascript.getMap("error")
102
+ CreateIntentResult.Failure(
103
+ cause = Exception(errorMap?.getString("message")),
104
+ displayMessage = errorMap?.getString("localizedMessage"),
105
+ )
105
106
  }
106
- is EmbeddedPaymentElement.Result.Failed -> {
107
- putString("status", "failed")
108
- putString("error", result.error.message ?: "Unknown error")
107
+ },
108
+ resultCallback = { result ->
109
+ val map =
110
+ Arguments.createMap().apply {
111
+ when (result) {
112
+ is EmbeddedPaymentElement.Result.Completed -> {
113
+ putString("status", "completed")
114
+ }
115
+
116
+ is EmbeddedPaymentElement.Result.Canceled -> {
117
+ putString("status", "canceled")
118
+ }
119
+
120
+ is EmbeddedPaymentElement.Result.Failed -> {
121
+ putString("status", "failed")
122
+ putString("error", result.error.message ?: "Unknown error")
123
+ }
124
+ }
125
+ }
126
+ requireStripeSdkModule().emitEmbeddedPaymentElementFormSheetConfirmComplete(map)
127
+ },
128
+ ).rowSelectionBehavior(
129
+ if (type == RowSelectionBehaviorType.Default) {
130
+ EmbeddedPaymentElement.RowSelectionBehavior.default()
131
+ } else {
132
+ EmbeddedPaymentElement.RowSelectionBehavior.immediateAction {
133
+ requireStripeSdkModule().emitEmbeddedPaymentElementRowSelectionImmediateAction()
109
134
  }
110
- }
111
- }
112
- requireStripeSdkModule().emitEmbeddedPaymentElementFormSheetConfirmComplete(map)
113
- },
114
- )
115
- }
135
+ },
136
+ )
137
+ }
116
138
 
117
- @Composable
118
- override fun Content() {
119
139
  val embedded = rememberEmbeddedPaymentElement(builder)
120
140
  var height by remember {
121
141
  mutableIntStateOf(0)
@@ -21,10 +21,8 @@ import com.reactnativestripesdk.utils.mapToPreferredNetworks
21
21
  import com.reactnativestripesdk.utils.toBundleObject
22
22
  import com.stripe.android.ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi
23
23
  import com.stripe.android.paymentelement.EmbeddedPaymentElement
24
- import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
25
24
  import com.stripe.android.paymentsheet.PaymentSheet
26
25
 
27
- @OptIn(ExperimentalEmbeddedPaymentElementApi::class)
28
26
  @ReactModule(name = EmbeddedPaymentElementViewManager.NAME)
29
27
  class EmbeddedPaymentElementViewManager :
30
28
  ViewGroupManager<EmbeddedPaymentElementView>(),
@@ -54,6 +52,9 @@ class EmbeddedPaymentElementViewManager :
54
52
  view: EmbeddedPaymentElementView,
55
53
  cfg: Dynamic,
56
54
  ) {
55
+ val rowSelectionBehaviorType = parseRowSelectionBehavior(cfg.asMap())
56
+ view.rowSelectionBehaviorType.value = rowSelectionBehaviorType
57
+
57
58
  val elementConfig = parseElementConfiguration(cfg.asMap(), view.context)
58
59
  view.latestElementConfig = elementConfig
59
60
  // if intentConfig is already set, configure immediately:
@@ -190,6 +191,21 @@ class EmbeddedPaymentElementViewManager :
190
191
  return configurationBuilder.build()
191
192
  }
192
193
 
194
+ private fun parseRowSelectionBehavior(map: ReadableMap): RowSelectionBehaviorType {
195
+ val rowSelectionBehavior =
196
+ map
197
+ .getMap("rowSelectionBehavior")
198
+ ?.getString("type")
199
+ ?.let { type ->
200
+ when (type) {
201
+ "immediateAction" -> RowSelectionBehaviorType.ImmediateAction
202
+ else -> RowSelectionBehaviorType.Default
203
+ }
204
+ }
205
+ ?: RowSelectionBehaviorType.Default
206
+ return rowSelectionBehavior
207
+ }
208
+
193
209
  private fun parseIntentConfiguration(map: ReadableMap): PaymentSheet.IntentConfiguration {
194
210
  val intentConfig = PaymentSheetFragment.buildIntentConfiguration(toBundleObject(map))
195
211
  return intentConfig ?: throw IllegalArgumentException("IntentConfiguration is null")
@@ -2,13 +2,11 @@ import com.facebook.react.bridge.Arguments
2
2
  import com.facebook.react.bridge.WritableMap
3
3
  import com.reactnativestripesdk.utils.mapFromPaymentSheetBillingDetails
4
4
  import com.stripe.android.paymentelement.EmbeddedPaymentElement
5
- import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
6
5
 
7
6
  /**
8
7
  * Serialize Stripe's PaymentOptionDisplayData into a WritableMap
9
8
  * that can be sent over the RN bridge.
10
9
  */
11
- @OptIn(ExperimentalEmbeddedPaymentElementApi::class)
12
10
  fun EmbeddedPaymentElement.PaymentOptionDisplayData.toWritableMap(): WritableMap =
13
11
  Arguments.createMap().apply {
14
12
  putString("label", label)
@@ -6,8 +6,9 @@ import android.content.res.Configuration
6
6
  import android.graphics.Color
7
7
  import android.os.Bundle
8
8
  import com.reactnativestripesdk.utils.PaymentSheetAppearanceException
9
- import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
9
+ import com.stripe.android.paymentelement.AppearanceAPIAdditionsPreview
10
10
  import com.stripe.android.paymentsheet.PaymentSheet
11
+ import com.stripe.android.uicore.StripeThemeDefaults
11
12
 
12
13
  @SuppressLint("RestrictedApi")
13
14
  fun buildPaymentSheetAppearance(
@@ -17,6 +18,7 @@ fun buildPaymentSheetAppearance(
17
18
  val colorParams = userParams?.getBundle(PaymentSheetAppearanceKeys.COLORS)
18
19
  val lightColorParams = colorParams?.getBundle(PaymentSheetAppearanceKeys.LIGHT) ?: colorParams
19
20
  val darkColorParams = colorParams?.getBundle(PaymentSheetAppearanceKeys.DARK) ?: colorParams
21
+ val insetParams = userParams?.getBundle(PaymentSheetAppearanceKeys.FORM_INSETS)
20
22
 
21
23
  val embeddedAppearance =
22
24
  buildEmbeddedAppearance(
@@ -37,6 +39,7 @@ fun buildPaymentSheetAppearance(
37
39
  context,
38
40
  ),
39
41
  embeddedAppearance = embeddedAppearance,
42
+ formInsetValues = buildFormInsets(insetParams),
40
43
  )
41
44
  }
42
45
 
@@ -50,9 +53,11 @@ fun buildPaymentSheetAppearance(
50
53
  userParams?.getBundle(PaymentSheetAppearanceKeys.PRIMARY_BUTTON),
51
54
  context,
52
55
  ),
56
+ formInsetValues = buildFormInsets(insetParams),
53
57
  )
54
58
  }
55
59
 
60
+ @OptIn(AppearanceAPIAdditionsPreview::class)
56
61
  private fun buildTypography(
57
62
  fontParams: Bundle?,
58
63
  context: Context,
@@ -198,6 +203,7 @@ private fun buildPrimaryButton(
198
203
  getFloatOrNull(shapeParams, PaymentSheetAppearanceKeys.BORDER_RADIUS),
199
204
  borderStrokeWidthDp =
200
205
  getFloatOrNull(shapeParams, PaymentSheetAppearanceKeys.BORDER_WIDTH),
206
+ heightDp = getFloatOrNull(shapeParams, PaymentSheetAppearanceKeys.HEIGHT),
201
207
  ),
202
208
  typography =
203
209
  PaymentSheet.PrimaryButtonTypography(
@@ -239,7 +245,6 @@ private fun buildPrimaryButtonColors(
239
245
  ),
240
246
  )
241
247
 
242
- @OptIn(ExperimentalEmbeddedPaymentElementApi::class)
243
248
  @SuppressLint("RestrictedApi")
244
249
  @Throws(PaymentSheetAppearanceException::class)
245
250
  private fun buildEmbeddedAppearance(
@@ -336,7 +341,7 @@ private fun buildEmbeddedAppearance(
336
341
  val checkmarkParams = getBundleOrNull(flatParams, PaymentSheetAppearanceKeys.CHECKMARK)
337
342
  val separatorInsetsParams = getBundleOrNull(flatParams, PaymentSheetAppearanceKeys.SEPARATOR_INSETS)
338
343
 
339
- // Default separator insets specific to FlatWithCheckmark
344
+ // Default separator insets specific to FlatWithCheckmark and FlatWithChevron
340
345
  val defaultSeparatorStartInsetDp = 0.0f
341
346
  val defaultSeparatorEndInsetDp = 0.0f
342
347
 
@@ -387,6 +392,59 @@ private fun buildEmbeddedAppearance(
387
392
  colorsDark = flatCheckmarkColors,
388
393
  )
389
394
  }
395
+ "flatWithChevron" -> {
396
+ val flatParams = getBundleOrNull(rowParams, PaymentSheetAppearanceKeys.FLAT)
397
+ val chevronParams = getBundleOrNull(flatParams, PaymentSheetAppearanceKeys.CHEVRON)
398
+ val separatorInsetsParams = getBundleOrNull(flatParams, PaymentSheetAppearanceKeys.SEPARATOR_INSETS)
399
+
400
+ // Default separator insets specific to FlatWithCheckmark and FlatWithChevron
401
+ val defaultSeparatorStartInsetDp = 0.0f
402
+ val defaultSeparatorEndInsetDp = 0.0f
403
+
404
+ // Parse dimensions as Floats
405
+ val separatorThickness = getFloatOr(flatParams, PaymentSheetAppearanceKeys.SEPARATOR_THICKNESS, defaultSeparatorThicknessDp)
406
+ val startSeparatorInset = getFloatOr(separatorInsetsParams, PaymentSheetAppearanceKeys.LEFT, defaultSeparatorStartInsetDp)
407
+ val endSeparatorInset = getFloatOr(separatorInsetsParams, PaymentSheetAppearanceKeys.RIGHT, defaultSeparatorEndInsetDp)
408
+
409
+ // Parse booleans
410
+ val topEnabled = getBooleanOr(flatParams, PaymentSheetAppearanceKeys.TOP_SEPARATOR_ENABLED, true)
411
+ val bottomEnabled = getBooleanOr(flatParams, PaymentSheetAppearanceKeys.BOTTOM_SEPARATOR_ENABLED, true)
412
+
413
+ val parsedSeparatorColor =
414
+ dynamicColorFromParams(
415
+ context,
416
+ flatParams,
417
+ PaymentSheetAppearanceKeys.SEPARATOR_COLOR,
418
+ Color.GRAY,
419
+ )
420
+
421
+ val parsedChevronColor =
422
+ dynamicColorFromParams(
423
+ context,
424
+ chevronParams,
425
+ PaymentSheetAppearanceKeys.COLOR,
426
+ defaultColors.componentBorder, // Default to component border color like other elements
427
+ )
428
+
429
+ // Create the required Colors object
430
+ val flatChevronColors =
431
+ PaymentSheet.Appearance.Embedded.RowStyle.FlatWithChevron.Colors(
432
+ separatorColor = parsedSeparatorColor,
433
+ chevronColor = parsedChevronColor,
434
+ )
435
+
436
+ PaymentSheet.Appearance.Embedded.RowStyle.FlatWithChevron(
437
+ separatorThicknessDp = separatorThickness,
438
+ startSeparatorInsetDp = startSeparatorInset,
439
+ endSeparatorInsetDp = endSeparatorInset,
440
+ topSeparatorEnabled = topEnabled,
441
+ bottomSeparatorEnabled = bottomEnabled,
442
+ additionalVerticalInsetsDp = additionalInsets,
443
+ horizontalInsetsDp = 0.0F, // We do not have an iOS equal for this API so it's not configurable in React Native
444
+ colorsLight = flatChevronColors,
445
+ colorsDark = flatChevronColors,
446
+ )
447
+ }
390
448
  "floatingButton" -> {
391
449
  val floatingParams = getBundleOrNull(rowParams, PaymentSheetAppearanceKeys.FLOATING)
392
450
  PaymentSheet.Appearance.Embedded.RowStyle.FloatingButton(
@@ -403,6 +461,22 @@ private fun buildEmbeddedAppearance(
403
461
  return PaymentSheet.Appearance.Embedded(style = rowStyle)
404
462
  }
405
463
 
464
+ @SuppressLint("RestrictedApi")
465
+ private fun buildFormInsets(insetParams: Bundle?): PaymentSheet.Insets {
466
+ val defaults = StripeThemeDefaults.formInsets
467
+ val left = getFloatOr(insetParams, PaymentSheetAppearanceKeys.LEFT, defaults.start)
468
+ val top = getFloatOr(insetParams, PaymentSheetAppearanceKeys.TOP, defaults.top)
469
+ val right = getFloatOr(insetParams, PaymentSheetAppearanceKeys.RIGHT, defaults.end)
470
+ val bottom = getFloatOr(insetParams, PaymentSheetAppearanceKeys.BOTTOM, defaults.bottom)
471
+
472
+ return PaymentSheet.Insets(
473
+ startDp = left,
474
+ topDp = top,
475
+ endDp = right,
476
+ bottomDp = bottom,
477
+ )
478
+ }
479
+
406
480
  /**
407
481
  * Pulls a light/dark hex‑string map out of [params],
408
482
  * chooses the right one based on the current UI mode,
@@ -564,6 +638,7 @@ private class PaymentSheetAppearanceKeys {
564
638
  const val SHAPES = "shapes"
565
639
  const val BORDER_RADIUS = "borderRadius"
566
640
  const val BORDER_WIDTH = "borderWidth"
641
+ const val HEIGHT = "height"
567
642
 
568
643
  const val PRIMARY_BUTTON = "primaryButton"
569
644
  const val TEXT = "text"
@@ -584,6 +659,7 @@ private class PaymentSheetAppearanceKeys {
584
659
  const val SELECTED_COLOR = "selectedColor"
585
660
  const val UNSELECTED_COLOR = "unselectedColor"
586
661
  const val CHECKMARK = "checkmark"
662
+ const val CHEVRON = "chevron"
587
663
  const val COLOR = "color"
588
664
  const val CHECKMARK_INSET = "inset"
589
665
 
@@ -593,5 +669,9 @@ private class PaymentSheetAppearanceKeys {
593
669
  // Keys for EdgeInsetsConfig
594
670
  const val LEFT = "left"
595
671
  const val RIGHT = "right"
672
+ const val TOP = "top"
673
+ const val BOTTOM = "bottom"
674
+
675
+ const val FORM_INSETS = "formInsetValues"
596
676
  }
597
677
  }
@@ -32,6 +32,8 @@ import com.reactnativestripesdk.utils.mapFromPaymentMethod
32
32
  import com.reactnativestripesdk.utils.mapToPreferredNetworks
33
33
  import com.reactnativestripesdk.utils.removeFragment
34
34
  import com.stripe.android.ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi
35
+ import com.stripe.android.model.PaymentMethod
36
+ import com.stripe.android.paymentelement.PaymentMethodOptionsSetupFutureUsagePreview
35
37
  import com.stripe.android.paymentsheet.CreateIntentCallback
36
38
  import com.stripe.android.paymentsheet.CreateIntentResult
37
39
  import com.stripe.android.paymentsheet.ExperimentalCustomerSessionApi
@@ -518,6 +520,7 @@ class PaymentSheetFragment : StripeFragment() {
518
520
  )
519
521
  }
520
522
 
523
+ @OptIn(PaymentMethodOptionsSetupFutureUsagePreview::class)
521
524
  private fun buildIntentConfigurationMode(modeParams: Bundle): PaymentSheet.IntentConfiguration.Mode {
522
525
  val currencyCode =
523
526
  modeParams.getString("currencyCode")
@@ -531,6 +534,7 @@ class PaymentSheetFragment : StripeFragment() {
531
534
  currency = currencyCode,
532
535
  setupFutureUse = mapToSetupFutureUse(modeParams.getString("setupFutureUsage")),
533
536
  captureMethod = mapToCaptureMethod(modeParams.getString("captureMethod")),
537
+ paymentMethodOptions = mapToPaymentMethodOptions(modeParams.getBundle("paymentMethodOptions")),
534
538
  )
535
539
  } else {
536
540
  val setupFutureUsage =
@@ -639,6 +643,7 @@ fun mapToSetupFutureUse(type: String?): PaymentSheet.IntentConfiguration.SetupFu
639
643
  when (type) {
640
644
  "OffSession" -> PaymentSheet.IntentConfiguration.SetupFutureUse.OffSession
641
645
  "OnSession" -> PaymentSheet.IntentConfiguration.SetupFutureUse.OnSession
646
+ "None" -> PaymentSheet.IntentConfiguration.SetupFutureUse.None
642
647
  else -> null
643
648
  }
644
649
 
@@ -650,6 +655,26 @@ fun mapToCaptureMethod(type: String?): PaymentSheet.IntentConfiguration.CaptureM
650
655
  else -> PaymentSheet.IntentConfiguration.CaptureMethod.Automatic
651
656
  }
652
657
 
658
+ @OptIn(PaymentMethodOptionsSetupFutureUsagePreview::class)
659
+ fun mapToPaymentMethodOptions(options: Bundle?): PaymentSheet.IntentConfiguration.Mode.Payment.PaymentMethodOptions? {
660
+ val sfuBundle = options?.getBundle("setupFutureUsageValues")
661
+ val paymentMethodToSfuMap = mutableMapOf<PaymentMethod.Type, PaymentSheet.IntentConfiguration.SetupFutureUse>()
662
+ sfuBundle?.keySet()?.forEach { code ->
663
+ val sfuValue = mapToSetupFutureUse(sfuBundle?.getString(code))
664
+ val paymentMethodType = PaymentMethod.Type.fromCode(code)
665
+ if (paymentMethodType != null && sfuValue != null) {
666
+ paymentMethodToSfuMap[paymentMethodType] = sfuValue
667
+ }
668
+ }
669
+ return if (paymentMethodToSfuMap.isNotEmpty()) {
670
+ PaymentSheet.IntentConfiguration.Mode.Payment.PaymentMethodOptions(
671
+ setupFutureUsageValues = paymentMethodToSfuMap,
672
+ )
673
+ } else {
674
+ null
675
+ }
676
+ }
677
+
653
678
  fun mapToCardBrandAcceptance(params: Bundle?): PaymentSheet.CardBrandAcceptance {
654
679
  val cardBrandAcceptanceParams = params?.getBundle("cardBrandAcceptance") ?: return PaymentSheet.CardBrandAcceptance.all()
655
680
  val filter = cardBrandAcceptanceParams.getString("filter") ?: return PaymentSheet.CardBrandAcceptance.all()
@@ -33,6 +33,10 @@ internal class PaymentSheetAppearance {
33
33
  appearance.embeddedPaymentElement = try buildEmbeddedPaymentElementAppearance(params: embeddedPaymentElementParams)
34
34
  }
35
35
 
36
+ if let formInsetParams = userParams[PaymentSheetAppearanceKeys.FORM_INSETS] as? NSDictionary {
37
+ appearance.formInsets = try buildFormInsets(params: formInsetParams)
38
+ }
39
+
36
40
  return appearance
37
41
  }
38
42
 
@@ -114,6 +118,9 @@ internal class PaymentSheetAppearance {
114
118
  if let shadowParams = shapeParams[PaymentSheetAppearanceKeys.SHADOW] as? NSDictionary {
115
119
  primaryButton.shadow = try buildShadow(params: shadowParams)
116
120
  }
121
+ if let height = shapeParams[PaymentSheetAppearanceKeys.HEIGHT] as? CGFloat {
122
+ primaryButton.height = height
123
+ }
117
124
  }
118
125
  if let colorParams = params[PaymentSheetAppearanceKeys.COLORS] as? NSDictionary {
119
126
  if (colorParams.object(forKey: PaymentSheetAppearanceKeys.LIGHT) != nil && colorParams.object(forKey: PaymentSheetAppearanceKeys.DARK) == nil ||
@@ -178,6 +185,8 @@ internal class PaymentSheetAppearance {
178
185
  row.style = .floatingButton
179
186
  case PaymentSheetAppearanceKeys.ROW_STYLE_FLAT_WITH_CHECKMARK:
180
187
  row.style = .flatWithCheckmark
188
+ case PaymentSheetAppearanceKeys.ROW_STYLE_FLAT_WITH_CHEVRON:
189
+ row.style = .flatWithChevron
181
190
  default:
182
191
  throw PaymentSheetAppearanceError.invalidRowStyle(styleString)
183
192
  }
@@ -232,6 +241,10 @@ internal class PaymentSheetAppearance {
232
241
  flat.checkmark = try buildEmbeddedCheckmark(params: checkmarkParams)
233
242
  }
234
243
 
244
+ if let chevronParams = params[PaymentSheetAppearanceKeys.CHEVRON] as? NSDictionary {
245
+ flat.chevron = try buildEmbeddedChevron(params: chevronParams)
246
+ }
247
+
235
248
  return flat
236
249
  }
237
250
 
@@ -270,6 +283,19 @@ internal class PaymentSheetAppearance {
270
283
  return checkmark
271
284
  }
272
285
 
286
+ private class func buildEmbeddedChevron(params: NSDictionary) throws -> PaymentSheet.Appearance.EmbeddedPaymentElement.Row.Flat.Chevron {
287
+ var chevron = PaymentSheet.Appearance.default.embeddedPaymentElement.row.flat.chevron
288
+
289
+ if let colorHexes = params[PaymentSheetAppearanceKeys.COLOR] as? [String: String] {
290
+ chevron.color = dynamicColor(
291
+ from: colorHexes,
292
+ default: UIColor.systemGray // Default iOS system gray color
293
+ )
294
+ }
295
+
296
+ return chevron
297
+ }
298
+
273
299
  private class func buildEmbeddedFloating(params: NSDictionary) throws -> PaymentSheet.Appearance.EmbeddedPaymentElement.Row.Floating {
274
300
  var floating = PaymentSheet.Appearance.default.embeddedPaymentElement.row.floating
275
301
 
@@ -314,6 +340,20 @@ internal class PaymentSheetAppearance {
314
340
  return defaultColor
315
341
  })
316
342
  }
343
+
344
+ private class func buildFormInsets(params: NSDictionary) throws -> NSDirectionalEdgeInsets {
345
+ let top = params[PaymentSheetAppearanceKeys.TOP] as? CGFloat ?? PaymentSheet.Appearance.default.formInsets.top
346
+ let leading = params[PaymentSheetAppearanceKeys.LEFT] as? CGFloat ?? PaymentSheet.Appearance.default.formInsets.leading
347
+ let bottom = params[PaymentSheetAppearanceKeys.BOTTOM] as? CGFloat ?? PaymentSheet.Appearance.default.formInsets.bottom
348
+ let trailing = params[PaymentSheetAppearanceKeys.RIGHT] as? CGFloat ?? PaymentSheet.Appearance.default.formInsets.trailing
349
+
350
+ return NSDirectionalEdgeInsets(
351
+ top: top,
352
+ leading: leading,
353
+ bottom: bottom,
354
+ trailing: trailing
355
+ )
356
+ }
317
357
  }
318
358
 
319
359
  enum PaymentSheetAppearanceError : Error {
@@ -333,7 +373,7 @@ extension PaymentSheetAppearanceError: LocalizedError {
333
373
  case .unexpectedHexStringLength(let hexString):
334
374
  return NSLocalizedString("Failed to set Payment Sheet appearance. Expected hex string of length 6 or 8, but received: \(hexString)", comment: "Failed to set color")
335
375
  case .invalidRowStyle(let styleString):
336
- return NSLocalizedString("Failed to set Embedded Payment Element appearance. Invalid row style '\(styleString)'. Expected one of: 'flatWithRadio', 'floatingButton', 'flatWithCheckmark'.", comment: "Invalid row style string")
376
+ return NSLocalizedString("Failed to set Embedded Payment Element appearance. Invalid row style '\(styleString)'. Expected one of: 'flatWithRadio', 'floatingButton', 'flatWithCheckmark', 'flatWithChevron'.", comment: "Invalid row style string")
337
377
  }
338
378
  }
339
379
  }
@@ -361,6 +401,7 @@ private struct PaymentSheetAppearanceKeys {
361
401
  static let SHAPES = "shapes"
362
402
  static let BORDER_RADIUS = "borderRadius"
363
403
  static let BORDER_WIDTH = "borderWidth"
404
+ static let HEIGHT = "height"
364
405
 
365
406
  static let SHADOW = "shadow"
366
407
  static let SHADOW_COLOR = "color"
@@ -389,6 +430,7 @@ private struct PaymentSheetAppearanceKeys {
389
430
  static let SELECTED_COLOR = "selectedColor"
390
431
  static let UNSELECTED_COLOR = "unselectedColor"
391
432
  static let CHECKMARK = "checkmark"
433
+ static let CHEVRON = "chevron"
392
434
  static let SPACING = "spacing"
393
435
  static let TOP = "top"
394
436
  static let LEFT = "left"
@@ -400,4 +442,7 @@ private struct PaymentSheetAppearanceKeys {
400
442
  static let ROW_STYLE_FLAT_WITH_RADIO = "flatWithRadio"
401
443
  static let ROW_STYLE_FLOATING_BUTTON = "floatingButton"
402
444
  static let ROW_STYLE_FLAT_WITH_CHECKMARK = "flatWithCheckmark"
445
+ static let ROW_STYLE_FLAT_WITH_CHEVRON = "flatWithChevron"
446
+
447
+ static let FORM_INSETS = "formInsetValues"
403
448
  }