@stripe/stripe-react-native 0.65.0 → 0.65.1

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 (203) hide show
  1. package/README.md +2 -2
  2. package/android/detekt/detekt-baseline.xml +1 -66
  3. package/android/gradle.properties +3 -3
  4. package/android/src/main/java/com/reactnativestripesdk/AuBECSDebitFormView.kt +3 -1
  5. package/android/src/main/java/com/reactnativestripesdk/AuBECSDebitFormViewManager.kt +2 -1
  6. package/android/src/main/java/com/reactnativestripesdk/CardFieldView.kt +5 -1
  7. package/android/src/main/java/com/reactnativestripesdk/CurrencySelectorElementView.kt +26 -0
  8. package/android/src/main/java/com/reactnativestripesdk/CurrencySelectorElementViewManager.kt +42 -0
  9. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementView.kt +105 -49
  10. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +72 -12
  11. package/android/src/main/java/com/reactnativestripesdk/FinancialConnectionsSheetManager.kt +8 -4
  12. package/android/src/main/java/com/reactnativestripesdk/GooglePayButtonManager.kt +2 -1
  13. package/android/src/main/java/com/reactnativestripesdk/GooglePayButtonView.kt +26 -11
  14. package/android/src/main/java/com/reactnativestripesdk/GooglePayLauncherManager.kt +2 -1
  15. package/android/src/main/java/com/reactnativestripesdk/NavigationBarView.kt +14 -5
  16. package/android/src/main/java/com/reactnativestripesdk/PaymentElementConfig.kt +6 -2
  17. package/android/src/main/java/com/reactnativestripesdk/PaymentLauncherManager.kt +2 -1
  18. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt +2 -1
  19. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementConfig.kt +6 -2
  20. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementViewManager.kt +2 -1
  21. package/android/src/main/java/com/reactnativestripesdk/PaymentOptionDisplayDataMapper.kt +8 -4
  22. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +9 -3
  23. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetManager.kt +110 -29
  24. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +190 -24
  25. package/android/src/main/java/com/reactnativestripesdk/StripeSdkPackage.kt +1 -0
  26. package/android/src/main/java/com/reactnativestripesdk/addresssheet/AddressLauncherManager.kt +2 -1
  27. package/android/src/main/java/com/reactnativestripesdk/addresssheet/AddressSheetView.kt +3 -1
  28. package/android/src/main/java/com/reactnativestripesdk/customersheet/CustomerSheetManager.kt +6 -2
  29. package/android/src/main/java/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt +3 -1
  30. package/android/src/main/java/com/reactnativestripesdk/customersheet/ReactNativeCustomerSessionProvider.kt +2 -1
  31. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt +2 -1
  32. package/android/src/main/java/com/reactnativestripesdk/utils/CheckoutAddressUpdate.kt +45 -0
  33. package/android/src/main/java/com/reactnativestripesdk/utils/CheckoutMappers.kt +101 -0
  34. package/android/src/main/java/com/reactnativestripesdk/utils/Errors.kt +4 -2
  35. package/android/src/main/java/com/reactnativestripesdk/utils/Extensions.kt +5 -1
  36. package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +29 -13
  37. package/android/src/main/java/com/reactnativestripesdk/utils/PostalCodeUtilities.kt +2 -1
  38. package/android/src/oldarch/java/com/facebook/react/viewmanagers/EmbeddedPaymentElementViewManagerDelegate.java +6 -0
  39. package/android/src/oldarch/java/com/facebook/react/viewmanagers/EmbeddedPaymentElementViewManagerInterface.java +2 -0
  40. package/android/src/oldarch/java/com/facebook/react/viewmanagers/StripeCurrencySelectorElementManagerDelegate.java +35 -0
  41. package/android/src/oldarch/java/com/facebook/react/viewmanagers/StripeCurrencySelectorElementManagerInterface.java +18 -0
  42. package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +8 -0
  43. package/android/src/test/java/com/reactnativestripesdk/mappers/MappersTest.kt +60 -0
  44. package/android/src/test/java/com/reactnativestripesdk/mappers/OnrampMappersTest.kt +38 -0
  45. package/android/src/test/java/com/reactnativestripesdk/mappers/PaymentOptionDisplayDataMapperTest.kt +9 -4
  46. package/android/src/test/java/com/reactnativestripesdk/utils/CheckoutMappersTest.kt +89 -0
  47. package/android/src/test/java/com/reactnativestripesdk/utils/CheckoutTestFixtures.java +49 -0
  48. package/ios/ConnectAccountOnboarding/ConnectAccountOnboardingViewController.swift +6 -8
  49. package/ios/CurrencySelectorElementManager.m +9 -0
  50. package/ios/CurrencySelectorElementManager.swift +12 -0
  51. package/ios/CurrencySelectorElementView.swift +24 -0
  52. package/ios/Mappers+Checkout.swift +193 -0
  53. package/ios/NewArch/EmbeddedPaymentElementViewComponentView.mm +16 -2
  54. package/ios/NewArch/StripeCurrencySelectorElementComponentView.h +10 -0
  55. package/ios/NewArch/StripeCurrencySelectorElementComponentView.mm +75 -0
  56. package/ios/StripeSdk.mm +123 -0
  57. package/ios/StripeSdkImpl+Checkout.swift +308 -0
  58. package/ios/StripeSdkImpl+Embedded.swift +108 -27
  59. package/ios/StripeSdkImpl+PaymentSheet.swift +18 -2
  60. package/ios/StripeSdkImpl.swift +3 -1
  61. package/jest/mock.js +13 -1
  62. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  63. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  64. package/lib/commonjs/components/AddressSheet.js +1 -1
  65. package/lib/commonjs/components/AddressSheet.js.map +1 -1
  66. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  67. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  68. package/lib/commonjs/components/CardField.js +1 -1
  69. package/lib/commonjs/components/CardField.js.map +1 -1
  70. package/lib/commonjs/components/CardForm.js +1 -1
  71. package/lib/commonjs/components/CardForm.js.map +1 -1
  72. package/lib/commonjs/components/CurrencySelectorElement.js +2 -0
  73. package/lib/commonjs/components/CurrencySelectorElement.js.map +1 -0
  74. package/lib/commonjs/components/PaymentMethodMessagingElement.js +1 -1
  75. package/lib/commonjs/components/PaymentMethodMessagingElement.js.map +1 -1
  76. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  77. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  78. package/lib/commonjs/components/StripeContainer.js +1 -1
  79. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  80. package/lib/commonjs/connect/Components.js +1 -1
  81. package/lib/commonjs/connect/Components.js.map +1 -1
  82. package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
  83. package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
  84. package/lib/commonjs/connect/EmbeddedComponent.js +1 -1
  85. package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
  86. package/lib/commonjs/connect/ModalCloseButton.js +1 -1
  87. package/lib/commonjs/connect/ModalCloseButton.js.map +1 -1
  88. package/lib/commonjs/connect/NavigationBar.js +1 -1
  89. package/lib/commonjs/connect/NavigationBar.js.map +1 -1
  90. package/lib/commonjs/events.js.map +1 -1
  91. package/lib/commonjs/functions.js +1 -1
  92. package/lib/commonjs/functions.js.map +1 -1
  93. package/lib/commonjs/helpers.js +1 -1
  94. package/lib/commonjs/hooks/useCheckout.js +1 -1
  95. package/lib/commonjs/hooks/useCheckout.js.map +1 -1
  96. package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
  97. package/lib/commonjs/specs/NativeAddressSheet.js +1 -1
  98. package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
  99. package/lib/commonjs/specs/NativeAuBECSDebitForm.js +1 -1
  100. package/lib/commonjs/specs/NativeCardField.js +1 -1
  101. package/lib/commonjs/specs/NativeCardField.js.map +1 -1
  102. package/lib/commonjs/specs/NativeCardForm.js +1 -1
  103. package/lib/commonjs/specs/NativeCardForm.js.map +1 -1
  104. package/lib/commonjs/specs/NativeConnectAccountOnboardingView.js +1 -1
  105. package/lib/commonjs/specs/NativeCurrencySelectorElement.js +2 -0
  106. package/lib/commonjs/specs/NativeCurrencySelectorElement.js.map +1 -0
  107. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
  108. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  109. package/lib/commonjs/specs/NativeGooglePayButton.js +1 -1
  110. package/lib/commonjs/specs/NativeNavigationBar.js +1 -1
  111. package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js +1 -1
  112. package/lib/commonjs/specs/NativeStripeContainer.js +1 -1
  113. package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
  114. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  115. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  116. package/lib/module/components/AddToWalletButton.js +1 -1
  117. package/lib/module/components/AddToWalletButton.js.map +1 -1
  118. package/lib/module/components/AddressSheet.js +1 -1
  119. package/lib/module/components/AddressSheet.js.map +1 -1
  120. package/lib/module/components/AuBECSDebitForm.js +1 -1
  121. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  122. package/lib/module/components/CardField.js +1 -1
  123. package/lib/module/components/CardField.js.map +1 -1
  124. package/lib/module/components/CardForm.js +1 -1
  125. package/lib/module/components/CardForm.js.map +1 -1
  126. package/lib/module/components/CurrencySelectorElement.js +2 -0
  127. package/lib/module/components/CurrencySelectorElement.js.map +1 -0
  128. package/lib/module/components/PaymentMethodMessagingElement.js +1 -1
  129. package/lib/module/components/PaymentMethodMessagingElement.js.map +1 -1
  130. package/lib/module/components/PlatformPayButton.js +1 -1
  131. package/lib/module/components/PlatformPayButton.js.map +1 -1
  132. package/lib/module/components/StripeContainer.js +1 -1
  133. package/lib/module/components/StripeContainer.js.map +1 -1
  134. package/lib/module/connect/Components.js +1 -1
  135. package/lib/module/connect/Components.js.map +1 -1
  136. package/lib/module/connect/ConnectComponentsProvider.js +1 -1
  137. package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
  138. package/lib/module/connect/EmbeddedComponent.js +1 -1
  139. package/lib/module/connect/EmbeddedComponent.js.map +1 -1
  140. package/lib/module/connect/ModalCloseButton.js +1 -1
  141. package/lib/module/connect/ModalCloseButton.js.map +1 -1
  142. package/lib/module/connect/NavigationBar.js +1 -1
  143. package/lib/module/connect/NavigationBar.js.map +1 -1
  144. package/lib/module/events.js.map +1 -1
  145. package/lib/module/functions.js +1 -1
  146. package/lib/module/functions.js.map +1 -1
  147. package/lib/module/helpers.js +1 -1
  148. package/lib/module/hooks/useCheckout.js +1 -1
  149. package/lib/module/hooks/useCheckout.js.map +1 -1
  150. package/lib/module/specs/NativeAddToWalletButton.js +1 -1
  151. package/lib/module/specs/NativeAddressSheet.js +1 -1
  152. package/lib/module/specs/NativeApplePayButton.js +1 -1
  153. package/lib/module/specs/NativeAuBECSDebitForm.js +1 -1
  154. package/lib/module/specs/NativeCardField.js +1 -1
  155. package/lib/module/specs/NativeCardField.js.map +1 -1
  156. package/lib/module/specs/NativeCardForm.js +1 -1
  157. package/lib/module/specs/NativeCardForm.js.map +1 -1
  158. package/lib/module/specs/NativeConnectAccountOnboardingView.js +1 -1
  159. package/lib/module/specs/NativeCurrencySelectorElement.js +2 -0
  160. package/lib/module/specs/NativeCurrencySelectorElement.js.map +1 -0
  161. package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
  162. package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  163. package/lib/module/specs/NativeGooglePayButton.js +1 -1
  164. package/lib/module/specs/NativeNavigationBar.js +1 -1
  165. package/lib/module/specs/NativePaymentMethodMessagingElement.js +1 -1
  166. package/lib/module/specs/NativeStripeContainer.js +1 -1
  167. package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
  168. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  169. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  170. package/lib/typescript/src/components/CurrencySelectorElement.d.ts +62 -0
  171. package/lib/typescript/src/components/CurrencySelectorElement.d.ts.map +1 -0
  172. package/lib/typescript/src/events.d.ts +11 -0
  173. package/lib/typescript/src/events.d.ts.map +1 -1
  174. package/lib/typescript/src/functions.d.ts.map +1 -1
  175. package/lib/typescript/src/hooks/useCheckout.d.ts.map +1 -1
  176. package/lib/typescript/src/specs/NativeCurrencySelectorElement.d.ts +25 -0
  177. package/lib/typescript/src/specs/NativeCurrencySelectorElement.d.ts.map +1 -0
  178. package/lib/typescript/src/specs/NativeEmbeddedPaymentElement.d.ts +5 -1
  179. package/lib/typescript/src/specs/NativeEmbeddedPaymentElement.d.ts.map +1 -1
  180. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +3 -1
  181. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
  182. package/lib/typescript/src/types/Checkout.d.ts +2 -2
  183. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +36 -1
  184. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
  185. package/package.json +14 -3
  186. package/src/components/CurrencySelectorElement.tsx +95 -0
  187. package/src/events.ts +11 -0
  188. package/src/functions.ts +30 -1
  189. package/src/hooks/useCheckout.tsx +114 -75
  190. package/src/specs/NativeCurrencySelectorElement.ts +35 -0
  191. package/src/specs/NativeEmbeddedPaymentElement.ts +12 -2
  192. package/src/specs/NativeStripeSdkModule.ts +8 -1
  193. package/src/types/Checkout.ts +2 -2
  194. package/src/types/EmbeddedPaymentElement.tsx +117 -26
  195. package/android/.idea/AndroidProjectSystem.xml +0 -6
  196. package/android/.idea/caches/deviceStreaming.xml +0 -835
  197. package/android/.idea/compiler.xml +0 -6
  198. package/android/.idea/gradle.xml +0 -17
  199. package/android/.idea/migrations.xml +0 -10
  200. package/android/.idea/misc.xml +0 -10
  201. package/android/.idea/runConfigurations.xml +0 -17
  202. package/android/.idea/vcs.xml +0 -6
  203. package/android/local.properties +0 -8
