@stripe/stripe-react-native 0.48.0 → 0.50.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 (139) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +1 -1
  3. package/android/gradle.properties +1 -1
  4. package/android/src/main/AndroidManifest.xml +10 -0
  5. package/android/src/main/java/com/reactnativestripesdk/CustomPaymentMethodActivity.kt +81 -0
  6. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementView.kt +171 -56
  7. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +34 -3
  8. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt +0 -17
  9. package/android/src/main/java/com/reactnativestripesdk/PaymentOptionDisplayDataMapper.kt +0 -2
  10. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +57 -3
  11. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +140 -19
  12. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +22 -0
  13. package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +52 -6
  14. package/android/src/main/res/values/styles.xml +27 -0
  15. package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +8 -0
  16. package/ios/ApplePayButtonManager.m +4 -0
  17. package/ios/ApplePayButtonView.swift +11 -4
  18. package/ios/Mappers.swift +0 -5
  19. package/ios/NewArch/ApplePayButtonComponentView.mm +6 -0
  20. package/ios/OldArch/StripeSdkEventEmitterCompat.h +1 -1
  21. package/ios/OldArch/StripeSdkEventEmitterCompat.m +7 -1
  22. package/ios/PaymentMethodFactory.swift +0 -17
  23. package/ios/PaymentSheetAppearance.swift +22 -1
  24. package/ios/StripeSdk.mm +7 -0
  25. package/ios/StripeSdkEmitter.swift +1 -0
  26. package/ios/StripeSdkImpl+Embedded.swift +39 -17
  27. package/ios/StripeSdkImpl+PaymentSheet.swift +156 -5
  28. package/ios/StripeSdkImpl.swift +43 -10
  29. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  30. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  31. package/lib/commonjs/components/AddressSheet.js +1 -1
  32. package/lib/commonjs/components/AddressSheet.js.map +1 -1
  33. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  34. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  35. package/lib/commonjs/components/CardField.js +1 -1
  36. package/lib/commonjs/components/CardField.js.map +1 -1
  37. package/lib/commonjs/components/CardForm.js +1 -1
  38. package/lib/commonjs/components/CardForm.js.map +1 -1
  39. package/lib/commonjs/components/CustomerSheet.js +1 -1
  40. package/lib/commonjs/components/CustomerSheet.js.map +1 -1
  41. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  42. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  43. package/lib/commonjs/components/StripeContainer.js +1 -1
  44. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  45. package/lib/commonjs/events.js.map +1 -1
  46. package/lib/commonjs/functions.js +1 -1
  47. package/lib/commonjs/functions.js.map +1 -1
  48. package/lib/commonjs/hooks/useStripe.js +1 -1
  49. package/lib/commonjs/hooks/useStripe.js.map +1 -1
  50. package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
  51. package/lib/commonjs/specs/NativeApplePayButton.js.map +1 -1
  52. package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
  53. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  54. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  55. package/lib/commonjs/types/PaymentIntent.js.map +1 -1
  56. package/lib/commonjs/types/PaymentSheet.js +1 -1
  57. package/lib/commonjs/types/PaymentSheet.js.map +1 -1
  58. package/lib/module/components/AddToWalletButton.js +1 -1
  59. package/lib/module/components/AddToWalletButton.js.map +1 -1
  60. package/lib/module/components/AddressSheet.js +1 -1
  61. package/lib/module/components/AddressSheet.js.map +1 -1
  62. package/lib/module/components/AuBECSDebitForm.js +1 -1
  63. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  64. package/lib/module/components/CardField.js +1 -1
  65. package/lib/module/components/CardField.js.map +1 -1
  66. package/lib/module/components/CardForm.js +1 -1
  67. package/lib/module/components/CardForm.js.map +1 -1
  68. package/lib/module/components/CustomerSheet.js +1 -1
  69. package/lib/module/components/CustomerSheet.js.map +1 -1
  70. package/lib/module/components/PlatformPayButton.js +1 -1
  71. package/lib/module/components/PlatformPayButton.js.map +1 -1
  72. package/lib/module/components/StripeContainer.js +1 -1
  73. package/lib/module/components/StripeContainer.js.map +1 -1
  74. package/lib/module/events.js.map +1 -1
  75. package/lib/module/functions.js +1 -1
  76. package/lib/module/functions.js.map +1 -1
  77. package/lib/module/hooks/useStripe.js +1 -1
  78. package/lib/module/hooks/useStripe.js.map +1 -1
  79. package/lib/module/specs/NativeApplePayButton.js +1 -1
  80. package/lib/module/specs/NativeApplePayButton.js.map +1 -1
  81. package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
  82. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  83. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  84. package/lib/module/types/PaymentIntent.js.map +1 -1
  85. package/lib/module/types/PaymentSheet.js +1 -1
  86. package/lib/module/types/PaymentSheet.js.map +1 -1
  87. package/lib/typescript/src/components/PlatformPayButton.d.ts.map +1 -1
  88. package/lib/typescript/src/events.d.ts +1 -1
  89. package/lib/typescript/src/events.d.ts.map +1 -1
  90. package/lib/typescript/src/functions.d.ts.map +1 -1
  91. package/lib/typescript/src/specs/NativeApplePayButton.d.ts +4 -0
  92. package/lib/typescript/src/specs/NativeApplePayButton.d.ts.map +1 -1
  93. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +2 -0
  94. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
  95. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +29 -0
  96. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
  97. package/lib/typescript/src/types/PaymentIntent.d.ts +2 -11
  98. package/lib/typescript/src/types/PaymentIntent.d.ts.map +1 -1
  99. package/lib/typescript/src/types/PaymentMethod.d.ts +2 -13
  100. package/lib/typescript/src/types/PaymentMethod.d.ts.map +1 -1
  101. package/lib/typescript/src/types/PaymentSheet.d.ts +78 -2
  102. package/lib/typescript/src/types/PaymentSheet.d.ts.map +1 -1
  103. package/package-lock.json +14114 -0
  104. package/package.json +1 -1
  105. package/patches/README.md +55 -0
  106. package/patches/old-arch-codegen-fix.patch +87 -0
  107. package/src/components/PlatformPayButton.tsx +12 -4
  108. package/src/events.ts +3 -1
  109. package/src/functions.ts +36 -1
  110. package/src/specs/NativeApplePayButton.ts +5 -0
  111. package/src/specs/NativeStripeSdkModule.ts +4 -0
  112. package/src/types/EmbeddedPaymentElement.tsx +80 -2
  113. package/src/types/PaymentIntent.ts +1 -11
  114. package/src/types/PaymentMethod.ts +0 -14
  115. package/src/types/PaymentSheet.ts +86 -1
  116. package/stripe-react-native.podspec +1 -1
  117. package/android/.gradle/8.11.1/checksums/checksums.lock +0 -0
  118. package/android/.gradle/8.11.1/checksums/md5-checksums.bin +0 -0
  119. package/android/.gradle/8.11.1/checksums/sha1-checksums.bin +0 -0
  120. package/android/.gradle/8.11.1/executionHistory/executionHistory.bin +0 -0
  121. package/android/.gradle/8.11.1/executionHistory/executionHistory.lock +0 -0
  122. package/android/.gradle/8.11.1/fileChanges/last-build.bin +0 -0
  123. package/android/.gradle/8.11.1/fileHashes/fileHashes.bin +0 -0
  124. package/android/.gradle/8.11.1/fileHashes/fileHashes.lock +0 -0
  125. package/android/.gradle/8.11.1/gc.properties +0 -0
  126. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  127. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  128. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  129. package/android/.gradle/config.properties +0 -2
  130. package/android/.gradle/file-system.probe +0 -0
  131. package/android/.gradle/vcs-1/gc.properties +0 -0
  132. package/android/.idea/caches/deviceStreaming.xml +0 -619
  133. package/android/.idea/compiler.xml +0 -6
  134. package/android/.idea/gradle.xml +0 -19
  135. package/android/.idea/migrations.xml +0 -10
  136. package/android/.idea/misc.xml +0 -10
  137. package/android/.idea/runConfigurations.xml +0 -17
  138. package/android/.idea/vcs.xml +0 -6
  139. package/android/local.properties +0 -8
