@stripe/stripe-react-native 0.68.0 → 0.70.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 (218) hide show
  1. package/android/.idea/AndroidProjectSystem.xml +6 -0
  2. package/android/.idea/caches/deviceStreaming.xml +835 -0
  3. package/android/.idea/compiler.xml +6 -0
  4. package/android/.idea/gradle.xml +17 -0
  5. package/android/.idea/migrations.xml +10 -0
  6. package/android/.idea/misc.xml +10 -0
  7. package/android/.idea/runConfigurations.xml +17 -0
  8. package/android/.idea/vcs.xml +6 -0
  9. package/android/gradle.properties +1 -1
  10. package/android/local.properties +8 -0
  11. package/android/src/main/java/com/reactnativestripesdk/LinkControllerManager.kt +175 -0
  12. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +31 -24
  13. package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +7 -8
  14. package/android/src/oldarch/java/com/reactnativestripesdk/NativeOnrampSdkModuleSpec.java +8 -0
  15. package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +8 -4
  16. package/android/src/onramp/java/com/reactnativestripesdk/OnrampErrors.kt +30 -35
  17. package/android/src/onramp/java/com/reactnativestripesdk/OnrampMappers.kt +48 -0
  18. package/android/src/onramp/java/com/reactnativestripesdk/OnrampSdkModule.kt +65 -2
  19. package/android/src/test/java/com/reactnativestripesdk/OnrampErrorsTest.kt +106 -27
  20. package/android/src/test/java/com/reactnativestripesdk/mappers/OnrampMappersTest.kt +37 -0
  21. package/android/src/test/java/com/reactnativestripesdk/mappers/PaymentMethodMappersTest.kt +110 -0
  22. package/ios/CurrencySelectorElementView.swift +1 -1
  23. package/ios/Mappers+Checkout.swift +3 -3
  24. package/ios/Mappers.swift +19 -0
  25. package/ios/OnrampErrors.swift +21 -19
  26. package/ios/StripeOnrampSdk.mm +16 -0
  27. package/ios/StripeSdk.mm +15 -15
  28. package/ios/StripeSdkImpl+Checkout.swift +15 -39
  29. package/ios/StripeSdkImpl+LinkController.swift +108 -0
  30. package/ios/StripeSdkImpl.swift +64 -3
  31. package/jest/setup.js +2 -0
  32. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  33. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  34. package/lib/commonjs/components/AddressSheet.js +1 -1
  35. package/lib/commonjs/components/AddressSheet.js.map +1 -1
  36. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  37. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  38. package/lib/commonjs/components/CardField.js +1 -1
  39. package/lib/commonjs/components/CardField.js.map +1 -1
  40. package/lib/commonjs/components/CardForm.js +1 -1
  41. package/lib/commonjs/components/CardForm.js.map +1 -1
  42. package/lib/commonjs/components/CurrencySelectorElement.js +1 -1
  43. package/lib/commonjs/components/CurrencySelectorElement.js.map +1 -1
  44. package/lib/commonjs/components/PaymentMethodMessagingElement.js +1 -1
  45. package/lib/commonjs/components/PaymentMethodMessagingElement.js.map +1 -1
  46. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  47. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  48. package/lib/commonjs/components/StripeContainer.js +1 -1
  49. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  50. package/lib/commonjs/components/StripeProvider.js +1 -1
  51. package/lib/commonjs/components/StripeProvider.js.map +1 -1
  52. package/lib/commonjs/connect/Components.js +1 -1
  53. package/lib/commonjs/connect/Components.js.map +1 -1
  54. package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
  55. package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
  56. package/lib/commonjs/connect/EmbeddedComponent.js +1 -1
  57. package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
  58. package/lib/commonjs/connect/ModalCloseButton.js +1 -1
  59. package/lib/commonjs/connect/ModalCloseButton.js.map +1 -1
  60. package/lib/commonjs/connect/NavigationBar.js +1 -1
  61. package/lib/commonjs/connect/NavigationBar.js.map +1 -1
  62. package/lib/commonjs/functions.js +1 -1
  63. package/lib/commonjs/functions.js.map +1 -1
  64. package/lib/commonjs/helpers.js +1 -1
  65. package/lib/commonjs/hooks/useCheckout.js +1 -1
  66. package/lib/commonjs/hooks/useCheckout.js.map +1 -1
  67. package/lib/commonjs/hooks/useLinkController.js +2 -0
  68. package/lib/commonjs/hooks/useLinkController.js.map +1 -0
  69. package/lib/commonjs/hooks/useOnramp.js +1 -1
  70. package/lib/commonjs/hooks/useOnramp.js.map +1 -1
  71. package/lib/commonjs/index.js +1 -1
  72. package/lib/commonjs/index.js.map +1 -1
  73. package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
  74. package/lib/commonjs/specs/NativeAddressSheet.js +1 -1
  75. package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
  76. package/lib/commonjs/specs/NativeAuBECSDebitForm.js +1 -1
  77. package/lib/commonjs/specs/NativeCardField.js +1 -1
  78. package/lib/commonjs/specs/NativeCardField.js.map +1 -1
  79. package/lib/commonjs/specs/NativeCardForm.js +1 -1
  80. package/lib/commonjs/specs/NativeCardForm.js.map +1 -1
  81. package/lib/commonjs/specs/NativeConnectAccountOnboardingView.js +1 -1
  82. package/lib/commonjs/specs/NativeCurrencySelectorElement.js +1 -1
  83. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
  84. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  85. package/lib/commonjs/specs/NativeGooglePayButton.js +1 -1
  86. package/lib/commonjs/specs/NativeNavigationBar.js +1 -1
  87. package/lib/commonjs/specs/NativeOnrampSdkModule.js.map +1 -1
  88. package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js +1 -1
  89. package/lib/commonjs/specs/NativeStripeContainer.js +1 -1
  90. package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
  91. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  92. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  93. package/lib/commonjs/types/Errors.js +1 -1
  94. package/lib/commonjs/types/Errors.js.map +1 -1
  95. package/lib/commonjs/types/LinkController.js +2 -0
  96. package/lib/commonjs/types/LinkController.js.map +1 -0
  97. package/lib/commonjs/types/Onramp.js +1 -1
  98. package/lib/commonjs/types/Onramp.js.map +1 -1
  99. package/lib/commonjs/types/index.js +1 -1
  100. package/lib/commonjs/types/index.js.map +1 -1
  101. package/lib/module/components/AddToWalletButton.js +1 -1
  102. package/lib/module/components/AddToWalletButton.js.map +1 -1
  103. package/lib/module/components/AddressSheet.js +1 -1
  104. package/lib/module/components/AddressSheet.js.map +1 -1
  105. package/lib/module/components/AuBECSDebitForm.js +1 -1
  106. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  107. package/lib/module/components/CardField.js +1 -1
  108. package/lib/module/components/CardField.js.map +1 -1
  109. package/lib/module/components/CardForm.js +1 -1
  110. package/lib/module/components/CardForm.js.map +1 -1
  111. package/lib/module/components/CurrencySelectorElement.js +1 -1
  112. package/lib/module/components/CurrencySelectorElement.js.map +1 -1
  113. package/lib/module/components/PaymentMethodMessagingElement.js +1 -1
  114. package/lib/module/components/PaymentMethodMessagingElement.js.map +1 -1
  115. package/lib/module/components/PlatformPayButton.js +1 -1
  116. package/lib/module/components/PlatformPayButton.js.map +1 -1
  117. package/lib/module/components/StripeContainer.js +1 -1
  118. package/lib/module/components/StripeContainer.js.map +1 -1
  119. package/lib/module/components/StripeProvider.js +1 -1
  120. package/lib/module/components/StripeProvider.js.map +1 -1
  121. package/lib/module/connect/Components.js +1 -1
  122. package/lib/module/connect/Components.js.map +1 -1
  123. package/lib/module/connect/ConnectComponentsProvider.js +1 -1
  124. package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
  125. package/lib/module/connect/EmbeddedComponent.js +1 -1
  126. package/lib/module/connect/EmbeddedComponent.js.map +1 -1
  127. package/lib/module/connect/ModalCloseButton.js +1 -1
  128. package/lib/module/connect/ModalCloseButton.js.map +1 -1
  129. package/lib/module/connect/NavigationBar.js +1 -1
  130. package/lib/module/connect/NavigationBar.js.map +1 -1
  131. package/lib/module/functions.js +1 -1
  132. package/lib/module/functions.js.map +1 -1
  133. package/lib/module/helpers.js +1 -1
  134. package/lib/module/hooks/useCheckout.js +1 -1
  135. package/lib/module/hooks/useCheckout.js.map +1 -1
  136. package/lib/module/hooks/useLinkController.js +2 -0
  137. package/lib/module/hooks/useLinkController.js.map +1 -0
  138. package/lib/module/hooks/useOnramp.js +1 -1
  139. package/lib/module/hooks/useOnramp.js.map +1 -1
  140. package/lib/module/index.js +1 -1
  141. package/lib/module/index.js.map +1 -1
  142. package/lib/module/specs/NativeAddToWalletButton.js +1 -1
  143. package/lib/module/specs/NativeAddressSheet.js +1 -1
  144. package/lib/module/specs/NativeApplePayButton.js +1 -1
  145. package/lib/module/specs/NativeAuBECSDebitForm.js +1 -1
  146. package/lib/module/specs/NativeCardField.js +1 -1
  147. package/lib/module/specs/NativeCardField.js.map +1 -1
  148. package/lib/module/specs/NativeCardForm.js +1 -1
  149. package/lib/module/specs/NativeCardForm.js.map +1 -1
  150. package/lib/module/specs/NativeConnectAccountOnboardingView.js +1 -1
  151. package/lib/module/specs/NativeCurrencySelectorElement.js +1 -1
  152. package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
  153. package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  154. package/lib/module/specs/NativeGooglePayButton.js +1 -1
  155. package/lib/module/specs/NativeNavigationBar.js +1 -1
  156. package/lib/module/specs/NativeOnrampSdkModule.js.map +1 -1
  157. package/lib/module/specs/NativePaymentMethodMessagingElement.js +1 -1
  158. package/lib/module/specs/NativeStripeContainer.js +1 -1
  159. package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
  160. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  161. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  162. package/lib/module/types/Errors.js +1 -1
  163. package/lib/module/types/Errors.js.map +1 -1
  164. package/lib/module/types/LinkController.js +2 -0
  165. package/lib/module/types/LinkController.js.map +1 -0
  166. package/lib/module/types/Onramp.js +1 -1
  167. package/lib/module/types/Onramp.js.map +1 -1
  168. package/lib/module/types/index.js +1 -1
  169. package/lib/module/types/index.js.map +1 -1
  170. package/lib/typescript/src/components/StripeProvider.d.ts.map +1 -1
  171. package/lib/typescript/src/connect/Components.d.ts +1 -1
  172. package/lib/typescript/src/connect/Components.d.ts.map +1 -1
  173. package/lib/typescript/src/functions.d.ts +19 -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/hooks/useLinkController.d.ts +28 -0
  177. package/lib/typescript/src/hooks/useLinkController.d.ts.map +1 -0
  178. package/lib/typescript/src/hooks/useOnramp.d.ts +18 -0
  179. package/lib/typescript/src/hooks/useOnramp.d.ts.map +1 -1
  180. package/lib/typescript/src/index.d.ts +1 -0
  181. package/lib/typescript/src/index.d.ts.map +1 -1
  182. package/lib/typescript/src/specs/NativeOnrampSdkModule.d.ts +2 -0
  183. package/lib/typescript/src/specs/NativeOnrampSdkModule.d.ts.map +1 -1
  184. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +5 -2
  185. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
  186. package/lib/typescript/src/types/Checkout.d.ts +1 -14
  187. package/lib/typescript/src/types/Checkout.d.ts.map +1 -1
  188. package/lib/typescript/src/types/Errors.d.ts +8 -0
  189. package/lib/typescript/src/types/Errors.d.ts.map +1 -1
  190. package/lib/typescript/src/types/LinkController.d.ts +71 -0
  191. package/lib/typescript/src/types/LinkController.d.ts.map +1 -0
  192. package/lib/typescript/src/types/Onramp.d.ts +82 -21
  193. package/lib/typescript/src/types/Onramp.d.ts.map +1 -1
  194. package/lib/typescript/src/types/index.d.ts +2 -1
  195. package/lib/typescript/src/types/index.d.ts.map +1 -1
  196. package/package.json +1 -1
  197. package/src/components/StripeProvider.tsx +0 -2
  198. package/src/connect/Components.tsx +10 -0
  199. package/src/functions.ts +48 -0
  200. package/src/hooks/useCheckout.tsx +0 -14
  201. package/src/hooks/useLinkController.tsx +54 -0
  202. package/src/hooks/useOnramp.tsx +50 -83
  203. package/src/index.tsx +1 -0
  204. package/src/specs/NativeOnrampSdkModule.ts +8 -0
  205. package/src/specs/NativeStripeSdkModule.ts +11 -7
  206. package/src/types/Checkout.ts +1 -19
  207. package/src/types/Errors.ts +9 -0
  208. package/src/types/LinkController.ts +75 -0
  209. package/src/types/Onramp.ts +98 -21
  210. package/src/types/index.ts +2 -0
  211. package/stripe-react-native.podspec +1 -1
  212. package/lib/commonjs/internal/stripeConfig.js +0 -2
  213. package/lib/commonjs/internal/stripeConfig.js.map +0 -1
  214. package/lib/module/internal/stripeConfig.js +0 -2
  215. package/lib/module/internal/stripeConfig.js.map +0 -1
  216. package/lib/typescript/src/internal/stripeConfig.d.ts +0 -3
  217. package/lib/typescript/src/internal/stripeConfig.d.ts.map +0 -1
  218. package/src/internal/stripeConfig.ts +0 -9