@@ -36,6 +36,7 @@ import com.reactnativestripesdk.utils.GooglePayErrorType
36
36
  import com.reactnativestripesdk.utils.RetrievePaymentIntentErrorType
37
37
  import com.reactnativestripesdk.utils.RetrieveSetupIntentErrorType
38
38
  import com.reactnativestripesdk.utils.StripeUIManager
39
+ import com.reactnativestripesdk.utils.buildCheckoutAddressUpdate
39
40
  import com.reactnativestripesdk.utils.createCanAddCardResult
40
41
  import com.reactnativestripesdk.utils.createError
41
42
  import com.reactnativestripesdk.utils.createMissingActivityError
@@ -45,6 +46,7 @@ import com.reactnativestripesdk.utils.getBooleanOr
45
46
  import com.reactnativestripesdk.utils.getIntOrNull
46
47
  import com.reactnativestripesdk.utils.getLongOrNull
47
48
  import com.reactnativestripesdk.utils.getValOr
49
+ import com.reactnativestripesdk.utils.mapFromCheckoutState
48
50
  import com.reactnativestripesdk.utils.mapFromPaymentIntentResult
49
51
  import com.reactnativestripesdk.utils.mapFromPaymentMethod
50
52
  import com.reactnativestripesdk.utils.mapFromSetupIntentResult