@@ -3,6 +3,7 @@ package com.reactnativestripesdk
3
3
  import android.app.Activity
4
4
  import android.app.Application
5
5
  import android.content.Context
6
+ import android.content.Intent
6
7
  import android.graphics.Bitmap
7
8
  import android.graphics.Canvas
8
9
  import android.graphics.Color
@@ -11,6 +12,7 @@ import android.os.Bundle
11
12
  import android.os.Handler
12
13
  import android.os.Looper
13
14
  import android.util.Base64
15
+ import android.util.Log
14
16
  import androidx.appcompat.content.res.AppCompatResources
15
17
  import androidx.core.graphics.drawable.DrawableCompat
16
18
  import com.facebook.react.bridge.Arguments
@@ -28,10 +30,18 @@ import com.reactnativestripesdk.utils.PaymentSheetException
28
30
  import com.reactnativestripesdk.utils.StripeFragment
29
31
  import com.reactnativestripesdk.utils.createError
30
32
  import com.reactnativestripesdk.utils.createResult
33
+ import com.reactnativestripesdk.utils.mapFromCustomPaymentMethod
31
34
  import com.reactnativestripesdk.utils.mapFromPaymentMethod
32
35
  import com.reactnativestripesdk.utils.mapToPreferredNetworks
36
+ import com.reactnativestripesdk.utils.parseCustomPaymentMethods
33
37
  import com.reactnativestripesdk.utils.removeFragment
34
38
  import com.stripe.android.ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi
39
+ import com.stripe.android.model.PaymentMethod
40
+ import com.stripe.android.paymentelement.ConfirmCustomPaymentMethodCallback
41
+ import com.stripe.android.paymentelement.CustomPaymentMethodResult
42
+ import com.stripe.android.paymentelement.CustomPaymentMethodResultHandler
43
+ import com.stripe.android.paymentelement.ExperimentalCustomPaymentMethodsApi
44
+ import com.stripe.android.paymentelement.PaymentMethodOptionsSetupFutureUsagePreview
35
45
  import com.stripe.android.paymentsheet.CreateIntentCallback
36
46
  import com.stripe.android.paymentsheet.CreateIntentResult
37
47
  import com.stripe.android.paymentsheet.ExperimentalCustomerSessionApi
@@ -40,11 +50,17 @@ import com.stripe.android.paymentsheet.PaymentSheet
40
50
  import com.stripe.android.paymentsheet.PaymentSheetResult
41
51
  import com.stripe.android.paymentsheet.PaymentSheetResultCallback
42
52
  import kotlinx.coroutines.CompletableDeferred
53
+ import kotlinx.coroutines.CoroutineScope
54
+ import kotlinx.coroutines.Dispatchers
55
+ import kotlinx.coroutines.delay
56
+ import kotlinx.coroutines.launch
43
57
  import java.io.ByteArrayOutputStream
44
58
  import kotlin.Exception
45
59
 