@@ -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
 
@@ -39,6 +39,7 @@ import com.stripe.android.crypto.onramp.model.OnrampCheckoutResult
39
39
  import com.stripe.android.crypto.onramp.model.OnrampCollectPaymentMethodResult
40
40
  import com.stripe.android.crypto.onramp.model.OnrampConfigurationResult
41
41
  import com.stripe.android.crypto.onramp.model.OnrampCreateCryptoPaymentTokenResult
42
+ import com.stripe.android.crypto.onramp.model.OnrampGetWalletOwnershipChallengeResult
42
43
  import com.stripe.android.crypto.onramp.model.OnrampUserAttestationResult
43
44
  import com.stripe.android.crypto.onramp.model.OnrampHasLinkAccountResult
44
45
  import com.stripe.android.crypto.onramp.model.OnrampLogOutResult
@@ -46,12 +47,14 @@ import com.stripe.android.crypto.onramp.model.OnrampRegisterLinkUserResult
46
47
  import com.stripe.android.crypto.onramp.model.OnrampRegisterWalletAddressResult
47
48
  import com.stripe.android.crypto.onramp.model.OnrampRetrieveMissingIdentifiersResult
48
49
  import com.stripe.android.crypto.onramp.model.OnrampSubmitIdentifiersResult
50
+ import com.stripe.android.crypto.onramp.model.OnrampSubmitWalletOwnershipSignatureResult
49
51
  import com.stripe.android.crypto.onramp.model.OnrampTokenAuthenticationResult