@@ -55,11 +57,13 @@ import com.reactnativestripesdk.utils.mapToPaymentMethodType
55
57
  import com.reactnativestripesdk.utils.mapToReturnURL
56
58
  import com.reactnativestripesdk.utils.mapToShippingDetails
57
59
  import com.reactnativestripesdk.utils.mapToUICustomization
60
+ import com.reactnativestripesdk.utils.toCheckoutAddress
58
61
  import com.stripe.android.ApiResultCallback
59
62
  import com.stripe.android.GooglePayJsonFactory
60
63
  import com.stripe.android.PaymentAuthConfig
61
64
  import com.stripe.android.PaymentConfiguration
62
65
  import com.stripe.android.Stripe
66
+ import com.stripe.android.checkout.Checkout
63
67
  import com.stripe.android.core.ApiVersion
64
68
  import com.stripe.android.core.AppInfo
65
69
  import com.stripe.android.core.reactnative.ReactNativeAnalytics
@@ -75,6 +79,7 @@ import com.stripe.android.model.PaymentMethod
75
79
  import com.stripe.android.model.RadarSession
76
80
  import com.stripe.android.model.SetupIntent
77
81
  import com.stripe.android.model.Token
82
+ import com.stripe.android.paymentelement.CheckoutSessionPreview
78
83
  import com.stripe.android.payments.bankaccount.CollectBankAccountConfiguration
79
84
  import com.stripe.android.paymentsheet.PaymentSheet
80
85
  import kotlinx.coroutines.CompletableDeferred