46
- @OptIn(ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi::class)
47
- class PaymentSheetFragment : StripeFragment() {
60
+ @OptIn(ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi::class, ExperimentalCustomPaymentMethodsApi::class)
61
+ class PaymentSheetFragment :
62
+ StripeFragment(),
63
+ ConfirmCustomPaymentMethodCallback {
48
64
  private lateinit var context: ReactApplicationContext
49
65
  private lateinit var initPromise: Promise
50
66
  private var paymentSheet: PaymentSheet? = null
@@ -59,6 +75,7 @@ class PaymentSheetFragment : StripeFragment() {
59
75
  internal var paymentSheetIntentCreationCallback = CompletableDeferred<ReadableMap>()
60
76
  private var keepJsAwake: KeepJsAwakeTask? = null
61
77
 
78
+ @OptIn(ExperimentalCustomPaymentMethodsApi::class)
62
79
  override fun prepare() {
63
80
  val merchantDisplayName = arguments?.getString("merchantDisplayName").orEmpty()
64
81
  if (merchantDisplayName.isEmpty()) {
@@ -238,6 +255,7 @@ class PaymentSheetFragment : StripeFragment() {
238
255
  mapToPreferredNetworks(arguments?.getIntegerArrayList("preferredNetworks")),
239
256
  ).allowsRemovalOfLastSavedPaymentMethod(allowsRemovalOfLastSavedPaymentMethod)
240
257
  .cardBrandAcceptance(mapToCardBrandAcceptance(arguments))
258
+ .customPaymentMethods(parseCustomPaymentMethods(arguments))
241
259
 
242
260
  primaryButtonLabel?.let { configurationBuilder.primaryButtonLabel(it) }
243
261
  paymentMethodOrder?.let { configurationBuilder.paymentMethodOrder(it) }
@@ -251,30 +269,35 @@ class PaymentSheetFragment : StripeFragment() {
251
269
  if (arguments?.getBoolean("customFlow") == true) {
252
270
  flowController =
253
271
  if (intentConfiguration != null) {
254
- PaymentSheet.FlowController.create(
255
- this,
256
- paymentOptionCallback = paymentOptionCallback,
257
- createIntentCallback = createIntentCallback,
258
- paymentResultCallback = paymentResultCallback,
259
- )
272
+ PaymentSheet.FlowController
273
+ .Builder(
274
+ resultCallback = paymentResultCallback,
275
+ paymentOptionCallback = paymentOptionCallback,
276
+ ).createIntentCallback(createIntentCallback)
277
+ .confirmCustomPaymentMethodCallback(this)
278
+ .build(this)
260
279
  } else {
261
- PaymentSheet.FlowController.create(
262
- this,
263
- paymentOptionCallback = paymentOptionCallback,
264
- paymentResultCallback = paymentResultCallback,
265
- )
280
+ PaymentSheet.FlowController
281
+ .Builder(
282
+ resultCallback = paymentResultCallback,
283
+ paymentOptionCallback = paymentOptionCallback,
284
+ ).confirmCustomPaymentMethodCallback(this)
285
+ .build(this)
266
286
  }
267
287
  configureFlowController()
268
288
  } else {
269
289
  paymentSheet =
270
290
  if (intentConfiguration != null) {
271
- PaymentSheet(
272
- this,
273
- createIntentCallback = createIntentCallback,
274
- paymentResultCallback = paymentResultCallback,
275
- )
291
+ PaymentSheet
292
+ .Builder(paymentResultCallback)
293
+ .createIntentCallback(createIntentCallback)
294
+ .confirmCustomPaymentMethodCallback(this)
295
+ .build(this)
276
296
  } else {
277
- PaymentSheet(this, callback = paymentResultCallback)
297
+ PaymentSheet
298
+ .Builder(paymentResultCallback)
299
+ .confirmCustomPaymentMethodCallback(this)
300
+ .build(this)
278
301
  }
279
302
  initPromise.resolve(WritableNativeMap())
280
303
  }
@@ -418,6 +441,81 @@ class PaymentSheetFragment : StripeFragment() {
418
441
  } ?: run { resolvePresentPromise(map) }
419
442
  }
420
443
 
444
+ @OptIn(ExperimentalCustomPaymentMethodsApi::class)
445
+ override fun onConfirmCustomPaymentMethod(
446
+ customPaymentMethod: PaymentSheet.CustomPaymentMethod,
447
+ billingDetails: PaymentMethod.BillingDetails,
448
+ ) {
449
+ // Launch a transparent Activity to ensure React Native UI can appear on top of the Stripe proxy activity.
450
+ try {
451
+ val intent =
452
+ Intent(context, CustomPaymentMethodActivity::class.java).apply {
453
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
454
+ addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
455
+ }
456
+ context.startActivity(intent)
457
+ } catch (e: Exception) {
458
+ Log.e("StripeReactNative", "Failed to start CustomPaymentMethodActivity", e)
459
+ }
460
+
461
+ val stripeSdkModule =
462
+ try {
463
+ context.getNativeModule(StripeSdkModule::class.java)
464
+ ?: throw IllegalArgumentException("StripeSdkModule not found")
465
+ } catch (ex: IllegalArgumentException) {
466
+ Log.e("StripeReactNative", "StripeSdkModule not found for CPM callback", ex)
467
+ CustomPaymentMethodActivity.finishCurrent()
468
+ return
469
+ }
470
+
471
+ // Keep JS awake while React Native is backgrounded by Stripe SDK.
472
+ val keepJsAwakeTask =
473
+ KeepJsAwakeTask(context).apply { start() }
474
+
475
+ // Run on main coroutine scope.
476
+ CoroutineScope(Dispatchers.Main).launch {
477
+ try {
478
+ // Give the CustomPaymentMethodActivity a moment to fully initialize
479
+ delay(100)
480
+
481
+ // Emit event so JS can show the Alert and eventually respond via `customPaymentMethodResultCallback`.
482
+ stripeSdkModule.emitOnCustomPaymentMethodConfirmHandlerCallback(
483
+ mapFromCustomPaymentMethod(customPaymentMethod, billingDetails),
484
+ )
485
+
486
+ // Await JS result.
487
+ val resultFromJs = stripeSdkModule.customPaymentMethodResultCallback.await()
488
+
489
+ keepJsAwakeTask.stop()
490
+
491
+ val status = resultFromJs.getString("status")
492
+
493
+ val nativeResult =
494
+ when (status) {
495
+ "completed" ->
496
+ CustomPaymentMethodResult.completed()
497
+ "canceled" ->
498
+ CustomPaymentMethodResult.canceled()
499
+ "failed" -> {
500
+ val errMsg = resultFromJs.getString("error") ?: "Custom payment failed"
501
+ CustomPaymentMethodResult.failed(displayMessage = errMsg)
502
+ }
503
+ else ->
504
+ CustomPaymentMethodResult.failed(displayMessage = "Unknown status")
505
+ }
506
+
507
+ // Return result to Stripe SDK.
508
+ CustomPaymentMethodResultHandler.handleCustomPaymentMethodResult(
509
+ context,
510
+ nativeResult,
511
+ )
512
+ } finally {
513
+ // Clean up the transparent activity
514
+ CustomPaymentMethodActivity.finishCurrent()
515
+ }
516
+ }
517
+ }
518
+
421
519
  companion object {
422
520
  internal const val TAG = "payment_sheet_launch_fragment"
423
521
 
@@ -518,6 +616,7 @@ class PaymentSheetFragment : StripeFragment() {
518
616
  )
519
617
  }
520
618
 
619
+ @OptIn(PaymentMethodOptionsSetupFutureUsagePreview::class)
521
620
  private fun buildIntentConfigurationMode(modeParams: Bundle): PaymentSheet.IntentConfiguration.Mode {
522
621
  val currencyCode =
523
622
  modeParams.getString("currencyCode")
@@ -531,6 +630,7 @@ class PaymentSheetFragment : StripeFragment() {
531
630
  currency = currencyCode,
532
631
  setupFutureUse = mapToSetupFutureUse(modeParams.getString("setupFutureUsage")),
533
632
  captureMethod = mapToCaptureMethod(modeParams.getString("captureMethod")),
633
+ paymentMethodOptions = mapToPaymentMethodOptions(modeParams.getBundle("paymentMethodOptions")),
534
634
  )
535
635
  } else {
536
636
  val setupFutureUsage =
@@ -639,6 +739,7 @@ fun mapToSetupFutureUse(type: String?): PaymentSheet.IntentConfiguration.SetupFu
639
739
  when (type) {
640
740
  "OffSession" -> PaymentSheet.IntentConfiguration.SetupFutureUse.OffSession
641
741
  "OnSession" -> PaymentSheet.IntentConfiguration.SetupFutureUse.OnSession
742
+ "None" -> PaymentSheet.IntentConfiguration.SetupFutureUse.None
642
743
  else -> null
643
744
  }
644
745
 
@@ -650,6 +751,26 @@ fun mapToCaptureMethod(type: String?): PaymentSheet.IntentConfiguration.CaptureM
650
751
  else -> PaymentSheet.IntentConfiguration.CaptureMethod.Automatic
651
752
  }
652
753
 
754
+ @OptIn(PaymentMethodOptionsSetupFutureUsagePreview::class)
755
+ fun mapToPaymentMethodOptions(options: Bundle?): PaymentSheet.IntentConfiguration.Mode.Payment.PaymentMethodOptions? {
756
+ val sfuBundle = options?.getBundle("setupFutureUsageValues")
757
+ val paymentMethodToSfuMap = mutableMapOf<PaymentMethod.Type, PaymentSheet.IntentConfiguration.SetupFutureUse>()
758
+ sfuBundle?.keySet()?.forEach { code ->
759
+ val sfuValue = mapToSetupFutureUse(sfuBundle?.getString(code))
760
+ val paymentMethodType = PaymentMethod.Type.fromCode(code)
761
+ if (paymentMethodType != null && sfuValue != null) {
762
+ paymentMethodToSfuMap[paymentMethodType] = sfuValue
763
+ }
764
+ }
765
+ return if (paymentMethodToSfuMap.isNotEmpty()) {
766
+ PaymentSheet.IntentConfiguration.Mode.Payment.PaymentMethodOptions(
767
+ setupFutureUsageValues = paymentMethodToSfuMap,
768
+ )
769
+ } else {
770
+ null
771
+ }
772
+ }
773
+
653
774
  fun mapToCardBrandAcceptance(params: Bundle?): PaymentSheet.CardBrandAcceptance {
654
775
  val cardBrandAcceptanceParams = params?.getBundle("cardBrandAcceptance") ?: return PaymentSheet.CardBrandAcceptance.all()
655
776
  val filter = cardBrandAcceptanceParams.getString("filter") ?: return PaymentSheet.CardBrandAcceptance.all()
@@ -8,6 +8,7 @@ import android.os.Bundle
8
8
  import android.util.Log
9
9
  import android.view.ViewGroup
10
10
  import androidx.fragment.app.FragmentActivity
11
+ import com.facebook.react.bridge.Arguments
11
12
  import com.facebook.react.bridge.BaseActivityEventListener
12
13
  import com.facebook.react.bridge.Promise
13
14
  import com.facebook.react.bridge.ReactApplicationContext
@@ -92,6 +93,7 @@ class StripeSdkModule(
92
93
  private var customerSheetFragment: CustomerSheetFragment? = null
93
94
 
94
95
  internal var embeddedIntentCreationCallback = CompletableDeferred<ReadableMap>()
96
+ internal var customPaymentMethodResultCallback = CompletableDeferred<ReadableMap>()
95
97
 
96
98
  internal var composeCompatView: StripeAbstractComposeView.CompatView? = null
97
99
 
@@ -233,6 +235,14 @@ class StripeSdkModule(
233
235
  getCurrentActivityOrResolveWithError(promise)?.let { activity ->
234
236
  paymentSheetFragment?.removeFragment(reactApplicationContext)
235
237
  val bundle = toBundleObject(params)
238
+
239
+ // Handle custom payment methods separately since toBundleObject cannot handle arrays of objects
240
+ val customPaymentMethodConfig = params.getMap("customPaymentMethodConfiguration")
241
+ if (customPaymentMethodConfig != null) {
242
+ // Store the original ReadableMap for custom payment methods
243
+ bundle.putSerializable("customPaymentMethodConfigurationReadableMap", customPaymentMethodConfig.toHashMap())
244
+ }
245
+
236
246
  paymentSheetFragment =
237
247
  PaymentSheetFragment.create(reactApplicationContext, bundle, promise)
238
248
  try {
@@ -298,6 +308,18 @@ class StripeSdkModule(
298
308
  paymentSheetFragment?.paymentSheetIntentCreationCallback?.complete(params)
299
309
  }
300
310
 
311
+ @ReactMethod
312
+ override fun customPaymentMethodResultCallback(
313
+ result: ReadableMap?,
314
+ promise: Promise?,
315
+ ) {
316
+ // Complete the deferred with the result from JavaScript
317
+ customPaymentMethodResultCallback.complete(result ?: Arguments.createMap())
318
+ // Reset for next use
319
+ customPaymentMethodResultCallback = CompletableDeferred()
320
+ promise?.resolve(null)
321
+ }
322
+
301
323
  @ReactMethod
302
324
  override fun createPaymentMethod(
303
325
  data: ReadableMap,
@@ -27,6 +27,8 @@ import com.stripe.android.model.StripeIntent
27
27
  import com.stripe.android.model.StripeIntent.NextActionData
28
28
  import com.stripe.android.model.StripeIntent.NextActionType
29
29
  import com.stripe.android.model.Token
30
+ import com.stripe.android.paymentelement.ExperimentalCustomPaymentMethodsApi
31
+ import com.stripe.android.paymentsheet.PaymentSheet
30
32
 
31
33
  internal fun createResult(
32
34
  key: String,
@@ -138,7 +140,6 @@ internal fun mapPaymentMethodType(type: PaymentMethod.Type?): String =
138
140
  PaymentMethod.Type.Oxxo -> "Oxxo"
139
141
  PaymentMethod.Type.P24 -> "P24"
140
142
  PaymentMethod.Type.SepaDebit -> "SepaDebit"
141
- PaymentMethod.Type.Sofort -> "Sofort"
142
143
  PaymentMethod.Type.Upi -> "Upi"
143
144
  PaymentMethod.Type.WeChatPay -> "WeChatPay"
144
145
  PaymentMethod.Type.Klarna -> "Klarna"
@@ -168,7 +169,6 @@ internal fun mapToPaymentMethodType(type: String?): PaymentMethod.Type? =
168
169
  "Oxxo" -> PaymentMethod.Type.Oxxo
169
170
  "P24" -> PaymentMethod.Type.P24
170
171
  "SepaDebit" -> PaymentMethod.Type.SepaDebit
171
- "Sofort" -> PaymentMethod.Type.Sofort
172
172
  "Upi" -> PaymentMethod.Type.Upi
173
173
  "WeChatPay" -> PaymentMethod.Type.WeChatPay
174
174
  "Klarna" -> PaymentMethod.Type.Klarna
@@ -415,10 +415,6 @@ internal fun mapFromPaymentMethod(paymentMethod: PaymentMethod): WritableMap {
415
415
  it.putString("last4", paymentMethod.bacsDebit?.last4)
416
416
  },
417
417
  )
418
- pm.putMap(
419
- "Sofort",
420
- WritableNativeMap().also { it.putString("country", paymentMethod.sofort?.country) },
421
- )
422
418
  pm.putMap(
423
419
  "Ideal",
424
420
  WritableNativeMap().also {
@@ -1065,3 +1061,53 @@ private fun Map<String, Any?>.toReadableMap(): ReadableMap {
1065
1061
 
1066
1062
  return writableMap
1067
1063
  }
1064
+
1065
+ @OptIn(ExperimentalCustomPaymentMethodsApi::class)
1066
+ @SuppressLint("RestrictedApi")
1067
+ internal fun parseCustomPaymentMethods(customPaymentMethodConfig: Bundle?): List<PaymentSheet.CustomPaymentMethod> {
1068
+ if (customPaymentMethodConfig == null) {
1069
+ return emptyList()
1070
+ }
1071
+
1072
+ val configHashMap = customPaymentMethodConfig.getSerializable("customPaymentMethodConfigurationReadableMap") as? HashMap<String, Any>
1073
+ if (configHashMap != null) {
1074
+ val customPaymentMethods = configHashMap["customPaymentMethods"] as? List<HashMap<String, Any>>
1075
+ if (customPaymentMethods != null) {
1076
+ val result = mutableListOf<PaymentSheet.CustomPaymentMethod>()
1077
+
1078
+ for (customPaymentMethodMap in customPaymentMethods) {
1079
+ val id = customPaymentMethodMap["id"] as? String
1080
+ if (id != null) {
1081
+ val subtitle = customPaymentMethodMap["subtitle"] as? String
1082
+ val disableBillingDetailCollection = customPaymentMethodMap["disableBillingDetailCollection"] as? Boolean ?: false
1083
+ result.add(
1084
+ PaymentSheet.CustomPaymentMethod(
1085
+ id = id,
1086
+ subtitle = subtitle,
1087
+ disableBillingDetailCollection = disableBillingDetailCollection,
1088
+ ),
1089
+ )
1090
+ }
1091
+ }
1092
+
1093
+ return result
1094
+ }
1095
+ }
1096
+
1097
+ return emptyList()
1098
+ }
1099
+
1100
+ @SuppressLint("RestrictedApi")
1101
+ internal fun mapFromCustomPaymentMethod(
1102
+ customPaymentMethod: PaymentSheet.CustomPaymentMethod,
1103
+ billingDetails: PaymentMethod.BillingDetails,
1104
+ ): WritableMap =
1105
+ WritableNativeMap().apply {
1106
+ putMap(
1107
+ "customPaymentMethod",
1108
+ WritableNativeMap().apply {
1109
+ putString("id", customPaymentMethod.id)
1110
+ },
1111
+ )
1112
+ putMap("billingDetails", mapFromBillingDetails(billingDetails))
1113
+ }
@@ -0,0 +1,27 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <style name="Theme.StripeReactNative.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
4
+ <!-- Core transparency settings -->
5
+ <item name="android:windowIsTranslucent">true</item>
6
+ <item name="android:windowBackground">@android:color/transparent</item>
7
+ <item name="android:windowContentOverlay">@null</item>
8
+ <item name="android:windowNoTitle">true</item>
9
+ <item name="android:windowIsFloating">false</item>
10
+ <item name="android:backgroundDimEnabled">false</item>
11
+ <item name="android:windowAnimationStyle">@null</item>
12
+
13
+ <!-- Enhanced transparency and touch handling -->
14
+ <item name="android:windowCloseOnTouchOutside">false</item>
15
+ <item name="android:windowEnableSplitTouch">false</item>
16
+ <item name="android:windowDrawsSystemBarBackgrounds">false</item>
17
+ <item name="android:statusBarColor">@android:color/transparent</item>
18
+ <item name="android:navigationBarColor">@android:color/transparent</item>
19
+
20
+ <!-- Performance optimizations -->
21
+ <item name="android:windowDisablePreview">true</item>
22
+
23
+ <!-- Prevent visual artifacts -->
24
+ <item name="android:windowShowWallpaper">false</item>
25
+ <item name="android:windowFullscreen">false</item>
26
+ </style>
27
+ </resources>
@@ -114,6 +114,10 @@ public abstract class NativeStripeSdkModuleSpec extends ReactContextBaseJavaModu
114
114
  invoke("embeddedPaymentElementLoadingFailed", value);
115
115
  }
116
116
 
117
+ protected final void emitOnCustomPaymentMethodConfirmHandlerCallback(ReadableMap value) {
118
+ invoke("onCustomPaymentMethodConfirmHandlerCallback", value);
119
+ }
120
+
117
121
  @ReactMethod
118
122
  @DoNotStrip
119
123
  public abstract void initialise(ReadableMap params, Promise promise);
@@ -154,6 +158,10 @@ public abstract class NativeStripeSdkModuleSpec extends ReactContextBaseJavaModu
154
158
  @DoNotStrip
155
159
  public abstract void intentCreationCallback(ReadableMap result, Promise promise);
156
160
 
161
+ @ReactMethod
162
+ @DoNotStrip
163
+ public abstract void customPaymentMethodResultCallback(ReadableMap result, Promise promise);
164
+
157
165
  @ReactMethod
158
166
  @DoNotStrip
159
167
  public abstract void presentPaymentSheet(ReadableMap options, Promise promise);
@@ -11,5 +11,9 @@ RCT_EXPORT_VIEW_PROPERTY(onShippingMethodSelectedAction, RCTDirectEventBlock)
11
11
  RCT_EXPORT_VIEW_PROPERTY(onShippingContactSelectedAction, RCTDirectEventBlock)
12
12
  RCT_EXPORT_VIEW_PROPERTY(onCouponCodeEnteredAction, RCTDirectEventBlock)
13
13
  RCT_EXPORT_VIEW_PROPERTY(onOrderTrackingAction, RCTDirectEventBlock)
14
+ RCT_EXPORT_VIEW_PROPERTY(hasShippingMethodCallback, BOOL)
15
+ RCT_EXPORT_VIEW_PROPERTY(hasShippingContactCallback, BOOL)
16
+ RCT_EXPORT_VIEW_PROPERTY(hasCouponCodeCallback, BOOL)
17
+ RCT_EXPORT_VIEW_PROPERTY(hasOrderTrackingCallback, BOOL)
14
18
 
15
19
  @end
@@ -10,16 +10,23 @@ public class ApplePayButtonView: UIView {
10
10
  @objc public var onCouponCodeEnteredAction: RCTDirectEventBlock?
11
11
  @objc public var onOrderTrackingAction: RCTDirectEventBlock?
12
12
 
13
+ // Boolean flags to track which callbacks were provided as props
14
+ @objc public var hasShippingMethodCallback = false
15
+ @objc public var hasShippingContactCallback = false
16
+ @objc public var hasCouponCodeCallback = false
17
+ @objc public var hasOrderTrackingCallback = false
18
+
13
19
  @objc public var type: NSNumber?
14
20
  @objc public var buttonStyle: NSNumber?
15
21
  @objc public var borderRadius: NSNumber?
16
22
  @objc public var disabled = false
17
23
 
18
24
  @objc func handleApplePayButtonTapped() {
19
- StripeSdkImpl.shared.shippingMethodUpdateJSCallback = onShippingMethodSelectedAction
20
- StripeSdkImpl.shared.shippingContactUpdateJSCallback = onShippingContactSelectedAction
21
- StripeSdkImpl.shared.couponCodeEnteredJSCallback = onCouponCodeEnteredAction
22
- StripeSdkImpl.shared.platformPayOrderTrackingJSCallback = onOrderTrackingAction
25
+ // Only set callbacks that were actually provided as props
26
+ StripeSdkImpl.shared.shippingMethodUpdateJSCallback = hasShippingMethodCallback ? onShippingMethodSelectedAction : nil
27
+ StripeSdkImpl.shared.shippingContactUpdateJSCallback = hasShippingContactCallback ? onShippingContactSelectedAction : nil
28
+ StripeSdkImpl.shared.couponCodeEnteredJSCallback = hasCouponCodeCallback ? onCouponCodeEnteredAction : nil
29
+ StripeSdkImpl.shared.platformPayOrderTrackingJSCallback = hasOrderTrackingCallback ? onOrderTrackingAction : nil
23
30
  }
24
31
 
25
32
  @objc public func didSetProps() {
package/ios/Mappers.swift CHANGED
@@ -285,7 +285,6 @@ class Mappers {
285
285
  case STPPaymentMethodType.EPS: return "Eps"
286
286
  case STPPaymentMethodType.bancontact: return "Bancontact"
287
287
  case STPPaymentMethodType.OXXO: return "Oxxo"
288
- case STPPaymentMethodType.sofort: return "Sofort"
289
288
  case STPPaymentMethodType.UPI: return "Upi"
290
289
  case STPPaymentMethodType.afterpayClearpay: return "AfterpayClearpay"
291
290
  case STPPaymentMethodType.klarna: return "Klarna"
@@ -316,7 +315,6 @@ class Mappers {
316
315
  case "Eps": return STPPaymentMethodType.EPS
317
316
  case "Bancontact": return STPPaymentMethodType.bancontact
318
317
  case "Oxxo": return STPPaymentMethodType.OXXO
319
- case "Sofort": return STPPaymentMethodType.sofort
320
318
  case "Upi": return STPPaymentMethodType.UPI
321
319
  case "AfterpayClearpay": return STPPaymentMethodType.afterpayClearpay
322
320
  case "Klarna": return STPPaymentMethodType.klarna
@@ -666,9 +664,6 @@ class Mappers {
666
664
  "SepaDebit": sepaDebit,
667
665
  "BacsDebit": bacsDebit,
668
666
  "AuBecsDebit": auBECSDebit,
669
- "Sofort": [
670
- "country": paymentMethod.sofort?.country
671
- ],
672
667
  "Upi": [
673
668
  "vpa": paymentMethod.upi?.vpa
674
669
  ],
@@ -98,6 +98,12 @@ using namespace facebook::react;
98
98
  _view.disabled = newViewProps.disabled;
99
99
  _view.borderRadius = @(newViewProps.borderRadius);
100
100
 
101
+ // Set the boolean flags from props
102
+ _view.hasShippingMethodCallback = newViewProps.hasShippingMethodCallback;
103
+ _view.hasShippingContactCallback = newViewProps.hasShippingContactCallback;
104
+ _view.hasCouponCodeCallback = newViewProps.hasCouponCodeCallback;
105
+ _view.hasOrderTrackingCallback = newViewProps.hasOrderTrackingCallback;
106
+
101
107
  [super updateProps:props oldProps:oldProps];
102
108
 
103
109
  [_view didSetProps];
@@ -21,7 +21,7 @@
21
21
  - (void)emitEmbeddedPaymentElementFormSheetConfirmComplete:(NSDictionary *)value;
22
22
  - (void)emitEmbeddedPaymentElementRowSelectionImmediateAction;
23
23
  - (void)emitEmbeddedPaymentElementLoadingFailed:(NSDictionary *)value;
24
-
24
+ - (void)emitOnCustomPaymentMethodConfirmHandlerCallback:(NSDictionary *)value;
25
25
  @end
26
26
 
27
27
  #endif
@@ -21,7 +21,8 @@
21
21
  @"embeddedPaymentElementDidUpdatePaymentOption",
22
22
  @"embeddedPaymentElementFormSheetConfirmComplete",
23
23
  @"embeddedPaymentElementRowSelectionImmediateAction",
24
- @"embeddedPaymentElementLoadingFailed"
24
+ @"embeddedPaymentElementLoadingFailed",
25
+ @"onCustomPaymentMethodConfirmHandlerCallback"
25
26
  ];
26
27
  }
27
28
 
@@ -100,6 +101,11 @@
100
101
  [self sendEventWithName:@"embeddedPaymentElementLoadingFailed" body:value];
101
102
  }
102
103
 
104
+ - (void)emitOnCustomPaymentMethodConfirmHandlerCallback:(NSDictionary *)value
105
+ {
106
+ [self sendEventWithName:@"onCustomPaymentMethodConfirmHandlerCallback" body:value];
107
+ }
108
+
103
109
  @end
104
110
 
105
111
  #endif
@@ -31,8 +31,6 @@ class PaymentMethodFactory {
31
31
  return try createFPXPaymentMethodParams()
32
32
  case STPPaymentMethodType.alipay:
33
33
  return try createAlipayPaymentMethodParams()
34
- case STPPaymentMethodType.sofort:
35
- return try createSofortPaymentMethodParams()
36
34
  case STPPaymentMethodType.bancontact:
37
35
  return try createBancontactPaymentMethodParams()
38
36
  case STPPaymentMethodType.SEPADebit:
@@ -82,8 +80,6 @@ class PaymentMethodFactory {
82
80
  return createCardPaymentMethodOptions()
83
81
  case STPPaymentMethodType.FPX:
84
82
  return nil
85
- case STPPaymentMethodType.sofort:
86
- return nil
87
83
  case STPPaymentMethodType.alipay:
88
84
  return try createAlipayPaymentMethodOptions()
89
85
  case STPPaymentMethodType.bancontact:
@@ -250,16 +246,6 @@ class PaymentMethodFactory {
250
246
  return options
251
247
  }
252
248
 
253
- private func createSofortPaymentMethodParams() throws -> STPPaymentMethodParams {
254
- guard let country = self.paymentMethodData?["country"] as? String else {
255
- throw PaymentMethodError.sofortPaymentMissingParams
256
- }
257
- let params = STPPaymentMethodSofortParams()
258
- params.country = country
259
-
260
- return STPPaymentMethodParams(sofort: params, billingDetails: billingDetailsParams, metadata: metadata)
261
- }
262
-
263
249
  private func createBancontactPaymentMethodParams() throws -> STPPaymentMethodParams {
264
250
  let params = STPPaymentMethodBancontactParams()
265
251
 
@@ -411,7 +397,6 @@ enum PaymentMethodError: Error {
411
397
  case epsPaymentMissingParams
412
398
  case idealPaymentMissingParams
413
399
  case paymentNotSupported
414
- case sofortPaymentMissingParams
415
400
  case cardPaymentOptionsMissingParams
416
401
  case bancontactPaymentMissingParams
417
402
  case sepaPaymentMissingParams
@@ -434,8 +419,6 @@ extension PaymentMethodError: LocalizedError {
434
419
  return NSLocalizedString("You must provide billing details", comment: "Create payment error")
435
420
  case .idealPaymentMissingParams:
436
421
  return NSLocalizedString("You must provide bank name", comment: "Create payment error")
437
- case .sofortPaymentMissingParams:
438
- return NSLocalizedString("You must provide bank account country", comment: "Create payment error")
439
422
  case .p24PaymentMissingParams:
440
423
  return NSLocalizedString("You must provide billing details", comment: "Create payment error")
441
424
  case .bancontactPaymentMissingParams:
@@ -185,6 +185,8 @@ internal class PaymentSheetAppearance {
185
185
  row.style = .floatingButton
186
186
  case PaymentSheetAppearanceKeys.ROW_STYLE_FLAT_WITH_CHECKMARK:
187
187
  row.style = .flatWithCheckmark
188
+ case PaymentSheetAppearanceKeys.ROW_STYLE_FLAT_WITH_CHEVRON:
189
+ row.style = .flatWithChevron
188
190
  default:
189
191
  throw PaymentSheetAppearanceError.invalidRowStyle(styleString)
190
192
  }
@@ -239,6 +241,10 @@ internal class PaymentSheetAppearance {
239
241
  flat.checkmark = try buildEmbeddedCheckmark(params: checkmarkParams)
240
242
  }
241
243
 
244
+ if let chevronParams = params[PaymentSheetAppearanceKeys.CHEVRON] as? NSDictionary {
245
+ flat.chevron = try buildEmbeddedChevron(params: chevronParams)
246
+ }
247
+
242
248
  return flat
243
249
  }
244
250
 
@@ -277,6 +283,19 @@ internal class PaymentSheetAppearance {
277
283
  return checkmark
278
284
  }
279
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
+
280
299
  private class func buildEmbeddedFloating(params: NSDictionary) throws -> PaymentSheet.Appearance.EmbeddedPaymentElement.Row.Floating {
281
300
  var floating = PaymentSheet.Appearance.default.embeddedPaymentElement.row.floating
282
301
 
@@ -354,7 +373,7 @@ extension PaymentSheetAppearanceError: LocalizedError {
354
373
  case .unexpectedHexStringLength(let hexString):
355
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")
356
375
  case .invalidRowStyle(let styleString):
357
- 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")
358
377
  }
359
378
  }
360
379
  }
@@ -411,6 +430,7 @@ private struct PaymentSheetAppearanceKeys {
411
430
  static let SELECTED_COLOR = "selectedColor"
412
431
  static let UNSELECTED_COLOR = "unselectedColor"
413
432
  static let CHECKMARK = "checkmark"
433
+ static let CHEVRON = "chevron"
414
434
  static let SPACING = "spacing"
415
435
  static let TOP = "top"
416
436
  static let LEFT = "left"
@@ -422,6 +442,7 @@ private struct PaymentSheetAppearanceKeys {
422
442
  static let ROW_STYLE_FLAT_WITH_RADIO = "flatWithRadio"
423
443
  static let ROW_STYLE_FLOATING_BUTTON = "floatingButton"
424
444
  static let ROW_STYLE_FLAT_WITH_CHECKMARK = "flatWithCheckmark"
445
+ static let ROW_STYLE_FLAT_WITH_CHEVRON = "flatWithChevron"
425
446
 
426
447
  static let FORM_INSETS = "formInsetValues"
427
448
  }