@stripe/stripe-react-native 0.68.0 → 0.69.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 (172) hide show
  1. package/android/gradle.properties +1 -1
  2. package/android/src/main/java/com/reactnativestripesdk/LinkControllerManager.kt +175 -0
  3. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +31 -24
  4. package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +8 -4
  5. package/android/src/onramp/java/com/reactnativestripesdk/OnrampErrors.kt +9 -28
  6. package/android/src/onramp/java/com/reactnativestripesdk/OnrampSdkModule.kt +3 -2
  7. package/android/src/test/java/com/reactnativestripesdk/OnrampErrorsTest.kt +20 -18
  8. package/ios/CurrencySelectorElementView.swift +1 -1
  9. package/ios/Mappers+Checkout.swift +3 -3
  10. package/ios/OnrampErrors.swift +1 -14
  11. package/ios/StripeSdk.mm +15 -15
  12. package/ios/StripeSdkImpl+Checkout.swift +15 -39
  13. package/ios/StripeSdkImpl+LinkController.swift +108 -0
  14. package/ios/StripeSdkImpl.swift +8 -3
  15. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  16. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  17. package/lib/commonjs/components/AddressSheet.js +1 -1
  18. package/lib/commonjs/components/AddressSheet.js.map +1 -1
  19. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  20. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  21. package/lib/commonjs/components/CardField.js +1 -1
  22. package/lib/commonjs/components/CardField.js.map +1 -1
  23. package/lib/commonjs/components/CardForm.js +1 -1
  24. package/lib/commonjs/components/CardForm.js.map +1 -1
  25. package/lib/commonjs/components/CurrencySelectorElement.js +1 -1
  26. package/lib/commonjs/components/CurrencySelectorElement.js.map +1 -1
  27. package/lib/commonjs/components/PaymentMethodMessagingElement.js +1 -1
  28. package/lib/commonjs/components/PaymentMethodMessagingElement.js.map +1 -1
  29. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  30. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  31. package/lib/commonjs/components/StripeContainer.js +1 -1
  32. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  33. package/lib/commonjs/connect/Components.js +1 -1
  34. package/lib/commonjs/connect/Components.js.map +1 -1
  35. package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
  36. package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
  37. package/lib/commonjs/connect/EmbeddedComponent.js +1 -1
  38. package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
  39. package/lib/commonjs/connect/ModalCloseButton.js +1 -1
  40. package/lib/commonjs/connect/ModalCloseButton.js.map +1 -1
  41. package/lib/commonjs/connect/NavigationBar.js +1 -1
  42. package/lib/commonjs/connect/NavigationBar.js.map +1 -1
  43. package/lib/commonjs/functions.js +1 -1
  44. package/lib/commonjs/functions.js.map +1 -1
  45. package/lib/commonjs/helpers.js +1 -1
  46. package/lib/commonjs/hooks/useCheckout.js +1 -1
  47. package/lib/commonjs/hooks/useCheckout.js.map +1 -1
  48. package/lib/commonjs/hooks/useLinkController.js +2 -0
  49. package/lib/commonjs/hooks/useLinkController.js.map +1 -0
  50. package/lib/commonjs/index.js +1 -1
  51. package/lib/commonjs/index.js.map +1 -1
  52. package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
  53. package/lib/commonjs/specs/NativeAddressSheet.js +1 -1
  54. package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
  55. package/lib/commonjs/specs/NativeAuBECSDebitForm.js +1 -1
  56. package/lib/commonjs/specs/NativeCardField.js +1 -1
  57. package/lib/commonjs/specs/NativeCardField.js.map +1 -1
  58. package/lib/commonjs/specs/NativeCardForm.js +1 -1
  59. package/lib/commonjs/specs/NativeCardForm.js.map +1 -1
  60. package/lib/commonjs/specs/NativeConnectAccountOnboardingView.js +1 -1
  61. package/lib/commonjs/specs/NativeCurrencySelectorElement.js +1 -1
  62. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
  63. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  64. package/lib/commonjs/specs/NativeGooglePayButton.js +1 -1
  65. package/lib/commonjs/specs/NativeNavigationBar.js +1 -1
  66. package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js +1 -1
  67. package/lib/commonjs/specs/NativeStripeContainer.js +1 -1
  68. package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
  69. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  70. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  71. package/lib/commonjs/types/Errors.js +1 -1
  72. package/lib/commonjs/types/Errors.js.map +1 -1
  73. package/lib/commonjs/types/LinkController.js +2 -0
  74. package/lib/commonjs/types/LinkController.js.map +1 -0
  75. package/lib/commonjs/types/index.js +1 -1
  76. package/lib/commonjs/types/index.js.map +1 -1
  77. package/lib/module/components/AddToWalletButton.js +1 -1
  78. package/lib/module/components/AddToWalletButton.js.map +1 -1
  79. package/lib/module/components/AddressSheet.js +1 -1
  80. package/lib/module/components/AddressSheet.js.map +1 -1
  81. package/lib/module/components/AuBECSDebitForm.js +1 -1
  82. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  83. package/lib/module/components/CardField.js +1 -1
  84. package/lib/module/components/CardField.js.map +1 -1
  85. package/lib/module/components/CardForm.js +1 -1
  86. package/lib/module/components/CardForm.js.map +1 -1
  87. package/lib/module/components/CurrencySelectorElement.js +1 -1
  88. package/lib/module/components/CurrencySelectorElement.js.map +1 -1
  89. package/lib/module/components/PaymentMethodMessagingElement.js +1 -1
  90. package/lib/module/components/PaymentMethodMessagingElement.js.map +1 -1
  91. package/lib/module/components/PlatformPayButton.js +1 -1
  92. package/lib/module/components/PlatformPayButton.js.map +1 -1
  93. package/lib/module/components/StripeContainer.js +1 -1
  94. package/lib/module/components/StripeContainer.js.map +1 -1
  95. package/lib/module/connect/Components.js +1 -1
  96. package/lib/module/connect/Components.js.map +1 -1
  97. package/lib/module/connect/ConnectComponentsProvider.js +1 -1
  98. package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
  99. package/lib/module/connect/EmbeddedComponent.js +1 -1
  100. package/lib/module/connect/EmbeddedComponent.js.map +1 -1
  101. package/lib/module/connect/ModalCloseButton.js +1 -1
  102. package/lib/module/connect/ModalCloseButton.js.map +1 -1
  103. package/lib/module/connect/NavigationBar.js +1 -1
  104. package/lib/module/connect/NavigationBar.js.map +1 -1
  105. package/lib/module/functions.js +1 -1
  106. package/lib/module/functions.js.map +1 -1
  107. package/lib/module/helpers.js +1 -1
  108. package/lib/module/hooks/useCheckout.js +1 -1
  109. package/lib/module/hooks/useCheckout.js.map +1 -1
  110. package/lib/module/hooks/useLinkController.js +2 -0
  111. package/lib/module/hooks/useLinkController.js.map +1 -0
  112. package/lib/module/index.js +1 -1
  113. package/lib/module/index.js.map +1 -1
  114. package/lib/module/specs/NativeAddToWalletButton.js +1 -1
  115. package/lib/module/specs/NativeAddressSheet.js +1 -1
  116. package/lib/module/specs/NativeApplePayButton.js +1 -1
  117. package/lib/module/specs/NativeAuBECSDebitForm.js +1 -1
  118. package/lib/module/specs/NativeCardField.js +1 -1
  119. package/lib/module/specs/NativeCardField.js.map +1 -1
  120. package/lib/module/specs/NativeCardForm.js +1 -1
  121. package/lib/module/specs/NativeCardForm.js.map +1 -1
  122. package/lib/module/specs/NativeConnectAccountOnboardingView.js +1 -1
  123. package/lib/module/specs/NativeCurrencySelectorElement.js +1 -1
  124. package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
  125. package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  126. package/lib/module/specs/NativeGooglePayButton.js +1 -1
  127. package/lib/module/specs/NativeNavigationBar.js +1 -1
  128. package/lib/module/specs/NativePaymentMethodMessagingElement.js +1 -1
  129. package/lib/module/specs/NativeStripeContainer.js +1 -1
  130. package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
  131. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  132. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  133. package/lib/module/types/Errors.js +1 -1
  134. package/lib/module/types/Errors.js.map +1 -1
  135. package/lib/module/types/LinkController.js +2 -0
  136. package/lib/module/types/LinkController.js.map +1 -0
  137. package/lib/module/types/index.js +1 -1
  138. package/lib/module/types/index.js.map +1 -1
  139. package/lib/typescript/src/connect/Components.d.ts +1 -1
  140. package/lib/typescript/src/connect/Components.d.ts.map +1 -1
  141. package/lib/typescript/src/functions.d.ts +19 -1
  142. package/lib/typescript/src/functions.d.ts.map +1 -1
  143. package/lib/typescript/src/hooks/useCheckout.d.ts.map +1 -1
  144. package/lib/typescript/src/hooks/useLinkController.d.ts +28 -0
  145. package/lib/typescript/src/hooks/useLinkController.d.ts.map +1 -0
  146. package/lib/typescript/src/index.d.ts +1 -0
  147. package/lib/typescript/src/index.d.ts.map +1 -1
  148. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +5 -2
  149. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
  150. package/lib/typescript/src/types/Checkout.d.ts +1 -14
  151. package/lib/typescript/src/types/Checkout.d.ts.map +1 -1
  152. package/lib/typescript/src/types/Errors.d.ts +8 -0
  153. package/lib/typescript/src/types/Errors.d.ts.map +1 -1
  154. package/lib/typescript/src/types/LinkController.d.ts +71 -0
  155. package/lib/typescript/src/types/LinkController.d.ts.map +1 -0
  156. package/lib/typescript/src/types/Onramp.d.ts +0 -13
  157. package/lib/typescript/src/types/Onramp.d.ts.map +1 -1
  158. package/lib/typescript/src/types/index.d.ts +2 -1
  159. package/lib/typescript/src/types/index.d.ts.map +1 -1
  160. package/package.json +1 -1
  161. package/src/connect/Components.tsx +10 -0
  162. package/src/functions.ts +48 -0
  163. package/src/hooks/useCheckout.tsx +0 -14
  164. package/src/hooks/useLinkController.tsx +54 -0
  165. package/src/index.tsx +1 -0
  166. package/src/specs/NativeStripeSdkModule.ts +11 -7
  167. package/src/types/Checkout.ts +1 -19
  168. package/src/types/Errors.ts +9 -0
  169. package/src/types/LinkController.ts +75 -0
  170. package/src/types/Onramp.ts +0 -14
  171. package/src/types/index.ts +2 -0
  172. package/stripe-react-native.podspec +1 -1