@@ -82,9 +87,10 @@ import kotlinx.coroutines.CoroutineScope
82
87
  import kotlinx.coroutines.Dispatchers
83
88
  import kotlinx.coroutines.launch
84
89
  import org.json.JSONObject
90
+ import java.util.UUID
85
91
 
86
92
  @ReactModule(name = StripeSdkModule.NAME)
87
- @OptIn(ReactNativeSdkInternal::class)
93
+ @OptIn(ReactNativeSdkInternal::class, CheckoutSessionPreview::class)
88
94
  class StripeSdkModule(
89
95
  reactContext: ReactApplicationContext,
90
96
  ) : NativeStripeSdkModuleSpec(reactContext) {
@@ -106,6 +112,7 @@ class StripeSdkModule(
106
112
  private var financialConnectionsSheetManager: FinancialConnectionsSheetManager? = null
107
113
  private var googlePayLauncherManager: GooglePayLauncherManager? = null
108
114
  private var googlePayPaymentMethodLauncherManager: GooglePayPaymentMethodLauncherManager? = null
115
+ internal val checkoutInstances = mutableMapOf<String, Checkout>()
109
116
 
110
117
  private var customerSheetManager: CustomerSheetManager? = null
111
118
 
@@ -158,6 +165,7 @@ class StripeSdkModule(
158
165
 
159
166
  stripeUIManagers.forEach { it.destroy() }
160
167
  stripeUIManagers.clear()
168
+ checkoutInstances.clear()
161
169
  }
162
170
 
163
171
  private fun registerStripeUIManager(uiManager: StripeUIManager) {
@@ -278,7 +286,7 @@ class StripeSdkModule(
278
286
  }
279
287
  } else {
280
288
  paymentSheetManager =
281
- PaymentSheetManager(reactApplicationContext, params, promise).also {
289
+ PaymentSheetManager(reactApplicationContext, params, promise, checkoutInstances).also {
282
290
  registerStripeUIManager(it)
283
291
  }
284
292
  }
@@ -1382,13 +1390,28 @@ class StripeSdkModule(
1382
1390
  )
1383
1391
  }
1384
1392
 
1393
+ // Android owns EmbeddedPaymentElement through its native view. Configuration,
1394
+ // update, confirm, and clear commands are handled by EmbeddedPaymentElementViewManager
1395
+ // so they can target the mounted Compose view instance. iOS stores its
1396
+ // EmbeddedPaymentElement on StripeSdkImpl instead, so the shared TurboModule
1397
+ // spec includes these module methods for the iOS implementation.
1398
+
1385
1399
  @ReactMethod
1386
1400
  override fun createEmbeddedPaymentElement(
1387
1401
  intentConfig: ReadableMap,
1388
1402
  configuration: ReadableMap,
1389
1403
  promise: Promise,
1390
1404
  ) {
1391
- // TODO:
1405
+ promise.resolve(null)
1406
+ }
1407
+
1408
+ @ReactMethod
1409
+ override fun createEmbeddedPaymentElementWithCheckout(
1410
+ sessionKey: String,
1411
+ configuration: ReadableMap,
1412
+ promise: Promise,
1413
+ ) {
1414
+ promise.resolve(null)
1392
1415
  }
1393
1416
 
1394
1417
  @ReactMethod
@@ -1396,7 +1419,7 @@ class StripeSdkModule(
1396
1419
  viewTag: Double,
1397
1420
  promise: Promise,
1398
1421
  ) {
1399
- // noop, iOS only
1422
+ // No-op on Android. JS dispatches confirm through the view command instead.
1400
1423
  }
1401
1424
 
1402
1425
  @ReactMethod
@@ -1407,12 +1430,21 @@ class StripeSdkModule(
1407
1430
  // TODO:
1408
1431
  }
1409
1432
 
1433
+ @ReactMethod
1434
+ override fun updateEmbeddedPaymentElementWithCheckout(
1435
+ sessionKey: String,
1436
+ promise: Promise,
1437
+ ) {
1438
+ // No-op on Android. JS dispatches Checkout updates through the view command instead.
1439
+ promise.resolve(null)
1440
+ }
1441
+
1410
1442
  @ReactMethod
1411
1443
  override fun clearEmbeddedPaymentOption(
1412
1444
  viewTag: Double,
1413
1445
  promise: Promise,
1414
1446
  ) {
1415
- // noop, iOS only
1447
+ // No-op on Android. JS dispatches clear through the view command instead.
1416
1448
  }
1417
1449
 
1418
1450
  @ReactMethod
@@ -1563,7 +1595,7 @@ class StripeSdkModule(
1563
1595
  // Schedule cleanup
1564
1596
  android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
1565
1597
  file.delete()
1566
- }, 3000)
1598
+ }, FILE_CLEANUP_DELAY_MS)
1567
1599
 
