@stripe/stripe-react-native 0.60.0 → 0.62.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 (113) hide show
  1. package/android/.idea/.name +1 -0
  2. package/android/.idea/AndroidProjectSystem.xml +6 -0
  3. package/android/.idea/android.iml +9 -0
  4. package/android/.idea/caches/deviceStreaming.xml +1550 -0
  5. package/android/.idea/compiler.xml +6 -0
  6. package/android/.idea/gradle.xml +19 -0
  7. package/android/.idea/kotlinc.xml +7 -0
  8. package/android/.idea/migrations.xml +10 -0
  9. package/android/.idea/misc.xml +10 -0
  10. package/android/.idea/runConfigurations.xml +17 -0
  11. package/android/.idea/vcs.xml +6 -0
  12. package/android/gradle.properties +2 -2
  13. package/android/local.properties +8 -0
  14. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetManager.kt +200 -198
  15. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +3 -1
  16. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonView.kt +17 -16
  17. package/android/src/onramp/java/com/reactnativestripesdk/OnrampSdkModule.kt +2 -36
  18. package/ios/Mappers.swift +71 -39
  19. package/ios/StripeSdkImpl.swift +11 -4
  20. package/ios/StripeSwiftInterop.h +1 -1
  21. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  22. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  23. package/lib/commonjs/components/AddressSheet.js +1 -1
  24. package/lib/commonjs/components/AddressSheet.js.map +1 -1
  25. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  26. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  27. package/lib/commonjs/components/CardField.js +1 -1
  28. package/lib/commonjs/components/CardField.js.map +1 -1
  29. package/lib/commonjs/components/CardForm.js +1 -1
  30. package/lib/commonjs/components/CardForm.js.map +1 -1
  31. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  32. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  33. package/lib/commonjs/components/StripeContainer.js +1 -1
  34. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  35. package/lib/commonjs/connect/Components.js +1 -1
  36. package/lib/commonjs/connect/Components.js.map +1 -1
  37. package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
  38. package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
  39. package/lib/commonjs/connect/EmbeddedComponent.js +1 -1
  40. package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
  41. package/lib/commonjs/connect/ModalCloseButton.js +1 -1
  42. package/lib/commonjs/connect/ModalCloseButton.js.map +1 -1
  43. package/lib/commonjs/connect/NavigationBar.js +1 -1
  44. package/lib/commonjs/connect/NavigationBar.js.map +1 -1
  45. package/lib/commonjs/helpers.js +1 -1
  46. package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
  47. package/lib/commonjs/specs/NativeAddressSheet.js +1 -1
  48. package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
  49. package/lib/commonjs/specs/NativeAuBECSDebitForm.js +1 -1
  50. package/lib/commonjs/specs/NativeCardField.js +1 -1
  51. package/lib/commonjs/specs/NativeCardField.js.map +1 -1
  52. package/lib/commonjs/specs/NativeCardForm.js +1 -1
  53. package/lib/commonjs/specs/NativeCardForm.js.map +1 -1
  54. package/lib/commonjs/specs/NativeConnectAccountOnboardingView.js +1 -1
  55. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
  56. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  57. package/lib/commonjs/specs/NativeGooglePayButton.js +1 -1
  58. package/lib/commonjs/specs/NativeNavigationBar.js +1 -1
  59. package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js +1 -1
  60. package/lib/commonjs/specs/NativeStripeContainer.js +1 -1
  61. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  62. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  63. package/lib/commonjs/types/Onramp.js +1 -1
  64. package/lib/commonjs/types/Onramp.js.map +1 -1
  65. package/lib/module/components/AddToWalletButton.js +1 -1
  66. package/lib/module/components/AddToWalletButton.js.map +1 -1
  67. package/lib/module/components/AddressSheet.js +1 -1
  68. package/lib/module/components/AddressSheet.js.map +1 -1
  69. package/lib/module/components/AuBECSDebitForm.js +1 -1
  70. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  71. package/lib/module/components/CardField.js +1 -1
  72. package/lib/module/components/CardField.js.map +1 -1
  73. package/lib/module/components/CardForm.js +1 -1
  74. package/lib/module/components/CardForm.js.map +1 -1
  75. package/lib/module/components/PlatformPayButton.js +1 -1
  76. package/lib/module/components/PlatformPayButton.js.map +1 -1
  77. package/lib/module/components/StripeContainer.js +1 -1
  78. package/lib/module/components/StripeContainer.js.map +1 -1
  79. package/lib/module/connect/Components.js +1 -1
  80. package/lib/module/connect/Components.js.map +1 -1
  81. package/lib/module/connect/ConnectComponentsProvider.js +1 -1
  82. package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
  83. package/lib/module/connect/EmbeddedComponent.js +1 -1
  84. package/lib/module/connect/EmbeddedComponent.js.map +1 -1
  85. package/lib/module/connect/ModalCloseButton.js +1 -1
  86. package/lib/module/connect/ModalCloseButton.js.map +1 -1
  87. package/lib/module/connect/NavigationBar.js +1 -1
  88. package/lib/module/connect/NavigationBar.js.map +1 -1
  89. package/lib/module/helpers.js +1 -1
  90. package/lib/module/specs/NativeAddToWalletButton.js +1 -1
  91. package/lib/module/specs/NativeAddressSheet.js +1 -1
  92. package/lib/module/specs/NativeApplePayButton.js +1 -1
  93. package/lib/module/specs/NativeAuBECSDebitForm.js +1 -1
  94. package/lib/module/specs/NativeCardField.js +1 -1
  95. package/lib/module/specs/NativeCardField.js.map +1 -1
  96. package/lib/module/specs/NativeCardForm.js +1 -1
  97. package/lib/module/specs/NativeCardForm.js.map +1 -1
  98. package/lib/module/specs/NativeConnectAccountOnboardingView.js +1 -1
  99. package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
  100. package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  101. package/lib/module/specs/NativeGooglePayButton.js +1 -1
  102. package/lib/module/specs/NativeNavigationBar.js +1 -1
  103. package/lib/module/specs/NativePaymentMethodMessagingElement.js +1 -1
  104. package/lib/module/specs/NativeStripeContainer.js +1 -1
  105. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  106. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  107. package/lib/module/types/Onramp.js +1 -1
  108. package/lib/module/types/Onramp.js.map +1 -1
  109. package/lib/typescript/src/types/Onramp.d.ts +11 -10
  110. package/lib/typescript/src/types/Onramp.d.ts.map +1 -1
  111. package/package.json +2 -3
  112. package/src/types/Onramp.ts +10 -9
  113. package/stripe-react-native.podspec +1 -1
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="CompilerConfiguration">
4
+ <bytecodeTargetLevel target="17" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="GradleMigrationSettings" migrationVersion="1" />
4
+ <component name="GradleSettings">
5
+ <option name="linkedExternalProjectsSettings">
6
+ <GradleProjectSettings>
7
+ <option name="testRunner" value="CHOOSE_PER_TEST" />
8
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
9
+ <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
10
+ <option name="modules">
11
+ <set>
12
+ <option value="$PROJECT_DIR$" />
13
+ <option value="$PROJECT_DIR$/lint-rules" />
14
+ </set>
15
+ </option>
16
+ </GradleProjectSettings>
17
+ </option>
18
+ </component>
19
+ </project>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="KotlinJpsPluginSettings">
4
+ <option name="externalSystemId" value="Gradle" />
5
+ <option name="version" value="1.8.0" />
6
+ </component>
7
+ </project>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectMigrations">
4
+ <option name="MigrateToGradleLocalJavaHome">
5
+ <set>
6
+ <option value="$PROJECT_DIR$" />
7
+ </set>
8
+ </option>
9
+ </component>
10
+ </project>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ExternalStorageConfigurationManager" enabled="true" />
4
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
5
+ <output url="file://$PROJECT_DIR$/build/classes" />
6
+ </component>
7
+ <component name="ProjectType">
8
+ <option name="id" value="Android" />
9
+ </component>
10
+ </project>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="RunConfigurationProducerService">
4
+ <option name="ignoredProducers">
5
+ <set>
6
+ <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
7
+ <option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
8
+ <option value="com.intellij.execution.junit.PatternConfigurationProducer" />
9
+ <option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
10
+ <option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
11
+ <option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
12
+ <option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
13
+ <option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
14
+ </set>
15
+ </option>
16
+ </component>
17
+ </project>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
5
+ </component>
6
+ </project>
@@ -1,6 +1,6 @@
1
- StripeSdk_kotlinVersion=2.3.10
1
+ StripeSdk_kotlinVersion=2.2.21
2
2
  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=23.0.2
