@stripe/stripe-react-native 0.55.1 → 0.57.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 (126) hide show
  1. package/android/gradle.properties +1 -1
  2. package/android/src/main/java/com/reactnativestripesdk/AuBECSDebitFormView.kt +4 -3
  3. package/android/src/main/java/com/reactnativestripesdk/CardFieldView.kt +7 -6
  4. package/android/src/main/java/com/reactnativestripesdk/CardFormView.kt +11 -11
  5. package/android/src/main/java/com/reactnativestripesdk/{CollectBankAccountLauncherFragment.kt → CollectBankAccountLauncherManager.kt} +21 -44
  6. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +17 -24
  7. package/android/src/main/java/com/reactnativestripesdk/EventEmitterCompat.kt +8 -0
  8. package/android/src/main/java/com/reactnativestripesdk/{FinancialConnectionsSheetFragment.kt → FinancialConnectionsSheetManager.kt} +30 -77
  9. package/android/src/main/java/com/reactnativestripesdk/GooglePayLauncherManager.kt +107 -0
  10. package/android/src/main/java/com/reactnativestripesdk/GooglePayPaymentMethodLauncherManager.kt +37 -0
  11. package/android/src/main/java/com/reactnativestripesdk/{PaymentLauncherFragment.kt → PaymentLauncherManager.kt} +39 -77
  12. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt +8 -20
  13. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +366 -483
  14. package/android/src/main/java/com/reactnativestripesdk/{PaymentSheetFragment.kt → PaymentSheetManager.kt} +79 -88
  15. package/android/src/main/java/com/reactnativestripesdk/StripeAbstractComposeView.kt +3 -2
  16. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +213 -210
  17. package/android/src/main/java/com/reactnativestripesdk/addresssheet/AddressLauncherManager.kt +78 -0
  18. package/android/src/main/java/com/reactnativestripesdk/addresssheet/AddressSheetView.kt +48 -35
  19. package/android/src/main/java/com/reactnativestripesdk/customersheet/{CustomerSheetFragment.kt → CustomerSheetManager.kt} +127 -104
  20. package/android/src/main/java/com/reactnativestripesdk/customersheet/ReactNativeCustomerSessionProvider.kt +35 -0
  21. package/android/src/main/java/com/reactnativestripesdk/utils/Extensions.kt +47 -12
  22. package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +87 -142
  23. package/android/src/main/java/com/reactnativestripesdk/utils/StripeUIManager.kt +62 -0
  24. package/android/src/oldarch/java/com/facebook/react/viewmanagers/ApplePayButtonManagerDelegate.java +12 -0
  25. package/android/src/oldarch/java/com/facebook/react/viewmanagers/ApplePayButtonManagerInterface.java +4 -0
  26. package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +8 -64
  27. package/ios/AddressSheet/AddressSheetView.swift +1 -1
  28. package/ios/ApplePayButtonManager.m +1 -1
  29. package/ios/ApplePayButtonView.swift +2 -2
  30. package/ios/FinancialConnections.swift +2 -2
  31. package/ios/Mappers.swift +2 -4
  32. package/ios/NewArch/ApplePayButtonComponentView.mm +1 -1
  33. package/ios/OldArch/StripeSdkEventEmitterCompat.h +2 -0
  34. package/ios/OldArch/StripeSdkEventEmitterCompat.m +12 -0
  35. package/ios/PaymentMethodFactory.swift +0 -17
  36. package/ios/PushProvisioning/AddToWalletButtonView.swift +1 -1
  37. package/ios/StripeSdk.mm +14 -0
  38. package/ios/StripeSdkEmitter.swift +2 -0
  39. package/ios/StripeSdkImpl+CustomerSheet.swift +71 -20
  40. package/ios/StripeSdkImpl+PaymentSheet.swift +29 -13
  41. package/ios/StripeSdkImpl.swift +9 -7
  42. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  43. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  44. package/lib/commonjs/components/AddressSheet.js +1 -1
  45. package/lib/commonjs/components/AddressSheet.js.map +1 -1
  46. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  47. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  48. package/lib/commonjs/components/CardField.js +1 -1
  49. package/lib/commonjs/components/CardField.js.map +1 -1
  50. package/lib/commonjs/components/CardForm.js +1 -1
  51. package/lib/commonjs/components/CardForm.js.map +1 -1
  52. package/lib/commonjs/components/CustomerSheet.js +1 -1
  53. package/lib/commonjs/components/CustomerSheet.js.map +1 -1
  54. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  55. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  56. package/lib/commonjs/components/StripeContainer.js +1 -1
  57. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  58. package/lib/commonjs/events.js.map +1 -1
  59. package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
  60. package/lib/commonjs/specs/NativeApplePayButton.js.map +1 -1
  61. package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
  62. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  63. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  64. package/lib/commonjs/types/PaymentIntent.js.map +1 -1
  65. package/lib/commonjs/types/PaymentSheet.js.map +1 -1
  66. package/lib/module/components/AddToWalletButton.js +1 -1
  67. package/lib/module/components/AddToWalletButton.js.map +1 -1
  68. package/lib/module/components/AddressSheet.js +1 -1
  69. package/lib/module/components/AddressSheet.js.map +1 -1
  70. package/lib/module/components/AuBECSDebitForm.js +1 -1
  71. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  72. package/lib/module/components/CardField.js +1 -1
  73. package/lib/module/components/CardField.js.map +1 -1
  74. package/lib/module/components/CardForm.js +1 -1
  75. package/lib/module/components/CardForm.js.map +1 -1
  76. package/lib/module/components/CustomerSheet.js +1 -1
  77. package/lib/module/components/CustomerSheet.js.map +1 -1
  78. package/lib/module/components/PlatformPayButton.js +1 -1
  79. package/lib/module/components/PlatformPayButton.js.map +1 -1
  80. package/lib/module/components/StripeContainer.js +1 -1
  81. package/lib/module/components/StripeContainer.js.map +1 -1
  82. package/lib/module/events.js.map +1 -1
  83. package/lib/module/specs/NativeApplePayButton.js +1 -1
  84. package/lib/module/specs/NativeApplePayButton.js.map +1 -1
  85. package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
  86. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  87. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  88. package/lib/module/types/PaymentIntent.js.map +1 -1
  89. package/lib/module/types/PaymentSheet.js.map +1 -1
  90. package/lib/typescript/src/components/CustomerSheet.d.ts +1 -1
  91. package/lib/typescript/src/components/CustomerSheet.d.ts.map +1 -1
  92. package/lib/typescript/src/events.d.ts +2 -0
  93. package/lib/typescript/src/events.d.ts.map +1 -1
  94. package/lib/typescript/src/specs/NativeApplePayButton.d.ts +1 -1
  95. package/lib/typescript/src/specs/NativeApplePayButton.d.ts.map +1 -1
  96. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +3 -1
  97. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
  98. package/lib/typescript/src/types/ConfirmationToken.d.ts +0 -3
  99. package/lib/typescript/src/types/ConfirmationToken.d.ts.map +1 -1
  100. package/lib/typescript/src/types/CustomerSheet.d.ts +70 -12
  101. package/lib/typescript/src/types/CustomerSheet.d.ts.map +1 -1
  102. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +2 -4
  103. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
  104. package/lib/typescript/src/types/PaymentIntent.d.ts +1 -9
  105. package/lib/typescript/src/types/PaymentIntent.d.ts.map +1 -1
  106. package/lib/typescript/src/types/PaymentMethod.d.ts +2 -8
  107. package/lib/typescript/src/types/PaymentMethod.d.ts.map +1 -1
  108. package/lib/typescript/src/types/PaymentSheet.d.ts +1 -6
  109. package/lib/typescript/src/types/PaymentSheet.d.ts.map +1 -1
  110. package/package.json +1 -1
  111. package/src/components/CustomerSheet.tsx +65 -9
  112. package/src/components/PlatformPayButton.tsx +1 -1
  113. package/src/events.ts +2 -0
  114. package/src/specs/NativeApplePayButton.ts +1 -1
  115. package/src/specs/NativeStripeSdkModule.ts +7 -0
  116. package/src/types/ConfirmationToken.ts +0 -3
  117. package/src/types/CustomerSheet.ts +80 -12
  118. package/src/types/EmbeddedPaymentElement.tsx +2 -4
  119. package/src/types/PaymentIntent.ts +0 -10
  120. package/src/types/PaymentMethod.ts +0 -9
  121. package/src/types/PaymentSheet.ts +1 -6
  122. package/stripe-react-native.podspec +1 -1
  123. package/android/src/main/java/com/reactnativestripesdk/GooglePayLauncherFragment.kt +0 -146
  124. package/android/src/main/java/com/reactnativestripesdk/GooglePayPaymentMethodLauncherFragment.kt +0 -68
  125. package/android/src/main/java/com/reactnativestripesdk/addresssheet/AddressLauncherFragment.kt +0 -102
  126. package/android/src/main/java/com/reactnativestripesdk/utils/StripeFragment.kt +0 -52
@@ -0,0 +1,78 @@
1
+ package com.reactnativestripesdk.addresssheet
2
+
3
+ import android.annotation.SuppressLint
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.WritableMap
6
+ import com.reactnativestripesdk.utils.ErrorType
7
+ import com.reactnativestripesdk.utils.StripeUIManager
8
+ import com.reactnativestripesdk.utils.createError
9
+ import com.stripe.android.core.reactnative.ReactNativeSdkInternal
10
+ import com.stripe.android.paymentsheet.PaymentSheet
11
+ import com.stripe.android.paymentsheet.addresselement.AddressDetails
12
+ import com.stripe.android.paymentsheet.addresselement.AddressLauncher
13
+ import com.stripe.android.paymentsheet.addresselement.AddressLauncherResult
14
+
15
+ @OptIn(ReactNativeSdkInternal::class)
16
+ class AddressLauncherManager(
17
+ context: ReactApplicationContext,
18
+ appearance: PaymentSheet.Appearance,
19
+ defaultAddress: AddressDetails?,
20
+ allowedCountries: Set<String>,
21
+ buttonTitle: String?,
22
+ title: String?,
23
+ googlePlacesApiKey: String?,
24
+ autocompleteCountries: Set<String>,
25
+ additionalFields: AddressLauncher.AdditionalFieldsConfiguration?,
26
+ private var callback: ((error: WritableMap?, address: AddressDetails?) -> Unit),
27
+ ) : StripeUIManager(context) {
28
+ companion object {
29
+ internal var publishableKey: String? = null
30
+ }
31
+
32
+ private lateinit var addressLauncher: AddressLauncher
33
+ private var configuration =
34
+ AddressLauncher.Configuration(
35
+ appearance = appearance,
36
+ address = defaultAddress,
37
+ allowedCountries = allowedCountries,
38
+ buttonTitle = buttonTitle,
39
+ additionalFields = additionalFields,
40
+ title = title,
41
+ googlePlacesApiKey = googlePlacesApiKey,
42
+ autocompleteCountries = autocompleteCountries,
43
+ )
44
+
45
+ override fun onPresent() {
46
+ val activity = getCurrentActivityOrResolveWithError(promise) ?: return
47
+ publishableKey?.let { publishableKey ->
48
+ @SuppressLint("RestrictedApi")
49
+ addressLauncher =
50
+ AddressLauncher(activity, signal, ::onAddressLauncherResult).also {
51
+ it.present(publishableKey = publishableKey, configuration = configuration)
52
+ }
53
+ }
54
+ ?: run {
55
+ callback.invoke(
56
+ createError(
57
+ ErrorType.Failed.toString(),
58
+ "No publishable key set. Stripe has not been initialized. Initialize Stripe in your app with the StripeProvider component or the initStripe method.",
59
+ ),
60
+ null,
61
+ )
62
+ }
63
+ }
64
+
65
+ private fun onAddressLauncherResult(result: AddressLauncherResult) {
66
+ when (result) {
67
+ is AddressLauncherResult.Canceled -> {
68
+ callback.invoke(
69
+ createError(ErrorType.Canceled.toString(), "The flow has been canceled."),
70
+ null,
71
+ )
72
+ }
73
+ is AddressLauncherResult.Succeeded -> {
74
+ callback.invoke(null, result.address)
75
+ }
76
+ }
77
+ }
78
+ }
@@ -1,7 +1,6 @@
1
1
  package com.reactnativestripesdk.addresssheet