1568
1600
  promise.resolve(
1569
1601
  Arguments.createMap().apply {
@@ -1695,10 +1727,39 @@ class StripeSdkModule(
1695
1727
  // noop, iOS only.
1696
1728
  }
1697
1729
 
1698
- // Checkout Session stubs — real implementations in a future PR.
1730
+ override fun initCheckoutSession(
1731
+ clientSecret: String,
1732
+ configuration: ReadableMap,
1733
+ promise: Promise,
1734
+ ) {
1735
+ val checkoutConfiguration = buildCheckoutConfiguration(configuration)
1736
+
1737
+ CoroutineScope(Dispatchers.Main).launch {
1738
+ Checkout.configure(
1739
+ context = reactApplicationContext,
1740
+ checkoutSessionClientSecret = clientSecret,
1741
+ configuration = checkoutConfiguration,
1742
+ ).fold(
1743
+ onSuccess = { checkout ->
1744
+ val sessionKey = UUID.randomUUID().toString()
1745
+ checkoutInstances[sessionKey] = checkout
1699
1746
 
1700
- override fun initCheckoutSession(clientSecret: String, configuration: ReadableMap, promise: Promise) {
1701
- promise.reject("NotImplemented", "Checkout is not yet implemented on Android")
1747
+ promise.resolve(
1748
+ Arguments.createMap().apply {
1749
+ putString("sessionKey", sessionKey)
1750
+ putMap("state", mapFromCheckoutState(checkout))
1751
+ },
1752
+ )
1753
+ },
1754
+ onFailure = { error ->
1755
+ promise.reject(
1756
+ ErrorType.Failed.toString(),
1757
+ error.message ?: "Failed to initialize checkout session.",
1758
+ error,
1759
+ )
1760
+ },
1761
+ )
1762
+ }
1702
1763
  }
1703
1764
 
1704
1765
  override fun checkoutUpdateShippingAddress(
@@ -1708,7 +1769,21 @@ class StripeSdkModule(
1708
1769
  phone: String?,
1709
1770
  promise: Promise,
1710
1771
  ) {
1711
- promise.reject("NotImplemented", "Checkout is not yet implemented on Android")
1772
+ val addressUpdate = buildCheckoutAddressUpdate(name, phone, address) ?: run {
1773
+ promise.reject(ErrorType.Failed.toString(), "A shipping address country is required.")
1774
+ return
1775
+ }
1776
+
1777
+ performCheckoutMutation(
1778
+ sessionKey = sessionKey,
1779
+ promise = promise,
1780
+ ) { checkout ->
1781
+ checkout.updateShippingAddress(
1782
+ name = addressUpdate.name,
1783
+ phoneNumber = addressUpdate.phone,
1784
+ address = addressUpdate.toCheckoutAddress(),
1785
+ )
1786
+ }
1712
1787
  }
1713
1788
 
1714
1789
  override fun checkoutUpdateBillingAddress(
@@ -1718,15 +1793,40 @@ class StripeSdkModule(
1718
1793
  phone: String?,
1719
1794
  promise: Promise,
1720
1795
  ) {
1721
- promise.reject("NotImplemented", "Checkout is not yet implemented on Android")
1796
+ val addressUpdate = buildCheckoutAddressUpdate(name, phone, address) ?: run {
1797
+ promise.reject(ErrorType.Failed.toString(), "A billing address country is required.")
1798
+ return
1799
+ }
1800
+
1801
+ performCheckoutMutation(
1802
+ sessionKey = sessionKey,
1803
+ promise = promise,
1804
+ ) { checkout ->
1805
+ checkout.updateBillingAddress(
1806
+ name = addressUpdate.name,
1807
+ phoneNumber = addressUpdate.phone,
1808
+ address = addressUpdate.toCheckoutAddress(),
1809
+ )
1810
+ }
1722
1811
  }
1723
1812
 
1724
- override fun checkoutApplyPromotionCode(sessionKey: String, code: String, promise: Promise) {
1725
- promise.reject("NotImplemented", "Checkout is not yet implemented on Android")
1813
+ override fun checkoutApplyPromotionCode(
1814
+ sessionKey: String,
1815
+ code: String,
1816
+ promise: Promise,
1817
+ ) {
1818
+ performCheckoutMutation(sessionKey, promise) { checkout ->
1819
+ checkout.applyPromotionCode(code)
1820
+ }
1726
1821
  }
1727
1822
 
1728
- override fun checkoutRemovePromotionCode(sessionKey: String, promise: Promise) {
1729
- promise.reject("NotImplemented", "Checkout is not yet implemented on Android")
1823
+ override fun checkoutRemovePromotionCode(
1824
+ sessionKey: String,
1825
+ promise: Promise,
1826
+ ) {
1827
+ performCheckoutMutation(sessionKey, promise) { checkout ->
1828
+ checkout.removePromotionCode()
1829
+ }
1730
1830
  }
1731
1831
 
1732
1832
  override fun checkoutUpdateLineItemQuantity(
@@ -1735,19 +1835,79 @@ class StripeSdkModule(
1735
1835
  quantity: Double,
1736
1836
  promise: Promise,
1737
1837
  ) {
1738
- promise.reject("NotImplemented", "Checkout is not yet implemented on Android")
1838
+ if (!quantity.isFinite() || quantity % 1.0 != 0.0) {
1839
+ promise.reject(ErrorType.Failed.toString(), "Line item quantity must be an integer.")
1840
+ return
1841
+ }
1842
+
1843
+ performCheckoutMutation(sessionKey, promise) { checkout ->
1844
+ checkout.updateLineItemQuantity(lineItemId = lineItemId, quantity = quantity.toInt())
1845
+ }
1846
+ }
1847
+
1848
+ override fun checkoutSelectShippingOption(
1849
+ sessionKey: String,
1850
+ id: String,
1851
+ promise: Promise,
1852
+ ) {
1853
+ performCheckoutMutation(sessionKey, promise) { checkout ->
1854
+ checkout.selectShippingOption(id)
1855
+ }
1856
+ }
1857
+
1858
+ override fun checkoutUpdateTaxId(
1859
+ sessionKey: String,
1860
+ type: String,
1861
+ value: String,
1862
+ promise: Promise,
1863
+ ) {
1864
+ performCheckoutMutation(sessionKey, promise) { checkout ->
1865
+ checkout.updateTaxId(type = type, value = value)
1866
+ }
1739
1867
  }
1740
1868
 
1741
- override fun checkoutSelectShippingOption(sessionKey: String, id: String, promise: Promise) {
1742
- promise.reject("NotImplemented", "Checkout is not yet implemented on Android")
1869
+ override fun checkoutRefresh(
1870
+ sessionKey: String,
1871
+ promise: Promise,
1872
+ ) {
1873
+ performCheckoutMutation(sessionKey, promise) { checkout ->
1874
+ checkout.refresh()
1875
+ }
1743
1876
  }
1744
1877
 
1745
- override fun checkoutUpdateTaxId(sessionKey: String, type: String, value: String, promise: Promise) {
1746
- promise.reject("NotImplemented", "Checkout is not yet implemented on Android")
1878
+ private fun buildCheckoutConfiguration(configuration: ReadableMap): Checkout.Configuration {
1879
+ val checkoutConfiguration = Checkout.Configuration()
1880
+ val adaptivePricing = configuration.getMap("adaptivePricing")
1881
+ if (adaptivePricing?.hasKey("allowed") == true) {
1882
+ checkoutConfiguration.adaptivePricingAllowed(adaptivePricing.getBooleanOr("allowed", false))
1883
+ }
1884
+ return checkoutConfiguration
1747
1885
  }
1748
1886
 
1749
- override fun checkoutRefresh(sessionKey: String, promise: Promise) {
1750
- promise.reject("NotImplemented", "Checkout is not yet implemented on Android")
1887
+ private fun performCheckoutMutation(
1888
+ sessionKey: String,
1889
+ promise: Promise,
1890
+ operation: suspend (Checkout) -> Result<Unit>,
1891
+ ) {
1892
+ val checkout = checkoutInstances[sessionKey] ?: run {
1893
+ promise.reject(ErrorType.Failed.toString(), "Checkout session not found.")
1894
+ return
1895
+ }
1896
+
1897
+ CoroutineScope(Dispatchers.Main).launch {
1898
+ operation(checkout).fold(
1899
+ onSuccess = {
1900
+ promise.resolve(mapFromCheckoutState(checkout))
1901
+ },
1902
+ onFailure = { error ->
1903
+ promise.reject(
1904
+ ErrorType.Failed.toString(),
1905
+ error.message ?: "Checkout operation failed.",
1906
+ error,
1907
+ )
1908
+ },
1909
+ )
1910
+ }
1751
1911
  }
1752
1912
 
1753
1913
  /**
@@ -1818,13 +1978,17 @@ class StripeSdkModule(
1818
1978
  */
1819
1979
  private fun preventActivityRecreation() {
1820
1980
  isRecreatingReactActivity = false
1821
- reactApplicationContext.currentActivity?.application?.unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks)
1981
+ reactApplicationContext.currentActivity?.application?.unregisterActivityLifecycleCallbacks(
1982
+ activityLifecycleCallbacks
1983
+ )
1822
1984
  reactApplicationContext.currentActivity?.application?.registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
1823
1985
  }
1824
1986
 
1825
1987
  private fun setupComposeCompatView() {
1826
1988
  UiThreadUtil.runOnUiThread {
1827
- composeCompatView = composeCompatView ?: StripeAbstractComposeView.CompatView(context = reactApplicationContext).also {
1989
+ composeCompatView = composeCompatView ?: StripeAbstractComposeView.CompatView(
1990
+ context = reactApplicationContext
1991
+ ).also {
1828
1992
  reactApplicationContext.currentActivity?.findViewById<ViewGroup>(android.R.id.content)?.addView(
1829
1993
  it,
1830
1994
  )
@@ -1842,6 +2006,8 @@ class StripeSdkModule(
1842
2006
  // Timeout for auth webview fallback (if JavaScript doesn't call authWebViewDeepLinkHandled)
1843
2007
  private const val AUTH_WEBVIEW_FALLBACK_TIMEOUT_MS = 60_000L
1844
2008
 
2009
+ private const val FILE_CLEANUP_DELAY_MS = 3000L
2010
+
1845
2011
  // SDK-managed storage for pending stripe-connect:// URLs
1846
2012
  // This is static because deep links can arrive before ReactContext is available
1847
2013
  private val pendingConnectUrls = mutableListOf<String>()
@@ -54,6 +54,7 @@ class StripeSdkPackage : BaseReactPackage() {
54
54
  CardFieldViewManager(),
55
55
  AuBECSDebitFormViewManager(),
56
56
  StripeContainerManager(),
57
+ CurrencySelectorElementViewManager(),
57
58
  CardFormViewManager(),
58
59
  GooglePayButtonManager(),
59
60
  AddToWalletButtonManager(),
@@ -55,7 +55,8 @@ class AddressLauncherManager(
55
55
  callback.invoke(
56
56
  createError(
57
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.",
58
+ "No publishable key set. Stripe has not been initialized. Initialize Stripe in your app with the " +
59
+ "StripeProvider component or the initStripe method.",
59
60
  ),
60
61
  null,
61
62
  )
@@ -162,7 +162,9 @@ class AddressSheetView(
162
162
  else -> AddressLauncher.AdditionalFieldsConfiguration.FieldConfiguration.HIDDEN
163
163
  }
164
164
 
165
- internal fun buildAdditionalFieldsConfiguration(params: ReadableMap): AddressLauncher.AdditionalFieldsConfiguration {
165
+ internal fun buildAdditionalFieldsConfiguration(
166
+ params: ReadableMap
167
+ ): AddressLauncher.AdditionalFieldsConfiguration {
166
168
  val phoneConfiguration = getFieldConfiguration(params.getString("phoneNumber"))
167
169
 
168
170
  return AddressLauncher.AdditionalFieldsConfiguration(
@@ -326,7 +326,9 @@ class CustomerSheetManager(
326
326
  internal fun createDefaultBillingDetails(map: ReadableMap): PaymentSheet.BillingDetails =
327
327
  buildBillingDetails(map) ?: PaymentSheet.BillingDetails()
328
328
 
329
- internal fun createBillingDetailsCollectionConfiguration(map: ReadableMap): PaymentSheet.BillingDetailsCollectionConfiguration =
329
+ internal fun createBillingDetailsCollectionConfiguration(
330
+ map: ReadableMap
331
+ ): PaymentSheet.BillingDetailsCollectionConfiguration =
330
332
  buildBillingDetailsCollectionConfiguration(map)
331
333
 
332
334
  internal fun createCustomerAdapter(
@@ -405,7 +407,9 @@ class CustomerSheetManager(
405
407
  return paymentOptionResult
406
408
  }
407
409
 
408
- internal fun createIntentConfiguration(intentConfigurationBundle: ReadableMap?): CustomerSheet.IntentConfiguration? =
410
+ internal fun createIntentConfiguration(
411
+ intentConfigurationBundle: ReadableMap?
412
+ ): CustomerSheet.IntentConfiguration? =
409
413
  intentConfigurationBundle?.let { bundle ->
410
414
  val onBehalfOf = bundle.getString("onBehalfOf")
411
415
  CustomerSheet.IntentConfiguration
@@ -67,7 +67,9 @@ class ReactNativeCustomerAdapter(
67
67
  return adapter.detachPaymentMethod(paymentMethodId)
68
68
  }
69
69
 
70
- override suspend fun setSelectedPaymentOption(paymentOption: CustomerAdapter.PaymentOption?): CustomerAdapter.Result<Unit> {
70
+ override suspend fun setSelectedPaymentOption(
71
+ paymentOption: CustomerAdapter.PaymentOption?
72
+ ): CustomerAdapter.Result<Unit> {
71
73
  if (overridesSetSelectedPaymentOption) {
72
74
  CompletableDeferred<Unit>().also {
73
75
  setSelectedPaymentOptionCallback = it
@@ -14,7 +14,8 @@ class ReactNativeCustomerSessionProvider(
14
14
  internal var providesCustomerSessionClientSecretCallback:
15
15
  CompletableDeferred<CustomerSheet.CustomerSessionClientSecret>? = null
16
16
 
17
- override suspend fun intentConfiguration(): Result<CustomerSheet.IntentConfiguration> = Result.success(intentConfiguration)
17
+ override suspend fun intentConfiguration(): Result<CustomerSheet.IntentConfiguration> =
18
+ Result.success(intentConfiguration)
18
19
 
19
20
  override suspend fun provideSetupIntentClientSecret(customerId: String): Result<String> {
20
21
  CompletableDeferred<String>().also {
@@ -30,7 +30,8 @@ class AddToWalletButtonManager :
30
30
  view.onAfterUpdateTransaction()
31
31
  }
32
32
 
33
- override fun createViewInstance(reactContext: ThemedReactContext): AddToWalletButtonView = AddToWalletButtonView(reactContext)
33
+ override fun createViewInstance(reactContext: ThemedReactContext): AddToWalletButtonView =
34
+ AddToWalletButtonView(reactContext)
34
35
 
35
36
  override fun getExportedCustomDirectEventTypeConstants() =
36
37
  mutableMapOf(
@@ -0,0 +1,45 @@
1
+ package com.reactnativestripesdk.utils
2
+
3
+ import com.facebook.react.bridge.ReadableMap
4
+ import com.stripe.android.paymentelement.CheckoutSessionPreview
5
+ import com.stripe.android.checkout.Address as CheckoutAddress
6
+
7
+ @OptIn(CheckoutSessionPreview::class)
8
+ internal data class CheckoutAddressUpdate(
9
+ val name: String? = null,
10
+ val phone: String? = null,
11
+ val country: String,
12
+ val line1: String? = null,
13
+ val line2: String? = null,
14
+ val city: String? = null,
15
+ val state: String? = null,
16
+ val postalCode: String? = null,
17
+ )
18
+
19
+ internal fun buildCheckoutAddressUpdate(
20
+ name: String?,
21
+ phone: String?,
22
+ address: ReadableMap,
23
+ ): CheckoutAddressUpdate? {
24
+ val country = address.getString("country")?.trim()?.takeIf { it.isNotEmpty() } ?: return null
25
+ return CheckoutAddressUpdate(
26
+ name = name?.trim(),
27
+ phone = phone?.trim(),
28
+ country = country,
29
+ line1 = address.getString("line1")?.trim(),
30
+ line2 = address.getString("line2")?.trim(),
31
+ city = address.getString("city")?.trim(),
32
+ state = address.getString("state")?.trim(),
33
+ postalCode = address.getString("postalCode")?.trim(),
34
+ )
35
+ }
36
+
37
+ @OptIn(CheckoutSessionPreview::class)
38
+ internal fun CheckoutAddressUpdate.toCheckoutAddress(): CheckoutAddress =
39
+ CheckoutAddress()
40
+ .country(country)
41
+ .line1(line1)
42
+ .line2(line2)
43
+ .city(city)
44
+ .state(state)
45
+ .postalCode(postalCode)
@@ -0,0 +1,101 @@
1
+ package com.reactnativestripesdk.utils
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableArray
5
+ import com.facebook.react.bridge.WritableMap
6
+ import com.stripe.android.checkout.Checkout
7
+ import com.stripe.android.checkout.CheckoutSession
8
+ import com.stripe.android.paymentelement.CheckoutSessionPreview
9
+
10
+ @OptIn(CheckoutSessionPreview::class)
11
+ internal fun mapFromCheckoutState(checkout: Checkout): WritableMap =
12
+ mapFromCheckoutState(
13
+ isLoading = checkout.isLoading.value,
14
+ session = checkout.checkoutSession.value,
15
+ )
16
+
17
+ @OptIn(CheckoutSessionPreview::class)
18
+ internal fun mapFromCheckoutState(
19
+ isLoading: Boolean,
20
+ session: CheckoutSession,
21
+ ): WritableMap =
22
+ Arguments.createMap().apply {
23
+ putString("status", if (isLoading) "loading" else "loaded")
24
+ putMap("session", mapFromCheckoutSession(session))
25
+ }
26
+
27
+ @OptIn(CheckoutSessionPreview::class)
28
+ private fun mapFromCheckoutSession(
29
+ session: CheckoutSession,
30
+ ): WritableMap =
31
+ Arguments.createMap().apply {
32
+ putString("id", session.id)
33
+ // TODO(CheckoutSessionsPreview): Populate from public Android checkout fields once
34
+ // stripe-android exposes status, paymentStatus, and livemode on CheckoutSession.
35
+ putString("status", "unknown")
36
+ putString("paymentStatus", "unknown")
37
+ putBoolean("livemode", false)
38
+
39
+ if (session.currency.isNotBlank()) {
40
+ putString("currency", session.currency)
41
+ }
42
+
43
+ session.totalSummary?.let { putMap("totals", mapFromCheckoutTotals(it)) }
44
+ putArray("lineItems", mapFromCheckoutLineItems(session))
45
+ putArray("shippingOptions", mapFromCheckoutShippingOptions(session))
46
+
47
+ // TODO(CheckoutSessionsPreview): Populate top-level discounts once stripe-android
48
+ // exposes them directly instead of only exposing TotalSummary.discountAmounts.
49
+ putArray("discounts", Arguments.createArray())
50
+
51
+ // TODO(CheckoutSessionsPreview): Populate customerId, customerEmail, billingAddress,
52
+ // and shippingAddress once stripe-android exposes them on public checkout models.
53
+ }
54
+
55
+ @OptIn(CheckoutSessionPreview::class)
56
+ private fun mapFromCheckoutTotals(totals: CheckoutSession.TotalSummary): WritableMap {
57
+ val taxTotal = totals.taxAmounts.sumOf { it.amount }
58
+
59
+ return Arguments.createMap().apply {
60
+ putDouble("subtotal", totals.subtotal.toDouble())
61
+ putDouble("total", totals.totalAmountDue.toDouble())
62
+ putDouble("due", totals.totalDueToday.toDouble())
63
+ // TODO(CheckoutSessionsPreview): Populate the top-level discount total once the
64
+ // Android TotalSummary model exposes it directly.
65
+ putDouble("discount", 0.0)
66
+ putDouble("shipping", (totals.shippingRate?.amount ?: 0L).toDouble())
67
+ putDouble("tax", taxTotal.toDouble())
68
+ }
69
+ }
70
+
71
+ @OptIn(CheckoutSessionPreview::class)
72
+ private fun mapFromCheckoutLineItems(session: CheckoutSession): WritableArray =
73
+ Arguments.createArray().apply {
74
+ session.lineItems.forEach { item ->
75
+ pushMap(
76
+ Arguments.createMap().apply {
77
+ putString("id", item.id)
78
+ putString("name", item.name)
79
+ putInt("quantity", item.quantity)
80
+ putDouble("unitAmount", (item.unitAmount ?: 0L).toDouble())
81
+ putString("currency", session.currency)
82
+ },
83
+ )
84
+ }
85
+ }
86
+
87
+ @OptIn(CheckoutSessionPreview::class)
88
+ private fun mapFromCheckoutShippingOptions(session: CheckoutSession): WritableArray =
89
+ Arguments.createArray().apply {
90
+ session.shippingOptions.forEach { option ->
91
+ pushMap(
92
+ Arguments.createMap().apply {
93
+ putString("id", option.id)
94
+ putString("displayName", option.displayName)
95
+ putDouble("amount", option.amount.toDouble())
96
+ putString("currency", session.currency)
97
+ option.deliveryEstimate?.let { putString("deliveryEstimate", it) }
98
+ },
99
+ )
100
+ }
101
+ }
@@ -186,14 +186,16 @@ internal fun createError(
186
186
  return mapError(code, error.message, error.localizedMessage, null, null, null)
187
187
  }
188
188
 
189
- internal fun createCanceledError(message: String? = null): WritableMap = createError(ErrorType.Canceled.toString(), message)
189
+ internal fun createCanceledError(message: String? = null): WritableMap =
190
+ createError(ErrorType.Canceled.toString(), message)
190
191
 
191
192
  internal fun createFailedError(error: Throwable): WritableMap = createError(ErrorType.Failed.toString(), error)
192
193
 
193
194
  internal fun createMissingInitError(): WritableMap =
194
195
  createError(
195
196
  ErrorType.Failed.toString(),
196
- "Stripe has not been initialized. Initialize Stripe in your app with the StripeProvider component or the initStripe method.",
197
+ "Stripe has not been initialized. Initialize Stripe in your app with the StripeProvider component " +
198
+ "or the initStripe method.",
197
199
  )
198
200
 
199
201
  internal fun createOnrampNotConfiguredError(): WritableMap =
@@ -27,7 +27,11 @@ fun View.hideSoftKeyboard() {
27
27
  fun ReadableMap?.getBooleanOr(
28
28
  key: String,
29
29
  default: Boolean,
30
- ): Boolean = if (this?.hasKey(key) == true && this.getType(key) == ReadableType.Boolean) this.getBoolean(key) else default
30
+ ): Boolean = if (this?.hasKey(key) == true && this.getType(key) == ReadableType.Boolean) {
31
+ this.getBoolean(key)
32
+ } else {
33
+ default
34
+ }
31
35
 
32
36
  fun ReadableMap?.getIntOrNull(key: String): Int? =
33
37
  if (this?.hasKey(key) == true && this.getType(key) == ReadableType.Number) this.getInt(key) else null