50
52
  import com.stripe.android.crypto.onramp.model.OnrampUpdatePhoneNumberResult
51
53
  import com.stripe.android.crypto.onramp.model.OnrampVerifyIdentityResult
52
54
  import com.stripe.android.crypto.onramp.model.OnrampVerifyKycInfoResult
53
55
  import com.stripe.android.crypto.onramp.model.PaymentMethodSelection
54
56
  import com.stripe.android.link.LinkController.PaymentMethodPreview
57
+ import com.stripe.android.link.LinkControllerPreview
55
58
  import com.stripe.android.link.PaymentMethodPreviewDetails
56
59
  import com.stripe.android.model.CardBrand
57
60
  import com.stripe.android.model.DateOfBirth
@@ -65,7 +68,7 @@ import kotlinx.coroutines.withContext
65
68
  import kotlinx.coroutines.withTimeout
66
69
 
67
70
  @SuppressLint("RestrictedApi")
68
- @OptIn(ExperimentalCryptoOnramp::class)
71
+ @OptIn(ExperimentalCryptoOnramp::class, LinkControllerPreview::class)
69
72
  @ReactModule(name = NativeOnrampSdkModuleSpec.NAME)
70
73
  class OnrampSdkModule(
71
74
  reactContext: ReactApplicationContext,
@@ -296,6 +299,66 @@ class OnrampSdkModule(
296
299
  }
297
300
  }