6
+ StripeSdk_stripeVersion=23.2.+
@@ -0,0 +1,8 @@
1
+ ## This file must *NOT* be checked into Version Control Systems,
2
+ # as it contains information specific to your local configuration.
3
+ #
4
+ # Location of the SDK. This is only used by Gradle.
5
+ # For customization when using a Version Control System, please read the
6
+ # header note.
7
+ #Fri Aug 30 15:10:34 EDT 2024
8
+ sdk.dir=/Users/samer/Library/Android/sdk
@@ -86,209 +86,15 @@ class PaymentSheetManager(
86
86
  internal var paymentSheetIntentCreationCallback = CompletableDeferred<ReadableMap>()
87
87
  internal var paymentSheetConfirmationTokenCreationCallback = CompletableDeferred<ReadableMap>()
88
88
  private var keepJsAwake: KeepJsAwakeTask? = null
89
+ private var lastConfigureWasCustomFlow: Boolean? = null
89
90
 
90
91
  @SuppressLint("RestrictedApi")
91
92
  override fun onCreate() {
92
- val activity = getCurrentActivityOrResolveWithError(initPromise) ?: return
93
- val merchantDisplayName = arguments.getString("merchantDisplayName").orEmpty()
94
- if (merchantDisplayName.isEmpty()) {
95
- initPromise.resolve(
96
- createError(ErrorType.Failed.toString(), "merchantDisplayName cannot be empty or null."),
97
- )
98
- return
99
- }
100
- paymentIntentClientSecret = arguments.getString("paymentIntentClientSecret").orEmpty()
101
- setupIntentClientSecret = arguments.getString("setupIntentClientSecret").orEmpty()
102
- intentConfiguration =
103
- try {
104
- buildIntentConfiguration(arguments.getMap("intentConfiguration"))
105
- } catch (error: PaymentSheetException) {
106
- initPromise.resolve(createError(ErrorType.Failed.toString(), error))
107
- return
108
- }
109
-
110
- // Determine which callback type to use based on what's provided
111
- val intentConfigMap = arguments.getMap("intentConfiguration")
112
- val useConfirmationTokenCallback = intentConfigMap?.hasKey("confirmationTokenConfirmHandler") == true
113
-
114
- val paymentOptionCallback =
115
- PaymentOptionResultCallback { paymentOptionResult ->
116
- paymentOptionResult.paymentOption?.let { paymentOption ->
117
- // Convert drawable to bitmap asynchronously to avoid shared state issues
118
- CoroutineScope(Dispatchers.Default).launch {
119
- val imageString =
120
- try {
121
- convertDrawableToBase64(paymentOption.icon())
122
- } catch (e: Exception) {
123
- val result =
124
- createError(
125
- PaymentSheetErrorType.Failed.toString(),
126
- "Failed to process payment option image: ${e.message}",
127
- )
128
- resolvePresentPromise(result)
129
- return@launch
130
- }
131
-
132
- val option: WritableMap = Arguments.createMap()
133
- option.putString("label", paymentOption.label)
134
- option.putString("image", imageString)
135
- val additionalFields: Map<String, Any> = mapOf("didCancel" to paymentOptionResult.didCancel)
136
- val result = createResult("paymentOption", option, additionalFields)
137
- resolvePresentPromise(result)
138
- }
139
- } ?: run {
140
- val result =
141
- if (paymentSheetTimedOut) {
142
- paymentSheetTimedOut = false
143
- createError(PaymentSheetErrorType.Timeout.toString(), "The payment has timed out")
144
- } else {
145
- createError(
146
- PaymentSheetErrorType.Canceled.toString(),
147
- "The payment option selection flow has been canceled",
148
- )
149
- }
150
- resolvePresentPromise(result)
151
- }
152
- }
153
-
154
- val paymentResultCallback =
155
- PaymentSheetResultCallback { paymentResult ->
156
- if (paymentSheetTimedOut) {
157
- paymentSheetTimedOut = false
158
- resolvePaymentResult(
159
- createError(PaymentSheetErrorType.Timeout.toString(), "The payment has timed out"),
160
- )
161
- } else {
162
- when (paymentResult) {
163
- is PaymentSheetResult.Canceled -> {
164
- resolvePaymentResult(
165
- createError(
166
- PaymentSheetErrorType.Canceled.toString(),
167
- "The payment flow has been canceled",
168
- ),
169
- )
170
- }
171
-
172
- is PaymentSheetResult.Failed -> {
173
- resolvePaymentResult(
174
- createError(PaymentSheetErrorType.Failed.toString(), paymentResult.error),
175
- )
176
- }
177
-
178
- is PaymentSheetResult.Completed -> {
179
- resolvePaymentResult(Arguments.createMap())
180
- }
181
- }
182
- }
183
- }
184
-
185
- val createIntentCallback =
186
- CreateIntentCallback { paymentMethod, shouldSavePaymentMethod ->
187
- val stripeSdkModule: StripeSdkModule? = context.getNativeModule(StripeSdkModule::class.java)
188
- val params =
189
- Arguments.createMap().apply {
190
- putMap("paymentMethod", mapFromPaymentMethod(paymentMethod))
191
- putBoolean("shouldSavePaymentMethod", shouldSavePaymentMethod)
192
- }
193
-
194
- stripeSdkModule?.eventEmitter?.emitOnConfirmHandlerCallback(params)
195
-
196
- val resultFromJavascript = paymentSheetIntentCreationCallback.await()
197
- // reset the completable
198
- paymentSheetIntentCreationCallback = CompletableDeferred<ReadableMap>()
199
-
200
- return@CreateIntentCallback resultFromJavascript.getString("clientSecret")?.let {
201
- CreateIntentResult.Success(clientSecret = it)
202
- }
203
- ?: run {
204
- val errorMap = resultFromJavascript.getMap("error")
205
- CreateIntentResult.Failure(
206
- cause = Exception(errorMap?.getString("message")),
207
- displayMessage = errorMap?.getString("localizedMessage"),
208
- )
209
- }
210
- }
211
-
212
- val createConfirmationTokenCallback =
213
- CreateIntentWithConfirmationTokenCallback { confirmationToken ->
214
- val stripeSdkModule: StripeSdkModule? = context.getNativeModule(StripeSdkModule::class.java)
215
- val params =
216
- Arguments.createMap().apply {
217
- putMap("confirmationToken", mapFromConfirmationToken(confirmationToken))
218
- }
219
-
220
- stripeSdkModule?.eventEmitter?.emitOnConfirmationTokenHandlerCallback(params)
221
-
222
- val resultFromJavascript = paymentSheetConfirmationTokenCreationCallback.await()
223
- // reset the completable
224
- paymentSheetConfirmationTokenCreationCallback = CompletableDeferred<ReadableMap>()
225
-
226
- return@CreateIntentWithConfirmationTokenCallback resultFromJavascript.getString("clientSecret")?.let {
227
- CreateIntentResult.Success(clientSecret = it)
228
- }
229
- ?: run {
230
- val errorMap = resultFromJavascript.getMap("error")
231
- CreateIntentResult.Failure(
232
- cause = Exception(errorMap?.getString("message")),
233
- displayMessage = errorMap?.getString("localizedMessage"),
234
- )
235
- }
236
- }
237
-
238
- if (arguments.getBooleanOr("customFlow", false)) {
239
- if (flowController == null) {
240
- flowController =
241
- if (intentConfiguration != null) {
242
- val builder =
243
- PaymentSheet.FlowController
244
- .Builder(
245
- resultCallback = paymentResultCallback,
246
- paymentOptionResultCallback = paymentOptionCallback,
247
- )
248
- if (useConfirmationTokenCallback) {
249
- builder.createIntentCallback(createConfirmationTokenCallback)
250
- } else {
251
- builder.createIntentCallback(createIntentCallback)
252
- }
253
- builder
254
- .confirmCustomPaymentMethodCallback(this)
255
- .build(activity)
256
- } else {
257
- PaymentSheet.FlowController
258
- .Builder(
259
- resultCallback = paymentResultCallback,
260
- paymentOptionResultCallback = paymentOptionCallback,
261
- ).confirmCustomPaymentMethodCallback(this)
262
- .build(activity)
263
- }
264
- }
265
- } else {
266
- if (paymentSheet == null) {
267
- paymentSheet =
268
- if (intentConfiguration != null) {
269
- val builder = PaymentSheet.Builder(paymentResultCallback)
270
- if (useConfirmationTokenCallback) {
271
- builder.createIntentCallback(createConfirmationTokenCallback)
272
- } else {
273
- builder.createIntentCallback(createIntentCallback)
274
- }
275
- @SuppressLint("RestrictedApi")
276
- builder
277
- .confirmCustomPaymentMethodCallback(this)
278
- .build(activity, signal)
279
- } else {
280
- @SuppressLint("RestrictedApi")
281
- PaymentSheet
282
- .Builder(paymentResultCallback)
283
- .confirmCustomPaymentMethodCallback(this)
284
- .build(activity, signal)
285
- }
286
- }
287
- }
288
93
  configure(arguments, initPromise)
289
94
  }
290
95
 
291
96
  override fun onDestroy() {
97
+ super.onDestroy()
292
98
  flowController = null
293
99
  paymentSheet = null
294
100
  }
@@ -380,15 +186,211 @@ class PaymentSheetManager(
380
186
 
381
187
  paymentSheetConfiguration = configurationBuilder.build()
382
188
  if (args.getBooleanOr("customFlow", false)) {
189
+ lastConfigureWasCustomFlow = true
190
+ if (flowController == null) {
191
+ initFlowController(args, promise)
192
+ }
383
193
  configureFlowController(promise)
384
194
  } else {
195
+ lastConfigureWasCustomFlow = false
196
+ if (paymentSheet == null) {
197
+ initPaymentSheet(args, promise)
198
+ }
385
199
  promise.resolve(Arguments.createMap())
386
200
  }
387
201
  }
388
202
 
203
+ private fun initPaymentSheet(
204
+ args: ReadableMap,
205
+ promise: Promise,
206
+ ) {
207
+ val activity = getCurrentActivityOrResolveWithError(promise) ?: return
208
+ val intentConfigMap = args.getMap("intentConfiguration")
209
+ val useConfirmationTokenCallback = intentConfigMap?.hasKey("confirmationTokenConfirmHandler") == true
210
+ paymentSheet =
211
+ if (intentConfiguration != null) {
212
+ val builder = PaymentSheet.Builder(buildPaymentSheetResultCallback())
213
+ if (useConfirmationTokenCallback) {
214
+ builder.createIntentCallback(buildCreateConfirmationTokenCallback())
215
+ } else {
216
+ builder.createIntentCallback(buildIntentCreationCallback())
217
+ }
218
+ @SuppressLint("RestrictedApi")
219
+ builder
220
+ .confirmCustomPaymentMethodCallback(this)
221
+ .build(activity, signal)
222
+ } else {
223
+ @SuppressLint("RestrictedApi")
224
+ PaymentSheet
225
+ .Builder(buildPaymentSheetResultCallback())
226
+ .confirmCustomPaymentMethodCallback(this)
227
+ .build(activity, signal)
228
+ }
229
+ }
230
+
231
+ private fun initFlowController(
232
+ args: ReadableMap,
233
+ promise: Promise,
234
+ ) {
235
+ val activity = getCurrentActivityOrResolveWithError(promise) ?: return
236
+ val intentConfigMap = args.getMap("intentConfiguration")
237
+ val useConfirmationTokenCallback =
238
+ intentConfigMap?.hasKey("confirmationTokenConfirmHandler") == true
239
+ flowController =
240
+ if (intentConfiguration != null) {
241
+ val builder =
242
+ PaymentSheet.FlowController
243
+ .Builder(
244
+ resultCallback = buildPaymentSheetResultCallback(),
245
+ paymentOptionResultCallback = buildPaymentOptionCallback(),
246
+ )
247
+ if (useConfirmationTokenCallback) {
248
+ builder.createIntentCallback(buildCreateConfirmationTokenCallback())
249
+ } else {
250
+ builder.createIntentCallback(buildIntentCreationCallback())
251
+ }
252
+ builder.confirmCustomPaymentMethodCallback(this)
253
+ builder.build(activity)
254
+ } else {
255
+ PaymentSheet.FlowController
256
+ .Builder(
257
+ resultCallback = buildPaymentSheetResultCallback(),
258
+ paymentOptionResultCallback = buildPaymentOptionCallback(),
259
+ ).confirmCustomPaymentMethodCallback(this)
260
+ .build(activity)
261
+ }
262
+ }
263
+
264
+ private fun buildCreateConfirmationTokenCallback(): CreateIntentWithConfirmationTokenCallback {
265
+ return CreateIntentWithConfirmationTokenCallback { confirmationToken ->
266
+ val stripeSdkModule: StripeSdkModule? = context.getNativeModule(StripeSdkModule::class.java)
267
+ val params =
268
+ Arguments.createMap().apply {
269
+ putMap("confirmationToken", mapFromConfirmationToken(confirmationToken))
270
+ }
271
+
272
+ stripeSdkModule?.eventEmitter?.emitOnConfirmationTokenHandlerCallback(params)
273
+
274
+ val resultFromJavascript = paymentSheetConfirmationTokenCreationCallback.await()
275
+ // reset the completable
276
+ paymentSheetConfirmationTokenCreationCallback = CompletableDeferred<ReadableMap>()
277
+
278
+ return@CreateIntentWithConfirmationTokenCallback resultFromJavascript.getString("clientSecret")?.let {
279
+ CreateIntentResult.Success(clientSecret = it)
280
+ }
281
+ ?: run {
282
+ val errorMap = resultFromJavascript.getMap("error")
283
+ CreateIntentResult.Failure(
284
+ cause = Exception(errorMap?.getString("message")),
285
+ displayMessage = errorMap?.getString("localizedMessage"),
286
+ )
287
+ }
288
+ }
289
+ }
290
+
291
+ private fun buildIntentCreationCallback(): CreateIntentCallback {
292
+ return CreateIntentCallback { paymentMethod, shouldSavePaymentMethod ->
293
+ val stripeSdkModule: StripeSdkModule? = context.getNativeModule(StripeSdkModule::class.java)
294
+ val params =
295
+ Arguments.createMap().apply {
296
+ putMap("paymentMethod", mapFromPaymentMethod(paymentMethod))
297
+ putBoolean("shouldSavePaymentMethod", shouldSavePaymentMethod)
298
+ }
299
+
300
+ stripeSdkModule?.eventEmitter?.emitOnConfirmHandlerCallback(params)
301
+
302
+ val resultFromJavascript = paymentSheetIntentCreationCallback.await()
303
+ // reset the completable
304
+ paymentSheetIntentCreationCallback = CompletableDeferred<ReadableMap>()
305
+
306
+ return@CreateIntentCallback resultFromJavascript.getString("clientSecret")?.let {
307
+ CreateIntentResult.Success(clientSecret = it)
308
+ }
309
+ ?: run {
310
+ val errorMap = resultFromJavascript.getMap("error")
311
+ CreateIntentResult.Failure(
312
+ cause = Exception(errorMap?.getString("message")),
313
+ displayMessage = errorMap?.getString("localizedMessage"),
314
+ )
315
+ }
316
+ }
317
+ }
318
+
319
+ private fun buildPaymentSheetResultCallback(): PaymentSheetResultCallback =
320
+ PaymentSheetResultCallback { paymentResult ->
321
+ if (paymentSheetTimedOut) {
322
+ paymentSheetTimedOut = false
323
+ resolvePaymentResult(
324
+ createError(PaymentSheetErrorType.Timeout.toString(), "The payment has timed out"),
325
+ )
326
+ } else {
327
+ when (paymentResult) {
328
+ is PaymentSheetResult.Canceled -> {
329
+ resolvePaymentResult(
330
+ createError(
331
+ PaymentSheetErrorType.Canceled.toString(),
332
+ "The payment flow has been canceled",
333
+ ),
334
+ )
335
+ }
336
+
337
+ is PaymentSheetResult.Failed -> {
338
+ resolvePaymentResult(
339
+ createError(PaymentSheetErrorType.Failed.toString(), paymentResult.error),
340
+ )
341
+ }
342
+
343
+ is PaymentSheetResult.Completed -> {
344
+ resolvePaymentResult(Arguments.createMap())
345
+ }
346
+ }
347
+ }
348
+ }
349
+
350
+ private fun buildPaymentOptionCallback(): PaymentOptionResultCallback {
351
+ return PaymentOptionResultCallback { paymentOptionResult ->
352
+ paymentOptionResult.paymentOption?.let { paymentOption ->
353
+ // Convert drawable to bitmap asynchronously to avoid shared state issues
354
+ CoroutineScope(Dispatchers.Default).launch {
355
+ val imageString =
356
+ try {
357
+ convertDrawableToBase64(paymentOption.icon())
358
+ } catch (e: Exception) {
359
+ val result =
360
+ createError(
361
+ PaymentSheetErrorType.Failed.toString(),
362
+ "Failed to process payment option image: ${e.message}",
363
+ )
364
+ resolvePresentPromise(result)
365
+ return@launch
366
+ }
367
+
368
+ val option: WritableMap = Arguments.createMap()
369
+ option.putString("label", paymentOption.label)
370
+ option.putString("image", imageString)
371
+ val additionalFields: Map<String, Any> = mapOf("didCancel" to paymentOptionResult.didCancel)
372
+ val result = createResult("paymentOption", option, additionalFields)
373
+ resolvePresentPromise(result)
374
+ }
375
+ } ?: run {
376
+ val result =
377
+ if (paymentSheetTimedOut) {
378
+ paymentSheetTimedOut = false
379
+ createError(PaymentSheetErrorType.Timeout.toString(), "The payment has timed out")
380
+ } else {
381
+ createError(
382
+ PaymentSheetErrorType.Canceled.toString(),
383
+ "The payment option selection flow has been canceled",
384
+ )
385
+ }
386
+ resolvePresentPromise(result)
387
+ }
388
+ }
389
+ }
390
+
389
391
  override fun onPresent() {
390
392
  keepJsAwake = KeepJsAwakeTask(context).apply { start() }
391
- if (paymentSheet != null) {
393
+ if (lastConfigureWasCustomFlow == false) {
392
394
  if (!paymentIntentClientSecret.isNullOrEmpty()) {
393
395
  paymentSheet?.presentWithPaymentIntent(
394
396
  paymentIntentClientSecret!!,
@@ -402,7 +404,7 @@ class PaymentSheetManager(
402
404
  configuration = paymentSheetConfiguration,
403
405
  )
404
406
  }
405
- } else if (flowController != null) {
407
+ } else if (lastConfigureWasCustomFlow == true && flowController != null) {
406
408
  flowController?.presentPaymentOptions()
407
409
  } else {
408
410
  promise?.resolve(createMissingInitError())
@@ -258,7 +258,9 @@ class StripeSdkModule(
258
258
  promise: Promise,
259
259
  ) {
260
260
  if (paymentSheetManager != null) {
261
- paymentSheetManager?.configure(params, promise)
261
+ UiThreadUtil.runOnUiThread {
262
+ paymentSheetManager?.configure(params, promise)
263
+ }
262
264
  } else {
263
265
  paymentSheetManager =
264
266
  PaymentSheetManager(reactApplicationContext, params, promise).also {
@@ -2,6 +2,7 @@ package com.reactnativestripesdk.pushprovisioning
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.res.ColorStateList
5
+ import android.graphics.Bitmap
5
6
  import android.graphics.drawable.Drawable
6
7
  import android.graphics.drawable.RippleDrawable
7
8
  import android.view.MotionEvent
@@ -12,10 +13,9 @@ import androidx.core.graphics.toColorInt
12
13
  import androidx.core.net.toUri
13
14
  import com.facebook.common.executors.UiThreadImmediateExecutorService
14
15
  import com.facebook.common.references.CloseableReference
15
- import com.facebook.datasource.BaseDataSubscriber
16
16
  import com.facebook.datasource.DataSource
17
17
  import com.facebook.drawee.backends.pipeline.Fresco
18
- import com.facebook.imagepipeline.image.CloseableBitmap
18
+ import com.facebook.imagepipeline.datasource.BaseBitmapReferenceDataSubscriber
19
19
  import com.facebook.imagepipeline.image.CloseableImage
20
20
  import com.facebook.imagepipeline.request.ImageRequestBuilder
21
21
  import com.facebook.react.bridge.ReadableMap
@@ -36,6 +36,7 @@ class AddToWalletButtonView(
36
36
 
37
37
  private var loadedSource: String? = null
38
38
  private var currentDataSource: DataSource<CloseableReference<CloseableImage>>? = null
39
+ private var currentBitmapReference: CloseableReference<Bitmap>? = null
39
40
 
40
41
  init {
41
42
  scaleType = ScaleType.CENTER_CROP
@@ -118,20 +119,14 @@ class AddToWalletButtonView(
118
119
  currentDataSource = dataSource
119
120
 
120
121
  dataSource.subscribe(
121
- object : BaseDataSubscriber<CloseableReference<CloseableImage>>() {
122
- override fun onNewResultImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
123
- if (!dataSource.isFinished) return
124
- val imageRef = dataSource.result ?: return
125
-
126
- try {
127
- val image = imageRef.get()
128
- if (image is CloseableBitmap) {
129
- val drawable = image.underlyingBitmap.toDrawable(resources)
130
- setImageWithRipple(drawable)
131
- }
132
- } finally {
133
- CloseableReference.closeSafely(imageRef)
134
- }
122
+ object : BaseBitmapReferenceDataSubscriber() {
123
+ override fun onNewResultImpl(bitmapReference: CloseableReference<Bitmap>?) {
124
+ val image = bitmapReference?.get() ?: return
125
+
126
+ currentBitmapReference = bitmapReference.cloneOrNull()
127
+
128
+ val drawable = image.toDrawable(resources)
129
+ setImageWithRipple(drawable)
135
130
  }
136
131
 
137
132
  override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
@@ -165,6 +160,12 @@ class AddToWalletButtonView(
165
160
 
166
161
  private fun cancelCurrentRequest() {
167
162
  currentDataSource?.close()
163
+
164
+ currentBitmapReference?.let {
165
+ CloseableReference.closeSafely(it)
166
+ }
167
+
168
+ currentBitmapReference = null
168
169
  currentDataSource = null
169
170
  }
170
171
 
@@ -47,7 +47,6 @@ import com.stripe.android.link.LinkController.PaymentMethodPreview
47
47
  import com.stripe.android.link.PaymentMethodPreviewDetails
48
48
  import com.stripe.android.model.CardBrand
49
49
  import com.stripe.android.model.DateOfBirth
50
- import com.stripe.android.paymentsheet.PaymentSheet
51
50
  import kotlinx.coroutines.CompletableDeferred
52
51
  import kotlinx.coroutines.CoroutineScope
53
52
  import kotlinx.coroutines.Dispatchers
@@ -289,35 +288,8 @@ class OnrampSdkModule(
289
288
  }
290
289
  CoroutineScope(Dispatchers.IO).launch {
291
290
  val firstName = kycInfo.getString("firstName")
292
- if (firstName.isNullOrEmpty()) {
293
- promise.resolve(
294
- createError(
295
- ErrorType.Unknown.toString(),
296
- "Missing required field: firstName",
297
- ),
298
- )
299
- return@launch
300
- }
301
291
  val lastName = kycInfo.getString("lastName")
302
- if (lastName.isNullOrEmpty()) {
303
- promise.resolve(
304
- createError(
305
- ErrorType.Unknown.toString(),
306
- "Missing required field: lastName",
307
- ),
308
- )
309
- return@launch
310
- }
311
292
  val idNumber = kycInfo.getString("idNumber")
312
- if (idNumber.isNullOrEmpty()) {
313
- promise.resolve(
314
- createError(
315
- ErrorType.Unknown.toString(),
316
- "Missing required field: idNumber",
317
- ),
318
- )
319
- return@launch
320
- }
321
293
 
322
294
  val dateOfBirthMap = kycInfo.getMap("dateOfBirth")
323
295
  val dob =
@@ -333,17 +305,11 @@ class OnrampSdkModule(
333
305
  year = dateOfBirthMap.getInt("year"),
334
306
  )
335
307
  } else {
336
- promise.resolve(
337
- createError(
338
- ErrorType.Unknown.toString(),
339
- "Missing required field: dateOfBirth",
340
- ),
341
- )
342
- return@launch
308
+ null
343
309
  }
344
310
 
345
311
  val addressMap = kycInfo.getMap("address")
346
- val addressObj = mapToPaymentSheetAddress(addressMap) ?: PaymentSheet.Address()
312
+ val addressObj = mapToPaymentSheetAddress(addressMap)
347
313
 
348
314
  val kycInfoObj =
349
315
  KycInfo(