2
2
 
3
3
  import android.annotation.SuppressLint
4
- import android.os.Bundle
5
4
  import android.util.Log
6
5
  import android.widget.FrameLayout
7
6
  import com.facebook.react.bridge.ReadableMap
@@ -13,7 +12,7 @@ import com.reactnativestripesdk.buildPaymentSheetAppearance
13
12
  import com.reactnativestripesdk.utils.ErrorType
14
13
  import com.reactnativestripesdk.utils.PaymentSheetAppearanceException
15
14
  import com.reactnativestripesdk.utils.createError
16
- import com.reactnativestripesdk.utils.toBundleObject
15
+ import com.reactnativestripesdk.utils.getBooleanOr
17
16
  import com.stripe.android.paymentsheet.PaymentSheet
18
17
  import com.stripe.android.paymentsheet.addresselement.AddressDetails
19
18
  import com.stripe.android.paymentsheet.addresselement.AddressLauncher
@@ -31,6 +30,14 @@ class AddressSheetView(
31
30
  private var googlePlacesApiKey: String? = null
32
31
  private var autocompleteCountries: Set<String> = emptySet()
33
32
  private var additionalFields: AddressLauncher.AdditionalFieldsConfiguration? = null
33
+ private var addressSheetManager: AddressLauncherManager? = null
34
+
35
+ override fun onDetachedFromWindow() {
36
+ super.onDetachedFromWindow()
37
+
38
+ addressSheetManager?.destroy()
39
+ addressSheetManager = null
40
+ }
34
41
 
35
42
  private fun onSubmit(params: WritableMap) {
36
43
  UIManagerHelper.getEventDispatcherForReactTag(context, id)?.dispatchEvent(
@@ -59,29 +66,37 @@ class AddressSheetView(
59
66
  private fun launchAddressSheet() {
60
67
  val appearance =
61
68
  try {
62
- buildPaymentSheetAppearance(toBundleObject(appearanceParams), context)
69
+ buildPaymentSheetAppearance(appearanceParams, context)
63
70
  } catch (error: PaymentSheetAppearanceException) {
64
71
  onError(createError(ErrorType.Failed.toString(), error))
65
72
  return
66
73
  }
67
- AddressLauncherFragment().presentAddressSheet(
68
- context,
69
- appearance,
70
- defaultAddress,
71
- allowedCountries,
72
- buttonTitle,
73
- sheetTitle,
74
- googlePlacesApiKey,
75
- autocompleteCountries,
76
- additionalFields,
77
- ) { error, address ->
78
- if (address != null) {
79
- onSubmit(buildResult(address))
80
- } else {
81
- onError(error)
74
+ addressSheetManager?.destroy()
75
+ addressSheetManager =
76
+ AddressLauncherManager(
77
+ context.reactApplicationContext,
78
+ appearance,
79
+ defaultAddress,
80
+ allowedCountries,
81
+ buttonTitle,
82
+ sheetTitle,
83
+ googlePlacesApiKey,
84
+ autocompleteCountries,
85
+ additionalFields,
86
+ ) { error, address ->
87
+ addressSheetManager?.destroy()
88
+ addressSheetManager = null
89
+
90
+ if (address != null) {
91
+ onSubmit(buildResult(address))
92
+ } else {
93
+ onError(error)
94
+ }
95
+ isVisible = false
96
+ }.also {
97
+ it.create()
98
+ it.present()
82
99
  }
83
- isVisible = false
84
- }
85
100
  }
86
101
 
87
102
  fun setAppearance(appearanceParams: ReadableMap?) {
@@ -117,27 +132,25 @@ class AddressSheetView(
117
132
  }
118
133
 
119
134
  companion object {
120
- internal fun buildAddressDetails(bundle: Bundle): AddressDetails =
135
+ internal fun buildAddressDetails(map: ReadableMap): AddressDetails =
121
136
  AddressDetails(
122
- name = bundle.getString("name"),
123
- address = buildAddress(bundle.getBundle("address")),
124
- phoneNumber = bundle.getString("phone"),
125
- isCheckboxSelected = bundle.getBoolean("isCheckboxSelected"),
137
+ name = map.getString("name"),
138
+ address = buildAddress(map.getMap("address")),
139
+ phoneNumber = map.getString("phone"),
140
+ isCheckboxSelected = map.getBooleanOr("isCheckboxSelected", false),
126
141
  )
127
142
 
128
- internal fun buildAddressDetails(map: ReadableMap): AddressDetails = buildAddressDetails(toBundleObject(map))
129
-
130
- internal fun buildAddress(bundle: Bundle?): PaymentSheet.Address? {
131
- if (bundle == null) {
143
+ internal fun buildAddress(map: ReadableMap?): PaymentSheet.Address? {
144
+ if (map == null) {
132
145
  return null
133
146
  }
134
147
  return PaymentSheet.Address(
135
- city = bundle.getString("city"),
136
- country = bundle.getString("country"),
137
- line1 = bundle.getString("line1"),
138
- line2 = bundle.getString("line2"),
139
- state = bundle.getString("state"),
140
- postalCode = bundle.getString("postalCode"),
148
+ city = map.getString("city"),
149
+ country = map.getString("country"),
150
+ line1 = map.getString("line1"),
151
+ line2 = map.getString("line2"),
152
+ state = map.getString("state"),
153
+ postalCode = map.getString("postalCode"),
141
154
  )
142
155
  }
143
156
 
@@ -10,12 +10,16 @@ import android.util.Log
10
10
  import com.facebook.react.bridge.Arguments
11
11
  import com.facebook.react.bridge.Promise
12
12
  import com.facebook.react.bridge.ReactApplicationContext
13
+ import com.facebook.react.bridge.ReadableMap
13
14
  import com.facebook.react.bridge.WritableMap
14
15
  import com.facebook.react.bridge.WritableNativeMap
15
16
  import com.reactnativestripesdk.ReactNativeCustomerAdapter
17
+ import com.reactnativestripesdk.ReactNativeCustomerSessionProvider
16
18
  import com.reactnativestripesdk.buildPaymentSheetAppearance
17
19
  import com.reactnativestripesdk.getBase64FromBitmap
18
20
  import com.reactnativestripesdk.getBitmapFromDrawable
21
+ import com.reactnativestripesdk.getIntegerArrayList
22
+ import com.reactnativestripesdk.getStringArrayList
19
23
  import com.reactnativestripesdk.mapToAddressCollectionMode
20
24
  import com.reactnativestripesdk.mapToCardBrandAcceptance
21
25
  import com.reactnativestripesdk.mapToCollectionMode
@@ -23,11 +27,13 @@ import com.reactnativestripesdk.utils.CreateTokenErrorType
23
27
  import com.reactnativestripesdk.utils.ErrorType
24
28
  import com.reactnativestripesdk.utils.KeepJsAwakeTask
25
29
  import com.reactnativestripesdk.utils.PaymentSheetAppearanceException
26
- import com.reactnativestripesdk.utils.StripeFragment
30
+ import com.reactnativestripesdk.utils.StripeUIManager
27
31
  import com.reactnativestripesdk.utils.createError
32
+ import com.reactnativestripesdk.utils.getBooleanOr
28
33
  import com.reactnativestripesdk.utils.mapFromPaymentMethod
29
34
  import com.reactnativestripesdk.utils.mapToPreferredNetworks
30
35
  import com.stripe.android.ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi
36
+ import com.stripe.android.core.reactnative.ReactNativeSdkInternal
31
37
  import com.stripe.android.customersheet.CustomerAdapter
32
38
  import com.stripe.android.customersheet.CustomerEphemeralKey
33
39
  import com.stripe.android.customersheet.CustomerSheet
@@ -39,66 +45,32 @@ import kotlinx.coroutines.CoroutineScope
39
45
  import kotlinx.coroutines.Dispatchers
40
46
  import kotlinx.coroutines.launch
41
47
 
42
- @OptIn(ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi::class)
43
- class CustomerSheetFragment : StripeFragment() {
48
+ @OptIn(ReactNativeSdkInternal::class, ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi::class)
49
+ class CustomerSheetManager(
50
+ context: ReactApplicationContext,
51
+ private var arguments: ReadableMap,
52
+ private var customerAdapterOverrides: ReadableMap,
53
+ private var initPromise: Promise,
54
+ ) : StripeUIManager(context) {
44
55
  private var customerSheet: CustomerSheet? = null
45
56
  internal var customerAdapter: ReactNativeCustomerAdapter? = null
46
- internal var context: ReactApplicationContext? = null
47
- internal var initPromise: Promise? = null
57
+ internal var customerSessionProvider: ReactNativeCustomerSessionProvider? = null
48
58
  private var presentPromise: Promise? = null
49
59
  private var keepJsAwake: KeepJsAwakeTask? = null
50
60
 
51
- override fun prepare() {
52
- val context =
53
- context
54
- ?: run {
55
- Log.e(
56
- "StripeReactNative",
57
- "No context found during CustomerSheet.initialize. Please file an issue: https://github.com/stripe/stripe-react-native/issues",
58
- )
59
- return
60
- }
61
- val initPromise =
62
- initPromise
63
- ?: run {
64
- Log.e(
65
- "StripeReactNative",
66
- "No promise found for CustomerSheet.initialize. Please file an issue: https://github.com/stripe/stripe-react-native/issues",
67
- )
68
- return
69
- }
70
-
71
- val headerTextForSelectionScreen = arguments?.getString("headerTextForSelectionScreen")
72
- val merchantDisplayName = arguments?.getString("merchantDisplayName")
73
- val googlePayEnabled = arguments?.getBoolean("googlePayEnabled") ?: false
74
- val billingDetailsBundle = arguments?.getBundle("defaultBillingDetails")
75
- val billingConfigParams = arguments?.getBundle("billingDetailsCollectionConfiguration")
76
- val setupIntentClientSecret = arguments?.getString("setupIntentClientSecret")
77
- val customerId = arguments?.getString("customerId")
78
- val customerEphemeralKeySecret = arguments?.getString("customerEphemeralKeySecret")
79
- val customerAdapterOverrideParams = arguments?.getBundle("customerAdapter")
61
+ override fun onCreate() {
62
+ val headerTextForSelectionScreen = arguments.getString("headerTextForSelectionScreen")
63
+ val merchantDisplayName = arguments.getString("merchantDisplayName")
64
+ val googlePayEnabled = arguments.getBooleanOr("googlePayEnabled", false)
65
+ val billingDetailsMap = arguments.getMap("defaultBillingDetails")
66
+ val billingConfigParams = arguments.getMap("billingDetailsCollectionConfiguration")
80
67
  val allowsRemovalOfLastSavedPaymentMethod =
81
- arguments?.getBoolean("allowsRemovalOfLastSavedPaymentMethod", true) ?: true
82
- val paymentMethodOrder = arguments?.getStringArrayList("paymentMethodOrder")
83
- if (customerId == null) {
84
- initPromise.resolve(
85
- createError(ErrorType.Failed.toString(), "You must provide a value for `customerId`"),
86
- )
87
- return
88
- }
89
- if (customerEphemeralKeySecret == null) {
90
- initPromise.resolve(
91
- createError(
92
- ErrorType.Failed.toString(),
93
- "You must provide a value for `customerEphemeralKeySecret`",
94
- ),
95
- )
96
- return
97
- }
68
+ arguments.getBooleanOr("allowsRemovalOfLastSavedPaymentMethod", true)
69
+ val paymentMethodOrder = arguments.getStringArrayList("paymentMethodOrder")
98
70
 
99
71
  val appearance =
100
72
  try {
101
- buildPaymentSheetAppearance(arguments?.getBundle("appearance"), context)
73
+ buildPaymentSheetAppearance(arguments.getMap("appearance"), context)
102
74
  } catch (error: PaymentSheetAppearanceException) {
103
75
  initPromise.resolve(createError(ErrorType.Failed.toString(), error))
104
76
  return
@@ -111,13 +83,13 @@ class CustomerSheetFragment : StripeFragment() {
111
83
  .googlePayEnabled(googlePayEnabled)
112
84
  .headerTextForSelectionScreen(headerTextForSelectionScreen)
113
85
  .preferredNetworks(
114
- mapToPreferredNetworks(arguments?.getIntegerArrayList("preferredNetworks")),
86
+ mapToPreferredNetworks(arguments.getIntegerArrayList("preferredNetworks")),
115
87
  ).allowsRemovalOfLastSavedPaymentMethod(allowsRemovalOfLastSavedPaymentMethod)
116
88
  .cardBrandAcceptance(mapToCardBrandAcceptance(arguments))
117
89
 
118
90
  paymentMethodOrder?.let { configuration.paymentMethodOrder(it) }
119
- billingDetailsBundle?.let {
120
- configuration.defaultBillingDetails(createDefaultBillingDetails(billingDetailsBundle))
91
+ billingDetailsMap?.let {
92
+ configuration.defaultBillingDetails(createDefaultBillingDetails(billingDetailsMap))
121
93
  }
122
94
  billingConfigParams?.let {
123
95
  configuration.billingDetailsCollectionConfiguration(
@@ -125,21 +97,63 @@ class CustomerSheetFragment : StripeFragment() {
125
97
  )
126
98
  }
127
99
 
128
- val customerAdapter =
129
- createCustomerAdapter(
130
- context,
131
- customerId,
132
- customerEphemeralKeySecret,
133
- setupIntentClientSecret,
134
- customerAdapterOverrideParams,
135
- ).also { this.customerAdapter = it }
136
-
137
- customerSheet =
138
- CustomerSheet.create(
139
- fragment = this,
140
- customerAdapter = customerAdapter,
141
- callback = ::handleResult,
100
+ val activity = getCurrentActivityOrResolveWithError(initPromise) ?: return
101
+
102
+ val customerEphemeralKeySecret = arguments.getString("customerEphemeralKeySecret")
103
+ val intentConfiguration = createIntentConfiguration(arguments.getMap("intentConfiguration"))
104
+ if (customerEphemeralKeySecret == null && intentConfiguration == null) {
105
+ initPromise.resolve(
106
+ createError(
107
+ ErrorType.Failed.toString(),
108
+ "You must provide either `customerEphemeralKeySecret` or `intentConfiguration`",
109
+ ),
142
110
  )
111
+ return
112
+ } else if (customerEphemeralKeySecret == null) {
113
+ val customerSessionProvider =
114
+ ReactNativeCustomerSessionProvider(
115
+ context = context,
116
+ intentConfiguration = intentConfiguration!!,
117
+ ).also { this.customerSessionProvider = it }
118
+
119
+ customerSheet =
120
+ CustomerSheet.create(
121
+ activity = activity,
122
+ customerSessionProvider = customerSessionProvider,
123
+ callback = ::handleResult,
124
+ )
125
+ } else if (intentConfiguration == null) {
126
+ val customerId = arguments.getString("customerId")
127
+ if (customerId == null) {
128
+ initPromise.resolve(
129
+ createError(ErrorType.Failed.toString(), "When using `customerEphemeralKeySecret` you must provide a value for `customerId`"),
130
+ )
131
+ return
132
+ }
133
+ val setupIntentClientSecret = arguments.getString("setupIntentClientSecret")
134
+ val customerAdapter =
135
+ createCustomerAdapter(
136
+ context,
137
+ customerId,
138
+ customerEphemeralKeySecret,
139
+ setupIntentClientSecret,
140
+ customerAdapterOverrides,
141
+ ).also { this.customerAdapter = it }
142
+
143
+ customerSheet =
144
+ CustomerSheet.create(
145
+ activity = activity,
146
+ customerAdapter = customerAdapter,
147
+ callback = ::handleResult,
148
+ )
149
+ } else {
150
+ initPromise.resolve(
151
+ createError(
152
+ ErrorType.Failed.toString(),
153
+ "You must provide either `customerEphemeralKeySecret` or `intentConfiguration`, but not both",
154
+ ),
155
+ )
156
+ }
143
157
 
144
158
  customerSheet?.configure(configuration.build())
145
159
 
@@ -168,16 +182,15 @@ class CustomerSheetFragment : StripeFragment() {
168
182
  resolvePresentPromise(promiseResult)
169
183
  }
170
184
 
171
- fun present(
172
- timeout: Long?,
173
- promise: Promise,
174
- ) {
175
- keepJsAwake = context?.let { KeepJsAwakeTask(it).apply { start() } }
185
+ override fun onPresent() {
186
+ keepJsAwake = context.let { KeepJsAwakeTask(it).apply { start() } }
176
187
  presentPromise = promise
188
+ val timeout = timeout
177
189
  if (timeout != null) {
178
190
  presentWithTimeout(timeout)
191
+ } else {
192
+ customerSheet?.present() ?: run { resolvePresentPromise(createMissingInitError()) }
179
193
  }
180
- customerSheet?.present() ?: run { resolvePresentPromise(createMissingInitError()) }
181
194
  }
182
195
 
183
196
  private fun presentWithTimeout(timeout: Long) {
@@ -207,7 +220,7 @@ class CustomerSheetFragment : StripeFragment() {
207
220
 
208
221
  override fun onActivityDestroyed(activity: Activity) {
209
222
  activities = mutableListOf()
210
- context?.currentActivity?.application?.unregisterActivityLifecycleCallbacks(this)
223
+ context.currentActivity?.application?.unregisterActivityLifecycleCallbacks(this)
211
224
  }
212
225
  }
213
226
 
@@ -223,7 +236,7 @@ class CustomerSheetFragment : StripeFragment() {
223
236
  )
224
237
 
225
238
  context
226
- ?.currentActivity
239
+ .currentActivity
227
240
  ?.application
228
241
  ?.registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
229
242
 
@@ -279,37 +292,35 @@ class CustomerSheetFragment : StripeFragment() {
279
292
  }
280
293
 
281
294
  companion object {
282
- internal const val TAG = "customer_sheet_launch_fragment"
283
-
284
295
  internal fun createMissingInitError(): WritableMap =
285
296
  createError(ErrorType.Failed.toString(), "No customer sheet has been initialized yet.")
286
297
 
287
- internal fun createDefaultBillingDetails(bundle: Bundle): PaymentSheet.BillingDetails {
288
- val addressBundle = bundle.getBundle("address")
298
+ internal fun createDefaultBillingDetails(map: ReadableMap): PaymentSheet.BillingDetails {
299
+ val addressMap = map.getMap("address")
289
300
  val address =
290
301
  PaymentSheet.Address(
291
- addressBundle?.getString("city"),
292
- addressBundle?.getString("country"),
293
- addressBundle?.getString("line1"),
294
- addressBundle?.getString("line2"),
295
- addressBundle?.getString("postalCode"),
296
- addressBundle?.getString("state"),
302
+ addressMap?.getString("city"),
303
+ addressMap?.getString("country"),
304
+ addressMap?.getString("line1"),
305
+ addressMap?.getString("line2"),
306
+ addressMap?.getString("postalCode"),
307
+ addressMap?.getString("state"),
297
308
  )
298
309
  return PaymentSheet.BillingDetails(
299
310
  address,
300
- bundle.getString("email"),
301
- bundle.getString("name"),
302
- bundle.getString("phone"),
311
+ map.getString("email"),
312
+ map.getString("name"),
313
+ map.getString("phone"),
303
314
  )
304
315
  }
305
316
 
306
- internal fun createBillingDetailsCollectionConfiguration(bundle: Bundle): PaymentSheet.BillingDetailsCollectionConfiguration =
317
+ internal fun createBillingDetailsCollectionConfiguration(map: ReadableMap): PaymentSheet.BillingDetailsCollectionConfiguration =
307
318
  PaymentSheet.BillingDetailsCollectionConfiguration(
308
- name = mapToCollectionMode(bundle.getString("name")),
309
- phone = mapToCollectionMode(bundle.getString("phone")),
310
- email = mapToCollectionMode(bundle.getString("email")),
311
- address = mapToAddressCollectionMode(bundle.getString("address")),
312
- attachDefaultsToPaymentMethod = bundle.getBoolean("attachDefaultsToPaymentMethod", false),
319
+ name = mapToCollectionMode(map.getString("name")),
320
+ phone = mapToCollectionMode(map.getString("phone")),
321
+ email = mapToCollectionMode(map.getString("email")),
322
+ address = mapToAddressCollectionMode(map.getString("address")),
323
+ attachDefaultsToPaymentMethod = map.getBooleanOr("attachDefaultsToPaymentMethod", false),
313
324
  )
314
325
 
315
326
  internal fun createCustomerAdapter(
@@ -317,7 +328,7 @@ class CustomerSheetFragment : StripeFragment() {
317
328
  customerId: String,
318
329
  customerEphemeralKeySecret: String,
319
330
  setupIntentClientSecret: String?,
320
- customerAdapterOverrideParams: Bundle?,
331
+ customerAdapterOverrideParams: ReadableMap?,
321
332
  ): ReactNativeCustomerAdapter {
322
333
  val ephemeralKeyProvider = {
323
334
  CustomerAdapter.Result.success(
@@ -350,18 +361,17 @@ class CustomerSheetFragment : StripeFragment() {
350
361
  context = context,
351
362
  adapter = customerAdapter,
352
363
  overridesFetchPaymentMethods =
353
- customerAdapterOverrideParams?.getBoolean("fetchPaymentMethods") ?: false,
364
+ customerAdapterOverrideParams.getBooleanOr("fetchPaymentMethods", false),
354
365
  overridesAttachPaymentMethod =
355
- customerAdapterOverrideParams?.getBoolean("attachPaymentMethod") ?: false,
366
+ customerAdapterOverrideParams.getBooleanOr("attachPaymentMethod", false),
356
367
  overridesDetachPaymentMethod =
357
- customerAdapterOverrideParams?.getBoolean("detachPaymentMethod") ?: false,
368
+ customerAdapterOverrideParams.getBooleanOr("detachPaymentMethod", false),
358
369
  overridesSetSelectedPaymentOption =
359
- customerAdapterOverrideParams?.getBoolean("setSelectedPaymentOption") ?: false,
370
+ customerAdapterOverrideParams.getBooleanOr("setSelectedPaymentOption", false),
360
371
  overridesFetchSelectedPaymentOption =
361
- customerAdapterOverrideParams?.getBoolean("fetchSelectedPaymentOption") ?: false,
372
+ customerAdapterOverrideParams.getBooleanOr("fetchSelectedPaymentOption", false),
362
373
  overridesSetupIntentClientSecretForCustomerAttach =
363
- customerAdapterOverrideParams?.getBoolean("setupIntentClientSecretForCustomerAttach")
364
- ?: false,
374
+ customerAdapterOverrideParams.getBooleanOr("setupIntentClientSecretForCustomerAttach", false),
365
375
  )
366
376
  }
367
377
 
@@ -389,6 +399,19 @@ class CustomerSheetFragment : StripeFragment() {
389
399
  return paymentOptionResult
390
400
  }
391
401
 
402
+ internal fun createIntentConfiguration(intentConfigurationBundle: ReadableMap?): CustomerSheet.IntentConfiguration? =
403
+ intentConfigurationBundle?.let { bundle ->
404
+ val onBehalfOf = bundle.getString("onBehalfOf")
405
+ CustomerSheet.IntentConfiguration
406
+ .Builder()
407
+ .paymentMethodTypes(bundle.getStringArrayList("paymentMethodTypes") ?: emptyList())
408
+ .apply {
409
+ if (onBehalfOf != null) {
410
+ this.onBehalfOf(onBehalfOf)
411
+ }
412
+ }.build()
413
+ }
414
+
392
415
  private fun buildResult(
393
416
  label: String,
394
417
  drawable: Drawable,
@@ -0,0 +1,35 @@
1
+ package com.reactnativestripesdk
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import com.stripe.android.customersheet.CustomerSheet
5
+ import kotlinx.coroutines.CompletableDeferred
6
+
7
+ class ReactNativeCustomerSessionProvider(
8
+ val context: ReactApplicationContext,
9
+ val intentConfiguration: CustomerSheet.IntentConfiguration,
10
+ ) : CustomerSheet.CustomerSessionProvider() {
11
+ private val stripeSdkModule = context.getNativeModule(StripeSdkModule::class.java)
12
+
13
+ internal var provideSetupIntentClientSecretCallback: CompletableDeferred<String>? = null
14
+ internal var providesCustomerSessionClientSecretCallback: CompletableDeferred<CustomerSheet.CustomerSessionClientSecret>? = null
15
+
16
+ override suspend fun intentConfiguration(): Result<CustomerSheet.IntentConfiguration> = Result.success(intentConfiguration)
17
+
18
+ override suspend fun provideSetupIntentClientSecret(customerId: String): Result<String> {
19
+ CompletableDeferred<String>().also {
20
+ provideSetupIntentClientSecretCallback = it
21
+ stripeSdkModule?.eventEmitter?.emitOnCustomerSessionProviderSetupIntentClientSecret()
22
+ val resultFromJavascript = it.await()
23
+ return Result.success(resultFromJavascript)
24
+ }
25
+ }
26
+
27
+ override suspend fun providesCustomerSessionClientSecret(): Result<CustomerSheet.CustomerSessionClientSecret> {
28
+ CompletableDeferred<CustomerSheet.CustomerSessionClientSecret>().also {
29
+ providesCustomerSessionClientSecretCallback = it
30
+ stripeSdkModule?.eventEmitter?.emitOnCustomerSessionProviderCustomerSessionClientSecret()
31
+ val resultFromJavascript = it.await()
32
+ return Result.success(resultFromJavascript)
33
+ }
34
+ }
35
+ }
@@ -3,10 +3,8 @@ package com.reactnativestripesdk.utils
3
3
  import android.content.Context
4
4
  import android.view.View
5
5
  import android.view.inputmethod.InputMethodManager
6
- import androidx.fragment.app.Fragment
7
- import androidx.fragment.app.FragmentActivity
8
6
  import com.facebook.react.bridge.Dynamic
9
- import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReadableArray
10
8
  import com.facebook.react.bridge.ReadableMap
11
9
  import com.facebook.react.bridge.ReadableType
12
10
 
@@ -26,17 +24,54 @@ fun View.hideSoftKeyboard() {
26
24
  }
27
25
  }
28
26
 
29
- fun Fragment.removeFragment(context: ReactApplicationContext) {
30
- (context.currentActivity as? FragmentActivity)?.supportFragmentManager?.let {
31
- if (it.findFragmentByTag(this.tag) != null) {
32
- it.beginTransaction().remove(this).commitAllowingStateLoss()
33
- }
27
+ fun ReadableMap?.getBooleanOr(
28
+ key: String,
29
+ default: Boolean,
30
+ ): Boolean = if (this?.hasKey(key) == true && this.getType(key) == ReadableType.Boolean) this.getBoolean(key) else default
31
+
32
+ fun ReadableMap?.getIntOrNull(key: String): Int? =
33
+ if (this?.hasKey(key) == true && this.getType(key) == ReadableType.Number) this.getInt(key) else null
34
+
35
+ fun ReadableMap?.getIntOr(
36
+ key: String,
37
+ default: Int,
38
+ ): Int = getIntOrNull(key) ?: default
39
+
40
+ fun ReadableMap?.getDoubleOrNull(key: String): Double? =
41
+ if (this?.hasKey(key) == true && this.getType(key) == ReadableType.Number) this.getDouble(key) else null
42
+
43
+ fun ReadableMap?.getDoubleOr(
44
+ key: String,
45
+ default: Double,
46
+ ): Double = getDoubleOrNull(key) ?: default
47
+
48
+ fun ReadableMap?.getFloatOrNull(key: String): Float? =
49
+ if (this?.hasKey(key) == true && this.getType(key) == ReadableType.Number) this.getDouble(key).toFloat() else null
50
+
51
+ fun ReadableMap?.getBooleanOrNull(key: String): Boolean? =
52
+ if (this?.hasKey(key) == true && this.getType(key) == ReadableType.Boolean) this.getBoolean(key) else null
53
+
54
+ fun ReadableMap?.getFloatOr(
55
+ key: String,
56
+ default: Float,
57
+ ): Float = getFloatOrNull(key) ?: default
58
+
59
+ fun ReadableMap.isEmpty() = !this.keySetIterator().hasNextKey()
60
+
61
+ fun ReadableMap.forEachKey(callback: (key: String) -> Unit) {
62
+ val iterator = this.keySetIterator()
63
+ while (iterator.hasNextKey()) {
64
+ val key = iterator.nextKey()
65
+ callback(key)
34
66
  }
35
67
  }
36
68
 
37
- fun ReadableMap.getBooleanOr(
38
- key: String,
39
- default: Boolean,
40
- ): Boolean = if (this.hasKey(key)) this.getBoolean(key) else default
69
+ fun ReadableArray.forEachMap(callback: (map: ReadableMap) -> Unit) {
70
+ for (i in 0 until this.size()) {
71
+ if (this.getType(i) == ReadableType.Map) {
72
+ this.getMap(i)?.let(callback)
73
+ }
74
+ }
75
+ }
41
76
 
42
77
  fun Dynamic.asMapOrNull(): ReadableMap? = if (this.type == ReadableType.Map) this.asMap() else null