298
301
 
302
+ @ReactMethod
303
+ override fun getWalletOwnershipChallenge(
304
+ walletAddress: String,
305
+ network: String,
306
+ promise: Promise,
307
+ ) {
308
+ val coordinator =
309
+ onrampCoordinator ?: run {
310
+ promise.resolve(createOnrampNotConfiguredError())
311
+ return
312
+ }
313
+ CoroutineScope(Dispatchers.IO).launch {
314
+ val cryptoNetwork = enumValues<CryptoNetwork>().firstOrNull { it.value == network }
315
+ if (cryptoNetwork == null) {
316
+ promise.resolve(createError(ErrorType.Unknown.toString(), "Invalid network: $network"))
317
+ return@launch
318
+ }
319
+
320
+ when (val result = coordinator.getWalletOwnershipChallenge(walletAddress, cryptoNetwork)) {
321
+ is OnrampGetWalletOwnershipChallengeResult.Completed -> {
322
+ promise.resolve(
323
+ WritableNativeMap().apply {
324
+ putMap("challenge", mapFromWalletOwnershipChallenge(result.challenge))
325
+ },
326
+ )
327
+ }
328
+ is OnrampGetWalletOwnershipChallengeResult.Failed -> {
329
+ promise.resolve(createOnrampFailedError(result.error))
330
+ }
331
+ }
332
+ }
333
+ }
334
+
335
+ @ReactMethod
336
+ override fun submitWalletOwnershipSignature(
337
+ challengeId: String,
338
+ signature: String,
339
+ promise: Promise,
340
+ ) {
341
+ val coordinator =
342
+ onrampCoordinator ?: run {
343
+ promise.resolve(createOnrampNotConfiguredError())
344
+ return
345
+ }
346
+ CoroutineScope(Dispatchers.IO).launch {
347
+ when (val result = coordinator.submitWalletOwnershipSignature(challengeId, signature)) {
348
+ is OnrampSubmitWalletOwnershipSignatureResult.Completed -> {
349
+ promise.resolve(
350
+ WritableNativeMap().apply {
351
+ putMap("consumerWallet", mapFromCryptoConsumerWallet(result.consumerWallet))
352
+ },
353
+ )
354
+ }
355
+ is OnrampSubmitWalletOwnershipSignatureResult.Failed -> {
356
+ promise.resolve(createOnrampFailedError(result.error))
357
+ }
358
+ }
359
+ }
360
+ }
361
+
299
362
  @ReactMethod
300
363
  override fun attachKycInfo(
301
364
  kycInfo: ReadableMap,
@@ -17,9 +17,9 @@ import org.robolectric.RobolectricTestRunner
17
17
  @RunWith(RobolectricTestRunner::class)
18
18
  class OnrampErrorsTest {
19
19
  @Test
20
- fun createOnrampFailedError_withAppAttestationException_preservesOnrampDiagnostics() {
20
+ fun createOnrampFailedError_withAppAttestationException_preservesApiFields() {
21
21
  val error =
22
- createOnrampException(
22
+ createApiOnrampException(
23
23
  className = "com.stripe.android.crypto.onramp.exception.AppAttestationException",
24
24
  reason = "android_package_name_mismatch",
25
25
  operation = "configure",
@@ -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.",
@@ -78,10 +67,10 @@ class OnrampErrorsTest {
78
67
  }
79
68
 
80
69
  @Test
81
- fun createOnrampFailedError_withUncategorizedApiError_preservesStripeCodes() {
70
+ fun createOnrampFailedError_withUncategorizedApiError_preservesApiFields() {
82
71
  val error =
83
- createOnrampException(
84
- className = "com.stripe.android.crypto.onramp.exception.UncategorizedApiErrorException",
72
+ createApiOnrampException(
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,12 +93,50 @@ 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"))
96
+ assertEquals("consumer_session_expired", details.getString("reason"))
108
97
  assertEquals("req_auth", details.getString("requestId"))
109
98
  assertTrue(details.getString("developerMessage")!!.contains("Code: consumer_session_expired"))
110
99
  }
111
100
 
112
- private fun createOnrampException(
101
+ @Test
102
+ fun createOnrampFailedError_withAppAttestationUnavailableException_preservesSdkErrorFields() {
103
+ val underlyingError = IllegalStateException("Native Link is not available")
104
+ val error =
105
+ createAppAttestationUnavailableException(
106
+ underlyingError = underlyingError,
107
+ operation = "configure",
108
+ appPackageName = "com.example.app",
109
+ mode = "test",
110
+ sdkVersions =
111
+ listOf(
112
+ SDKVersion(name = "stripe-android", version = "23.10.0"),
113
+ SDKVersion(name = "stripe-react-native", version = "0.67.0"),
114
+ ),
115
+ userMessage = "This app couldn't be verified. Contact the app developer for help.",
116
+ )
117
+
118
+ val details = createOnrampFailedError(error).getMap("error")
119
+
120
+ assertNotNull(details)
121
+ assertEquals("Failed", details!!.getString("code"))
122
+ assertEquals("AppAttestationUnavailableError", details.getString("onrampErrorType"))
123
+ assertEquals(
124
+ "This app couldn't be verified. Contact the app developer for help.",
125
+ details.getString("message"),
126
+ )
127
+ assertEquals(
128
+ "This app couldn't be verified. Contact the app developer for help.",
129
+ details.getString("localizedMessage"),
130
+ )
131
+ assertEquals(
132
+ "This app couldn't be verified. Contact the app developer for help.",
133
+ details.getString("userMessage"),
134
+ )
135
+ assertEquals("app_attestation_unavailable", details.getString("stripeErrorCode"))
136
+ assertTrue(details.getString("developerMessage")!!.contains("operation: configure"))
137
+ }
138
+
139
+ private fun createApiOnrampException(
113
140
  className: String,
114
141
  reason: String,
115
142
  operation: String,
@@ -132,12 +159,9 @@ class OnrampErrorsTest {
132
159
  docUrl = docUrl,
133
160
  )
134
161
  val cause = APIException(stripeError = stripeError, requestId = requestId)
135
- val context =
162
+ val apiErrorContext =
136
163
  APIErrorContext(
137
164
  reason = reason,
138
- operation = operation,
139
- appPackageName = appPackageName,
140
- mode = mode,
141
165
  apiErrorCode = apiErrorCode,
142
166
  apiErrorType = apiErrorType,
143
167
  apiErrorMessage = apiErrorMessage,
@@ -145,24 +169,79 @@ class OnrampErrorsTest {
145
169
  docUrl = docUrl,
146
170
  underlyingError = cause,
147
171
  )
172
+ val diagnosticContext =
173
+ createDiagnosticContext(sdkVersions, operation, appPackageName, mode)
148
174
 
149
- // The exception classes in the `com.stripe.android.crypto.onramp.exception` package
150
- // don't have public constructors, so we use reflection to create instances for testing.
151
175
  val constructor =
152
176
  Class
153
177
  .forName(className)
154
178
  .getDeclaredConstructor(
155
179
  APIErrorContext::class.java,
156
- List::class.java,
180
+ diagnosticContext.javaClass,
157
181
  String::class.java,
158
182
  ).apply {
159
183
  isAccessible = true
160
184
  }
161
185
 
162
186
  return constructor.newInstance(
163
- context,
164
- sdkVersions,
187
+ apiErrorContext,
188
+ diagnosticContext,
165
189
  userMessage,
166
190
  ) as Exception
167
191
  }
192
+
193
+ private fun createAppAttestationUnavailableException(
194
+ underlyingError: Throwable,
195
+ operation: String,
196
+ appPackageName: String,
197
+ mode: String,
198
+ sdkVersions: List<SDKVersion>,
199
+ userMessage: String,
200
+ ): Exception {
201
+ val diagnosticContext =
202
+ createDiagnosticContext(sdkVersions, operation, appPackageName, mode)
203
+ val exceptionClass =
204
+ Class.forName(
205
+ "com.stripe.android.crypto.onramp.exception.AppAttestationUnavailableException",
206
+ )
207
+
208
+ val constructor =
209
+ exceptionClass
210
+ .getDeclaredConstructor(
211
+ Throwable::class.java,
212
+ diagnosticContext.javaClass,
213
+ String::class.java,
214
+ )
215
+ .apply {
216
+ isAccessible = true
217
+ }
218
+
219
+ return constructor.newInstance(
220
+ underlyingError,
221
+ diagnosticContext,
222
+ userMessage,
223
+ ) as Exception
224
+ }
225
+
226
+ private fun createDiagnosticContext(
227
+ sdkVersions: List<SDKVersion>,
228
+ operation: String,
229
+ appPackageName: String,
230
+ mode: String,
231
+ ): Any {
232
+ val diagnosticContextClass =
233
+ Class.forName("com.stripe.android.crypto.onramp.exception.DiagnosticContext")
234
+ val constructor =
235
+ diagnosticContextClass
236
+ .getDeclaredConstructor(
237
+ List::class.java,
238
+ String::class.java,
239
+ String::class.java,
240
+ String::class.java,
241
+ ).apply {
242
+ isAccessible = true
243
+ }
244
+
245
+ return constructor.newInstance(sdkVersions, operation, appPackageName, mode)
246
+ }
168
247
  }
@@ -8,7 +8,9 @@ import com.reactnativestripesdk.ComplianceIdentifierFieldException
8
8
  import com.reactnativestripesdk.InvalidIdentifiersArrayException
9
9
  import com.reactnativestripesdk.mapAppearance
10
10
  import com.reactnativestripesdk.mapConfig
11
+ import com.reactnativestripesdk.mapFromCryptoConsumerWallet
11
12
  import com.reactnativestripesdk.mapFromKycInfo
13
+ import com.reactnativestripesdk.mapFromWalletOwnershipChallenge
12
14
  import com.reactnativestripesdk.mapGooglePayConfig
13
15
  import com.reactnativestripesdk.mapPaymentDetailsType
14
16
  import com.reactnativestripesdk.mapToComplianceIdentifiers
@@ -16,6 +18,7 @@ import com.reactnativestripesdk.utils.readableArrayOf
16
18
  import com.reactnativestripesdk.utils.readableMapOf
17
19
  import com.stripe.android.core.model.CountryCode
18
20
  import com.stripe.android.crypto.onramp.ExperimentalCryptoOnramp
21
+ import com.stripe.android.crypto.onramp.model.CryptoNetwork
19
22
  import com.stripe.android.crypto.onramp.model.KycInfo
20
23
  import com.stripe.android.crypto.onramp.model.PaymentMethodDisplayData
21
24
  import com.stripe.android.googlepaylauncher.GooglePayEnvironment
@@ -593,6 +596,40 @@ class OnrampMappersTest {
593
596
  assertFalse(address.hasKey("state"))
594
597
  }
595
598
 
599
+ @Test
600
+ fun mapFromWalletOwnershipChallenge_MapsAllFields() {
601
+ val result =
602
+ mapFromWalletOwnershipChallenge(
603
+ challengeId = "cwoc_123",
604
+ walletAddress = "0x1234",
605
+ network = CryptoNetwork.Solana,
606
+ message = "Sign this exact message",
607
+ expiresAt = "2026-07-15T16:00:00Z",
608
+ )
609
+
610
+ assertEquals("cwoc_123", result.getString("challengeId"))
611
+ assertEquals("0x1234", result.getString("walletAddress"))
612
+ assertEquals("solana", result.getString("network"))
613
+ assertEquals("Sign this exact message", result.getString("message"))
614
+ assertEquals("2026-07-15T16:00:00Z", result.getString("expiresAt"))
615
+ }
616
+
617
+ @Test
618
+ fun mapFromCryptoConsumerWallet_MapsAllFields() {
619
+ val result =
620
+ mapFromCryptoConsumerWallet(
621
+ id = "ccw_123",
622
+ network = CryptoNetwork.Ethereum,
623
+ walletAddress = "0xabcd",
624
+ verifiedOwnership = true,
625
+ )
626
+
627
+ assertEquals("ccw_123", result.getString("id"))
628
+ assertEquals("0xabcd", result.getString("walletAddress"))
629
+ assertEquals("ethereum", result.getString("network"))
630
+ assertTrue(result.getBoolean("verifiedOwnership"))
631
+ }
632
+
596
633
  @Test
597
634
  fun mapToComplianceIdentifiers_ValidIdentifiers_ReturnsIdentifiers() {
598
635
  val identifiers =
@@ -0,0 +1,110 @@
1
+ package com.reactnativestripesdk.mappers
2
+
3
+ import android.annotation.SuppressLint
4
+ import com.reactnativestripesdk.utils.mapFromPaymentMethod
5
+ import com.stripe.android.model.CardBrand
6
+ import com.stripe.android.model.PaymentMethod
7
+ import org.junit.Assert.assertEquals
8
+ import org.junit.Assert.assertNotNull
9
+ import org.junit.Assert.assertNull
10
+ import org.junit.Test
11
+ import org.junit.runner.RunWith
12
+ import org.robolectric.RobolectricTestRunner
13
+
14
+ @SuppressLint("RestrictedApi")
15
+ @RunWith(RobolectricTestRunner::class)
16
+ class PaymentMethodMappersTest {
17
+ @Test
18
+ fun mapFromPaymentMethod_CardWithNetworks_MapsAvailableNetworks() {
19
+ val paymentMethod =
20
+ PaymentMethod
21
+ .Builder()
22
+ .setId("pm_test123")
23
+ .setType(PaymentMethod.Type.Card)
24
+ .setCard(
25
+ PaymentMethod.Card(
26
+ brand = CardBrand.Discover,
27
+ networks = PaymentMethod.Card.Networks(available = setOf("discover")),
28
+ ),
29
+ ).build()
30
+
31
+ val result = mapFromPaymentMethod(paymentMethod)
32
+ val availableNetworks = result.getMap("Card")?.getArray("availableNetworks")
33
+
34
+ assertNotNull(availableNetworks)
35
+ assertEquals(1, availableNetworks!!.size())
36
+ assertEquals("discover", availableNetworks.getString(0))
37
+ }
38
+
39
+ @Test
40
+ fun mapFromPaymentMethod_CardWithoutNetworks_AvailableNetworksIsNull() {
41
+ val paymentMethod =
42
+ PaymentMethod
43
+ .Builder()
44
+ .setId("pm_test123")
45
+ .setType(PaymentMethod.Type.Card)
46
+ .setCard(PaymentMethod.Card(brand = CardBrand.Visa))
47
+ .build()
48
+
49
+ val result = mapFromPaymentMethod(paymentMethod)
50
+
51
+ assertNull(result.getMap("Card")?.getArray("availableNetworks"))
52
+ }
53
+
54
+ @Test
55
+ fun mapFromPaymentMethod_USBankAccountWithNetworks_MapsSupportedNetworks() {
56
+ val paymentMethod =
57
+ PaymentMethod
58
+ .Builder()
59
+ .setId("pm_test123")
60
+ .setType(PaymentMethod.Type.USBankAccount)
61
+ .setUSBankAccount(
62
+ PaymentMethod.USBankAccount(
63
+ accountHolderType = PaymentMethod.USBankAccount.USBankAccountHolderType.INDIVIDUAL,
64
+ accountType = PaymentMethod.USBankAccount.USBankAccountType.CHECKING,
65
+ bankName = null,
66
+ fingerprint = null,
67
+ last4 = null,
68
+ financialConnectionsAccount = null,
69
+ networks =
70
+ PaymentMethod.USBankAccount.USBankNetworks(
71
+ preferred = "ach",
72
+ supported = listOf("ach"),
73
+ ),
74
+ routingNumber = null,
75
+ ),
76
+ ).build()
77
+
78
+ val result = mapFromPaymentMethod(paymentMethod)
79
+ val supportedNetworks = result.getMap("USBankAccount")?.getArray("supportedNetworks")
80
+
81
+ assertNotNull(supportedNetworks)
82
+ assertEquals(1, supportedNetworks!!.size())
83
+ assertEquals("ach", supportedNetworks.getString(0))
84
+ }
85
+
86
+ @Test
87
+ fun mapFromPaymentMethod_USBankAccountWithoutNetworks_SupportedNetworksIsNull() {
88
+ val paymentMethod =
89
+ PaymentMethod
90
+ .Builder()
91
+ .setId("pm_test123")
92
+ .setType(PaymentMethod.Type.USBankAccount)
93
+ .setUSBankAccount(
94
+ PaymentMethod.USBankAccount(
95
+ accountHolderType = PaymentMethod.USBankAccount.USBankAccountHolderType.INDIVIDUAL,
96
+ accountType = PaymentMethod.USBankAccount.USBankAccountType.CHECKING,
97
+ bankName = null,
98
+ fingerprint = null,
99
+ last4 = null,
100
+ financialConnectionsAccount = null,
101
+ networks = null,
102
+ routingNumber = null,
103
+ ),
104
+ ).build()
105
+
106
+ val result = mapFromPaymentMethod(paymentMethod)
107
+
108
+ assertNull(result.getMap("USBankAccount")?.getArray("supportedNetworks"))
109
+ }
110
+ }
@@ -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
 
package/ios/Mappers.swift CHANGED
@@ -1385,6 +1385,25 @@ class Mappers {
1385
1385
  ]
1386
1386
  }
1387
1387
 
1388
+ class func mapFromWalletOwnershipChallenge(_ challenge: WalletOwnershipChallenge) -> [String: Any] {
1389
+ [
1390
+ "challengeId": challenge.challengeId,
1391
+ "walletAddress": challenge.walletAddress,
1392
+ "network": challenge.network.rawValue,
1393
+ "message": challenge.message,
1394
+ "expiresAt": challenge.expiresAt,
1395
+ ]
1396
+ }
1397
+
1398
+ class func mapFromCryptoConsumerWallet(_ wallet: CryptoConsumerWallet) -> [String: Any] {
1399
+ [
1400
+ "id": wallet.id,
1401
+ "walletAddress": wallet.walletAddress,
1402
+ "network": wallet.network.rawValue,
1403
+ "verifiedOwnership": wallet.verifiedOwnership,
1404
+ ]
1405
+ }
1406
+
1388
1407
  private class func mapFromComplianceIdentifierRequirement(_ requirement: ComplianceIdentifierRequirement) -> [String: String] {
1389
1408
  [
1390
1409
  "type": requirement.type.rawValue,
@@ -41,7 +41,10 @@ enum OnrampErrors {
41
41
  message: onrampError.userMessage,
42
42
  localizedMessage: onrampError.localizedDescription,
43
43
  stripeErrorCode: onrampError.code,
44
- additionalFields: commonOnrampFields(error: onrampError)
44
+ additionalFields: commonOnrampFields(
45
+ error: onrampError,
46
+ onrampErrorType: onrampErrorType(for: onrampError)
47
+ )
45
48
  )
46
49
  }
47
50
 
@@ -61,16 +64,11 @@ enum OnrampErrors {
61
64
  stripeErrorCode: error.code,
62
65
  additionalFields: commonOnrampFields(error: error, onrampErrorType: onrampErrorType).merging([
63
66
  "reason": error.reason,
64
- "operation": error.operation,
65
- "appPackageName": error.appIdentifier,
66
- "mode": error.mode,
67
- "sdkVersions": mapSDKVersions(error.sdkVersions),
68
67
  "requestId": error.requestID,
69
68
  "apiErrorCode": error.code,
70
69
  "apiErrorType": error.type,
71
70
  "apiErrorMessage": error.apiMessage,
72
71
  "apiUserMessage": error.apiUserMessage,
73
- "docUrl": error.docURL?.absoluteString,
74
72
  ]) { _, new in new }
75
73
  )
76
74
  }
@@ -79,29 +77,33 @@ enum OnrampErrors {
79
77
  error: StripeCryptoOnrampError,
80
78
  onrampErrorType: String? = nil
81
79
  ) -> [String: Any?] {
82
- return [
83
- "onrampErrorType": onrampErrorType,
80
+ var fields: [String: Any?] = [
84
81
  "developerMessage": error.developerMessage,
85
82
  "userMessage": error.userMessage,
83
+ "docUrl": error.docURL?.absoluteString,
86
84
  ]
87
- }
88
85
 
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
- ]
86
+ if let onrampErrorType {
87
+ fields["onrampErrorType"] = onrampErrorType
95
88
  }
89
+
90
+ return fields
96
91
  }
97
92
 
98
93
  private static func onrampErrorType(for error: StripeCryptoOnrampAPIError) -> String {
99
- switch error {
100
- case is AppAttestationAPIError:
94
+ if error.code == "link_failed_to_attest_request" {
101
95
  return "AppAttestationError"
102
- default:
103
- return "UncategorizedApiError"
104
96
  }
97
+
98
+ return "UncategorizedApiError"
99
+ }
100
+
101
+ private static func onrampErrorType(for error: StripeCryptoOnrampError) -> String? {
102
+ if error.code == "app_attestation_unavailable" {
103
+ return "AppAttestationUnavailableError"
104
+ }
105
+
106
+ return nil
105
107
  }
106
108
 
107
109
  private static func createOnrampErrorMap(
@@ -58,6 +58,22 @@ RCT_EXPORT_METHOD(registerWalletAddress:(nonnull NSString *)address
58
58
  [StripeSdkImpl.shared registerWalletAddress:address network:network resolver:resolve rejecter:reject];
59
59
  }
60
60
 
61
+ RCT_EXPORT_METHOD(getWalletOwnershipChallenge:(nonnull NSString *)walletAddress
62
+ network:(nonnull NSString *)network
63
+ resolve:(nonnull RCTPromiseResolveBlock)resolve
64
+ reject:(nonnull RCTPromiseRejectBlock)reject)
65
+ {
66
+ [StripeSdkImpl.shared getWalletOwnershipChallenge:walletAddress network:network resolver:resolve rejecter:reject];
67
+ }
68
+
69
+ RCT_EXPORT_METHOD(submitWalletOwnershipSignature:(nonnull NSString *)challengeId
70
+ signature:(nonnull NSString *)signature
71
+ resolve:(nonnull RCTPromiseResolveBlock)resolve
72
+ reject:(nonnull RCTPromiseRejectBlock)reject)
73
+ {
74
+ [StripeSdkImpl.shared submitWalletOwnershipSignature:challengeId signature:signature resolver:resolve rejecter:reject];
75
+ }
76
+
61
77
  RCT_EXPORT_METHOD(attachKycInfo:(nonnull NSDictionary *)info
62
78
  resolve:(nonnull RCTPromiseResolveBlock)resolve
63
79
  reject:(nonnull RCTPromiseRejectBlock)reject)
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