@@ -3,4 +3,4 @@ StripeSdk_compileSdkVersion=36
3
3
  StripeSdk_targetSdkVersion=36
4
4
  StripeSdk_minSdkVersion=23
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.11.0
6
+ StripeSdk_stripeVersion=23.12.0
@@ -0,0 +1,175 @@
1
+ package com.reactnativestripesdk
2
+
3
+ import androidx.activity.ComponentActivity
4
+ import androidx.lifecycle.SavedStateHandle
5
+ import androidx.lifecycle.lifecycleScope
6
+ import com.facebook.react.bridge.Arguments
7
+ import com.facebook.react.bridge.Promise
8
+ import com.facebook.react.bridge.ReactApplicationContext
9
+ import com.facebook.react.bridge.ReadableMap
10
+ import com.reactnativestripesdk.utils.ErrorType
11
+ import com.reactnativestripesdk.utils.createError
12
+ import com.reactnativestripesdk.utils.mapFromPaymentMethod
13
+ import com.stripe.android.link.LinkController
14
+ import com.stripe.android.link.LinkControllerPreview
15
+ import kotlinx.coroutines.Dispatchers
16
+ import kotlinx.coroutines.launch
17
+ import kotlinx.coroutines.withContext
18
+
19
+ internal const val LINK_CONTROLLER_NOT_INITIALIZED_ERROR =
20
+ "LinkController has not been initialized. Call initLinkController first."
21
+
22
+ /**
23
+ * Manages [LinkController] initialization and presentation for the React Native bridge.
24
+ *
25
+ * @PrivatePreview
26
+ */
27
+ @OptIn(LinkControllerPreview::class)
28
+ internal class LinkControllerManager(
29
+ private val context: ReactApplicationContext,
30
+ private val publishableKey: String,
31
+ private val stripeAccountId: String?,
32
+ ) {
33
+ private var linkController: LinkController? = null
34
+ private var linkPresenter: LinkController.Presenter? = null
35
+ private var presentPromise: Promise? = null
36
+
37
+ fun configure(params: ReadableMap, promise: Promise) {
38
+ val email = params.getString("email")
39
+ val merchantDisplayName = params.getString("merchantDisplayName")
40
+ if (merchantDisplayName == null) {
41
+ promise.resolve(createError(ErrorType.Failed.toString(), "merchantDisplayName is required."))
42
+ return
43
+ }
44
+
45
+ val activity = context.currentActivity as? ComponentActivity
46
+ if (activity == null) {
47
+ promise.resolve(createError(ErrorType.Failed.toString(), "Activity is not available. Retry this method."))
48
+ return
49
+ }
50
+
51
+ val phoneNumber = params.getString("phoneNumber")
52
+ val allowLogout = if (params.hasKey("allowLogout")) params.getBoolean("allowLogout") else true
53
+ val setupIntentClientSecret = params.getString("setupIntentClientSecret")
54
+
55
+ val supportedTypes = parseSupportedPaymentMethodTypes(params)
56
+
57
+ val config = LinkController.Configuration(
58
+ merchantDisplayName = merchantDisplayName,
59
+ publishableKey = publishableKey,
60
+ stripeAccountId = stripeAccountId
61
+ )
62
+ .also { if (email != null) it.email(email) }
63
+ .phoneNumber(phoneNumber)
64
+ .supportedPaymentMethodTypes(supportedTypes)
65
+ .allowLogout(allowLogout)
66
+ .setupIntentClientSecret(setupIntentClientSecret)
67
+
68
+ // Build a new controller if this is the first call or after a reset.
69
+ // SavedStateHandle() is empty — state will not survive process death, which is
70
+ // the same trade-off made by other Stripe RN SDK APIs.
71
+ if (linkController == null) {
72
+ linkController = LinkController.Builder(
73
+ application = activity.application,
74
+ savedStateHandle = SavedStateHandle()
75
+ ).build()
76
+ }
77
+
78
+ val controller = linkController!!
79
+
80
+ activity.lifecycleScope.launch {
81
+ val result = controller.configure(config)
82
+ if (result.isSuccess) {
83
+ // Create once: presenter may register ActivityResultLaunchers, which must happen before onStart().
84
+ if (linkPresenter == null) {
85
+ linkPresenter = controller.createPresenter(
86
+ activity = activity,
87
+ presentCallback = { presentResult -> handlePresentResult(presentResult) }
88
+ )
89
+ }
90
+ promise.resolve(Arguments.createMap())
91
+ } else {
92
+ promise.resolve(
93
+ createError(
94
+ ErrorType.Failed.toString(),
95
+ result.exceptionOrNull()?.message ?: "LinkController configuration failed."
96
+ )
97
+ )
98
+ }
99
+ }
100
+ }
101
+
102
+ fun present(promise: Promise) {
103
+ val presenter = linkPresenter
104
+ if (presenter == null) {
105
+ promise.resolve(createError(ErrorType.Failed.toString(), LINK_CONTROLLER_NOT_INITIALIZED_ERROR))
106
+ return
107
+ }
108
+
109
+ presentPromise = promise
110
+ presenter.present()
111
+ }
112
+
113
+ fun destroy() {
114
+ linkController = null
115
+ linkPresenter = null
116
+ presentPromise = null
117
+ }
118
+
119
+ private fun parseSupportedPaymentMethodTypes(params: ReadableMap): List<LinkController.PaymentMethodType>? =
120
+ params.getArray("supportedPaymentMethodTypes")?.let { arr ->
121
+ (0 until arr.size()).mapNotNull { i ->
122
+ when (arr.getString(i)) {
123
+ "card" -> LinkController.PaymentMethodType.Card
124
+ "bankAccount" -> LinkController.PaymentMethodType.BankAccount
125
+ else -> null
126
+ }
127
+ }.ifEmpty { null }
128
+ }
129
+
130
+ private fun handlePresentResult(result: LinkController.PresentResult) {
131
+ val promise = presentPromise ?: return
132
+ val controller = linkController ?: return
133
+ when (result) {
134
+ is LinkController.PresentResult.Completed -> {
135
+ val response = Arguments.createMap()
136
+ response.putMap("paymentMethod", mapFromPaymentMethod(result.paymentMethod))
137
+
138
+ val preview = controller.paymentMethodPreview.value
139
+ val currentScope = (context.currentActivity as? ComponentActivity)?.lifecycleScope
140
+ if (preview != null && currentScope != null) {
141
+ currentScope.launch {
142
+ val iconBase64 = withContext(Dispatchers.IO) {
143
+ try {
144
+ convertDrawableToBase64(preview.icon)
145
+ } catch (_: Exception) {
146
+ null
147
+ }
148
+ }
149
+ val previewMap = Arguments.createMap()
150
+ previewMap.putString("label", preview.label)
151
+ preview.sublabel?.let { previewMap.putString("sublabel", it) }
152
+ if (iconBase64 != null) {
153
+ previewMap.putString("icon", "data:image/png;base64,$iconBase64")
154
+ }
155
+ response.putMap("paymentMethodPreview", previewMap)
156
+ promise.resolve(response)
157
+ }
158
+ } else {
159
+ promise.resolve(response)
160
+ }
161
+ }
162
+ is LinkController.PresentResult.Canceled -> {
163
+ promise.resolve(
164
+ createError(
165
+ ErrorType.Canceled.toString(),
166
+ "The customer canceled the Link flow."
167
+ )
168
+ )
169
+ }
170
+ is LinkController.PresentResult.Failed -> {
171
+ promise.resolve(createError(ErrorType.Failed.toString(), result.error))
172
+ }
173
+ }
174
+ }
175
+ }
@@ -127,6 +127,7 @@ class StripeSdkModule(
127
127
  private val checkoutStateObservers = mutableMapOf<String, Job>()
128
128
 
129
129
  private var customerSheetManager: CustomerSheetManager? = null
130
+ private var linkControllerManager: LinkControllerManager? = null
130
131
 
131
132
  internal var embeddedIntentCreationCallback = CompletableDeferred<ReadableMap>()
132
133
  internal var embeddedConfirmationTokenCreationCallback = CompletableDeferred<ReadableMap>()
@@ -180,6 +181,8 @@ class StripeSdkModule(
180
181
  checkoutStateObservers.values.forEach { it.cancel() }
181
182
  checkoutStateObservers.clear()
182
183
  checkoutInstances.clear()
184
+ linkControllerManager?.destroy()
185
+ linkControllerManager = null
183
186
  }
184
187
 
185
188
  private fun registerStripeUIManager(uiManager: StripeUIManager) {
@@ -1798,30 +1801,6 @@ class StripeSdkModule(
1798
1801
  }
1799
1802
  }
1800
1803
 
1801
- override fun checkoutUpdateBillingAddress(
1802
- sessionKey: String,
1803
- address: ReadableMap,
1804
- name: String?,
1805
- phone: String?,
1806
- promise: Promise,
1807
- ) {
1808
- val addressUpdate = buildCheckoutAddressUpdate(name, phone, address) ?: run {
1809
- promise.reject(ErrorType.Failed.toString(), "A billing address country is required.")
1810
- return
1811
- }
1812
-
1813
- performCheckoutMutation(
1814
- sessionKey = sessionKey,
1815
- promise = promise,
1816
- ) { checkout ->
1817
- checkout.updateBillingAddress(
1818
- name = addressUpdate.name,
1819
- phoneNumber = addressUpdate.phone,
1820
- address = addressUpdate.toCheckoutAddress(),
1821
- )
1822
- }
1823
- }
1824
-
1825
1804
  override fun checkoutApplyPromotionCode(
1826
1805
  sessionKey: String,
1827
1806
  code: String,
@@ -2024,6 +2003,34 @@ class StripeSdkModule(
2024
2003
  }
2025
2004
  }
2026
2005
 
2006
+ // LinkController - Private Preview
2007
+
2008
+ @ReactMethod
2009
+ override fun initLinkController(
2010
+ params: ReadableMap,
2011
+ promise: Promise,
2012
+ ) {
2013
+ linkControllerManager?.destroy()
2014
+ linkControllerManager = LinkControllerManager(
2015
+ context = reactApplicationContext,
2016
+ publishableKey = publishableKey,
2017
+ stripeAccountId = stripeAccountId,
2018
+ )
2019
+ linkControllerManager?.configure(params, promise)
2020
+ }
2021
+
2022
+ @ReactMethod
2023
+ override fun presentLinkController(promise: Promise) {
2024
+ val manager = linkControllerManager
2025
+ if (manager == null) {
2026
+ promise.resolve(createError(ErrorType.Failed.toString(), LINK_CONTROLLER_NOT_INITIALIZED_ERROR))
2027
+ return
2028
+ }
2029
+ UiThreadUtil.runOnUiThread {
2030
+ manager.present(promise)
2031
+ }
2032
+ }
2033
+
2027
2034
  /**
2028
2035
  * React native apps do not properly handle activity re-creation so make
2029
2036
  * sure to dismiss any stripe ui when that happens to make sure apps stay
@@ -305,10 +305,6 @@ public abstract class NativeStripeSdkModuleSpec extends ReactContextBaseJavaModu
305
305
  @DoNotStrip
306
306
  public abstract void checkoutUpdateShippingAddress(String sessionKey, ReadableMap address, @Nullable String name, @Nullable String phone, Promise promise);
307
307
 
308
- @ReactMethod
309
- @DoNotStrip
310
- public abstract void checkoutUpdateBillingAddress(String sessionKey, ReadableMap address, @Nullable String name, @Nullable String phone, Promise promise);
311
-
312
308
  @ReactMethod
313
309
  @DoNotStrip
314
310
  public abstract void checkoutApplyPromotionCode(String sessionKey, String code, Promise promise);
@@ -333,6 +329,14 @@ public abstract class NativeStripeSdkModuleSpec extends ReactContextBaseJavaModu
333
329
  @DoNotStrip
334
330
  public abstract void checkoutRunServerUpdateComplete(String sessionKey, @Nullable String error, Promise promise);
335
331
 
332
+ @ReactMethod
333
+ @DoNotStrip
334
+ public abstract void initLinkController(ReadableMap params, Promise promise);
335
+
336
+ @ReactMethod
337
+ @DoNotStrip
338
+ public abstract void presentLinkController(Promise promise);
339
+
336
340
  @ReactMethod
337
341
  @DoNotStrip
338
342
  public abstract void addListener(String eventType);
@@ -3,14 +3,12 @@
3
3
  package com.reactnativestripesdk
4
4
 
5
5
  import com.facebook.react.bridge.Arguments
6
- import com.facebook.react.bridge.WritableArray
7
6
  import com.facebook.react.bridge.WritableMap
8
7
  import com.reactnativestripesdk.utils.ErrorType
9
8
  import com.stripe.android.core.exception.StripeException
10
9
  import com.stripe.android.crypto.onramp.ExperimentalCryptoOnramp
11
10
  import com.stripe.android.crypto.onramp.exception.AppAttestationException
12
11
  import com.stripe.android.crypto.onramp.exception.CryptoOnrampApiException
13
- import com.stripe.android.crypto.onramp.exception.SDKVersion
14
12
  import com.stripe.android.crypto.onramp.exception.StripeCryptoOnrampError
15
13
 
16
14
  internal fun createOnrampFailedError(error: Throwable): WritableMap =
@@ -32,29 +30,26 @@ private fun createOnrampError(
32
30
 
33
31
  val stripeError = apiException.stripeError
34
32
  val onrampErrorType = apiException.toOnrampErrorType()
33
+ val apiErrorContext = apiException.apiErrorContext
35
34
 
36
35
  return createOnrampErrorMap(
37
36
  code = code,
38
37
  message = apiException.userMessage,
39
38
  localizedMessage = apiException.localizedMessage,
40
39
  declineCode = stripeError?.declineCode,
41
- type = apiException.context.apiErrorType ?: stripeError?.type,
40
+ type = apiErrorContext.apiErrorType ?: stripeError?.type,
42
41
  stripeErrorCode = apiException.code,
43
42
  ) {
44
43
  putString("onrampErrorType", onrampErrorType)
45
44
  putString("developerMessage", apiException.developerMessage)
46
45
  putString("userMessage", apiException.userMessage)
47
- putString("reason", apiException.context.reason)
48
- putString("operation", apiException.context.operation)
49
- putString("appPackageName", apiException.context.appPackageName)
50
- putString("mode", apiException.context.mode)
51
- putArray("sdkVersions", mapFromSdkVersions(apiException.sdkVersions))
52
- putString("requestId", apiException.context.requestId)
53
- putString("apiErrorCode", apiException.context.apiErrorCode)
54
- putString("apiErrorType", apiException.context.apiErrorType)
55
- putString("apiErrorMessage", apiException.context.apiErrorMessage)
56
- putString("apiUserMessage", apiException.context.apiUserMessage)
57
- putString("docUrl", apiException.context.docUrl)
46
+ putString("reason", apiErrorContext.reason)
47
+ putString("requestId", apiErrorContext.requestId)
48
+ putString("apiErrorCode", apiErrorContext.apiErrorCode)
49
+ putString("apiErrorType", apiErrorContext.apiErrorType)
50
+ putString("apiErrorMessage", apiErrorContext.apiErrorMessage)
51
+ putString("apiUserMessage", apiErrorContext.apiUserMessage)
52
+ putString("docUrl", apiErrorContext.docUrl)
58
53
  }
59
54
  }
60
55
 
@@ -129,17 +124,3 @@ private fun createOnrampErrorMap(
129
124
  map.putMap("error", details)
130
125
  return map
131
126
  }
132
-
133
- private fun mapFromSdkVersions(
134
- sdkVersions: List<SDKVersion>,
135
- ): WritableArray =
136
- Arguments.createArray().apply {
137
- sdkVersions.forEach { sdkVersion ->
138
- pushMap(
139
- Arguments.createMap().apply {
140
- putString("name", sdkVersion.name)
141
- putString("version", sdkVersion.version)
142
- },
143
- )
144
- }
145
- }
@@ -1,4 +1,4 @@
1
- @file:OptIn(ExperimentalCryptoOnramp::class)
1
+ @file:OptIn(ExperimentalCryptoOnramp::class, LinkControllerPreview::class)
2
2
 
3
3
  package com.reactnativestripesdk
4
4
 
@@ -52,6 +52,7 @@ import com.stripe.android.crypto.onramp.model.OnrampVerifyIdentityResult
52
52
  import com.stripe.android.crypto.onramp.model.OnrampVerifyKycInfoResult
53
53
  import com.stripe.android.crypto.onramp.model.PaymentMethodSelection
54
54
  import com.stripe.android.link.LinkController.PaymentMethodPreview
55
+ import com.stripe.android.link.LinkControllerPreview
55
56
  import com.stripe.android.link.PaymentMethodPreviewDetails
56
57
  import com.stripe.android.model.CardBrand
57
58
  import com.stripe.android.model.DateOfBirth
@@ -65,7 +66,7 @@ import kotlinx.coroutines.withContext
65
66
  import kotlinx.coroutines.withTimeout
66
67
 
67
68
  @SuppressLint("RestrictedApi")
68
- @OptIn(ExperimentalCryptoOnramp::class)
69
+ @OptIn(ExperimentalCryptoOnramp::class, LinkControllerPreview::class)
69
70
  @ReactModule(name = NativeOnrampSdkModuleSpec.NAME)
70
71
  class OnrampSdkModule(
71
72
  reactContext: ReactApplicationContext,
@@ -52,17 +52,6 @@ class OnrampErrorsTest {
52
52
  assertEquals("api_error", details.getString("type"))
53
53
  assertEquals("api_error", details.getString("apiErrorType"))
54
54
  assertEquals("android_package_name_mismatch", details.getString("reason"))
55
- assertEquals("configure", details.getString("operation"))
56
- assertEquals("com.example.app", details.getString("appPackageName"))
57
- assertEquals("test", details.getString("mode"))
58
- val sdkVersions = details.getArray("sdkVersions")
59
- assertNotNull(sdkVersions)
60
- val stripeAndroidVersion = sdkVersions!!.getMap(0)
61
- assertEquals("stripe-android", stripeAndroidVersion!!.getString("name"))
62
- assertEquals("23.9.1", stripeAndroidVersion.getString("version"))
63
- val reactNativeVersion = sdkVersions.getMap(1)
64
- assertEquals("stripe-react-native", reactNativeVersion!!.getString("name"))
65
- assertEquals("0.66.0", reactNativeVersion.getString("version"))
66
55
  assertEquals("req_attestation", details.getString("requestId"))
67
56
  assertEquals(
68
57
  "Attestation request could not be verified.",
@@ -81,7 +70,7 @@ class OnrampErrorsTest {
81
70
  fun createOnrampFailedError_withUncategorizedApiError_preservesStripeCodes() {
82
71
  val error =
83
72
  createOnrampException(
84
- className = "com.stripe.android.crypto.onramp.exception.UncategorizedApiErrorException",
73
+ className = "com.stripe.android.crypto.onramp.exception.UncategorizedException",
85
74
  reason = "consumer_session_expired",
86
75
  operation = "authorize",
87
76
  appPackageName = "com.example.app",
@@ -104,7 +93,6 @@ class OnrampErrorsTest {
104
93
  assertEquals("authentication_error", details.getString("type"))
105
94
  assertEquals("Session expired. Please sign in again.", details.getString("message"))
106
95
  assertEquals("Session expired. Please sign in again.", details.getString("userMessage"))
107
- assertEquals("authorize", details.getString("operation"))
108
96
  assertEquals("req_auth", details.getString("requestId"))
109
97
  assertTrue(details.getString("developerMessage")!!.contains("Code: consumer_session_expired"))
110
98
  }
@@ -135,9 +123,6 @@ class OnrampErrorsTest {
135
123
  val context =
136
124
  APIErrorContext(
137
125
  reason = reason,
138
- operation = operation,
139
- appPackageName = appPackageName,
140
- mode = mode,
141
126
  apiErrorCode = apiErrorCode,
142
127
  apiErrorType = apiErrorType,
143
128
  apiErrorMessage = apiErrorMessage,
@@ -145,6 +130,23 @@ class OnrampErrorsTest {
145
130
  docUrl = docUrl,
146
131
  underlyingError = cause,
147
132
  )
133
+ val diagnosticContextClass =
134
+ Class.forName("com.stripe.android.crypto.onramp.exception.DiagnosticContext")
135
+ val diagnosticContext =
136
+ diagnosticContextClass
137
+ .getDeclaredConstructor(
138
+ List::class.java,
139
+ String::class.java,
140
+ String::class.java,
141
+ String::class.java,
142
+ ).apply {
143
+ isAccessible = true
144
+ }.newInstance(
145
+ sdkVersions,
146
+ operation,
147
+ appPackageName,
148
+ mode,
149
+ )
148
150
 
149
151
  // The exception classes in the `com.stripe.android.crypto.onramp.exception` package
150
152
  // don't have public constructors, so we use reflection to create instances for testing.
@@ -153,7 +155,7 @@ class OnrampErrorsTest {
153
155
  .forName(className)
154
156
  .getDeclaredConstructor(
155
157
  APIErrorContext::class.java,
156
- List::class.java,
158
+ diagnosticContextClass,
157
159
  String::class.java,
158
160
  ).apply {
159
161
  isAccessible = true
@@ -161,7 +163,7 @@ class OnrampErrorsTest {
161
163
 
162
164
  return constructor.newInstance(
163
165
  context,
164
- sdkVersions,
166
+ diagnosticContext,
165
167
  userMessage,
166
168
  ) as Exception
167
169
  }
@@ -110,7 +110,7 @@ public class StripeCurrencySelectorElementContainerView: UIView {
110
110
  /// so the caption-text changes after a currency selection are reflected
111
111
  /// in our reported height back to RN.
112
112
  private func subscribeForLayoutUpdates(on checkout: Checkout) {
113
- sessionCancellable = checkout.$state
113
+ sessionCancellable = checkout.$session
114
114
  .dropFirst()
115
115
  .receive(on: DispatchQueue.main)
116
116
  .sink { [weak self] _ in
@@ -12,10 +12,10 @@ extension Mappers {
12
12
 
13
13
  // MARK: - State & Session
14
14
 
15
- class func mapFromCheckoutState(_ state: Checkout.State) -> NSDictionary {
15
+ class func mapFromCheckoutState(isLoading: Bool, session: Checkout.Session) -> NSDictionary {
16
16
  [
17
- "status": state.isLoading ? "loading" : "loaded",
18
- "session": mapFromCheckoutSession(state.session),
17
+ "status": isLoading ? "loading" : "loaded",
18
+ "session": mapFromCheckoutSession(session),
19
19
  ]
20
20
  }
21
21
 
@@ -61,10 +61,6 @@ enum OnrampErrors {
61
61
  stripeErrorCode: error.code,
62
62
  additionalFields: commonOnrampFields(error: error, onrampErrorType: onrampErrorType).merging([
63
63
  "reason": error.reason,
64
- "operation": error.operation,
65
- "appPackageName": error.appIdentifier,
66
- "mode": error.mode,
67
- "sdkVersions": mapSDKVersions(error.sdkVersions),
68
64
  "requestId": error.requestID,
69
65
  "apiErrorCode": error.code,
70
66
  "apiErrorType": error.type,
@@ -86,18 +82,9 @@ enum OnrampErrors {
86
82
  ]
87
83
  }
88
84
 
89
- private static func mapSDKVersions(_ sdkVersions: [SDKVersion]) -> [[String: String]] {
90
- return sdkVersions.map { sdkVersion in
91
- [
92
- "name": sdkVersion.name,
93
- "version": sdkVersion.version,
94
- ]
95
- }
96
- }
97
-
98
85
  private static func onrampErrorType(for error: StripeCryptoOnrampAPIError) -> String {
99
86
  switch error {
100
- case is AppAttestationAPIError:
87
+ case is AppAttestationError:
101
88
  return "AppAttestationError"
102
89
  default:
103
90
  return "UncategorizedApiError"
package/ios/StripeSdk.mm CHANGED
@@ -289,21 +289,6 @@ RCT_EXPORT_METHOD(checkoutUpdateShippingAddress:(nonnull NSString *)sessionKey
289
289
  rejecter:reject];
290
290
  }
291
291
 
292
- RCT_EXPORT_METHOD(checkoutUpdateBillingAddress:(nonnull NSString *)sessionKey
293
- address:(nonnull NSDictionary *)address
294
- name:(NSString *_Nullable)name
295
- phone:(NSString *_Nullable)phone
296
- resolve:(nonnull RCTPromiseResolveBlock)resolve
297
- reject:(nonnull RCTPromiseRejectBlock)reject)
298
- {
299
- [StripeSdkImpl.shared checkoutUpdateBillingAddress:sessionKey
300
- address:address
301
- name:name
302
- phone:phone
303
- resolver:resolve
304
- rejecter:reject];
305
- }
306
-
307
292
  RCT_EXPORT_METHOD(checkoutApplyPromotionCode:(nonnull NSString *)sessionKey
308
293
  code:(nonnull NSString *)code
309
294
  resolve:(nonnull RCTPromiseResolveBlock)resolve
@@ -599,6 +584,21 @@ RCT_EXPORT_METHOD(createRadarSession:(nonnull RCTPromiseResolveBlock)resolve
599
584
  [StripeSdkImpl.shared createRadarSession:resolve rejecter:reject];
600
585
  }
601
586
 
587
+ // LinkController - Private Preview
588
+
589
+ RCT_EXPORT_METHOD(initLinkController:(nonnull NSDictionary *)params
590
+ resolve:(nonnull RCTPromiseResolveBlock)resolve
591
+ reject:(nonnull RCTPromiseRejectBlock)reject)
592
+ {
593
+ [StripeSdkImpl.shared initLinkController:params resolver:resolve rejecter:reject];
594
+ }
595
+
596
+ RCT_EXPORT_METHOD(presentLinkController:(nonnull RCTPromiseResolveBlock)resolve
597
+ reject:(nonnull RCTPromiseRejectBlock)reject)
598
+ {
599
+ [StripeSdkImpl.shared presentLinkController:resolve rejecter:reject];
600
+ }
601
+
602
602
  /* clang-format on */
603
603
 
604
604
  #ifdef RCT_NEW_ARCH_ENABLED
@@ -11,7 +11,7 @@ import Foundation
11
11
 
12
12
  extension StripeSdkImpl {
13
13
  internal func currentCheckoutStateResult(checkout: Checkout) -> NSDictionary {
14
- Mappers.mapFromCheckoutState(checkout.state)
14
+ Mappers.mapFromCheckoutState(isLoading: checkout.isLoading, session: checkout.session)
15
15
  }
16
16
 
17
17
  @objc(initCheckoutSession:configuration:resolver:rejecter:)
@@ -21,7 +21,10 @@ extension StripeSdkImpl {
21
21
  resolver resolve: @escaping RCTPromiseResolveBlock,
22
22
  rejecter reject: @escaping RCTPromiseRejectBlock
23
23
  ) {
24
- let checkoutConfiguration = buildCheckoutConfiguration(params: configuration)
24
+ let checkoutConfiguration = buildCheckoutConfiguration(
25
+ clientSecret: clientSecret,
26
+ params: configuration
27
+ )
25
28
 
26
29
  Task { @MainActor [weak self] in
27
30
  guard let self else {
@@ -30,18 +33,16 @@ extension StripeSdkImpl {
30
33
  }
31
34
 
32
35
  do {
33
- let checkout = try await Checkout(
34
- clientSecret: clientSecret,
35
- configuration: checkoutConfiguration
36
- )
36
+ let checkout = try await Checkout(configuration: checkoutConfiguration)
37
37
  let sessionKey = UUID().uuidString
38
38
 
39
- let cancellable = checkout.$state
39
+ let cancellable = checkout.$isLoading
40
+ .combineLatest(checkout.$session)
40
41
  .dropFirst()
41
- .sink { [weak self] state in
42
+ .sink { [weak self] isLoading, session in
42
43
  self?.emitter?.emitCheckoutSessionDidChangeState([
43
44
  "sessionKey": sessionKey,
44
- "state": Mappers.mapFromCheckoutState(state),
45
+ "state": Mappers.mapFromCheckoutState(isLoading: isLoading, session: session),
45
46
  ])
46
47
  }
47
48
 
@@ -84,32 +85,6 @@ extension StripeSdkImpl {
84
85
  }
85
86
  }
86
87
 
87
- @objc(checkoutUpdateBillingAddress:address:name:phone:resolver:rejecter:)
88
- public func checkoutUpdateBillingAddress(
89
- sessionKey: String,
90
- address: NSDictionary,
91
- name: String?,
92
- phone: String?,
93
- resolver resolve: @escaping RCTPromiseResolveBlock,
94
- rejecter reject: @escaping RCTPromiseRejectBlock
95
- ) {
96
- performCheckoutAddressMutation(
97
- sessionKey: sessionKey,
98
- address: address,
99
- name: name,
100
- phone: phone,
101
- missingCountryMessage: "A billing address country is required.",
102
- resolver: resolve,
103
- rejecter: reject
104
- ) { checkout, addressUpdate in
105
- try await checkout.updateBillingAddress(
106
- name: addressUpdate.name,
107
- phone: addressUpdate.phone,
108
- address: addressUpdate.address
109
- )
110
- }
111
- }
112
-
113
88
  @objc(checkoutApplyPromotionCode:code:resolver:rejecter:)
114
89
  public func checkoutApplyPromotionCode(
115
90
  sessionKey: String,
@@ -234,8 +209,11 @@ extension StripeSdkImpl {
234
209
  resolve(nil)
235
210
  }
236
211
 
237
- internal func buildCheckoutConfiguration(params: NSDictionary) -> Checkout.Configuration {
238
- var configuration = Checkout.Configuration()
212
+ internal func buildCheckoutConfiguration(
213
+ clientSecret: String,
214
+ params: NSDictionary
215
+ ) -> Checkout.Configuration {
216
+ var configuration = Checkout.Configuration(clientSecret: clientSecret)
239
217
 
240
218
  if let adaptivePricing = params["adaptivePricing"] as? NSDictionary,
241
219
  let allowed = adaptivePricing["allowed"] as? Bool {
@@ -329,8 +307,6 @@ extension StripeSdkImpl {
329
307
  switch checkoutError {
330
308
  case .invalidClientSecret:
331
309
  return "InvalidClientSecret"
332
- case .sessionNotOpen:
333
- return "SessionNotOpen"
334
310
  case .sheetCurrentlyPresented:
335
311
  return "SheetCurrentlyPresented"
336
312
  default: