@stripe/stripe-react-native 0.67.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 (230) hide show
  1. package/android/build.gradle +3 -3
  2. package/android/gradle.properties +1 -1
  3. package/android/proguard-rules.txt +4 -0
  4. package/android/src/main/java/com/reactnativestripesdk/CurrencySelectorAppearance.kt +229 -0
  5. package/android/src/main/java/com/reactnativestripesdk/CurrencySelectorElementView.kt +8 -1
  6. package/android/src/main/java/com/reactnativestripesdk/CurrencySelectorElementViewManager.kt +11 -0
  7. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementView.kt +0 -22
  8. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +0 -22
  9. package/android/src/main/java/com/reactnativestripesdk/LinkControllerManager.kt +175 -0
  10. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetManager.kt +11 -4
  11. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +43 -38
  12. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt +8 -0
  13. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonView.kt +6 -0
  14. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/PushProvisioningProxy.kt +6 -5
  15. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/TapAndPayProxy.kt +91 -0
  16. package/android/src/oldarch/java/com/facebook/react/viewmanagers/AddToWalletButtonManagerDelegate.java +3 -0
  17. package/android/src/oldarch/java/com/facebook/react/viewmanagers/AddToWalletButtonManagerInterface.java +1 -0
  18. package/android/src/oldarch/java/com/facebook/react/viewmanagers/EmbeddedPaymentElementViewManagerDelegate.java +0 -3
  19. package/android/src/oldarch/java/com/facebook/react/viewmanagers/EmbeddedPaymentElementViewManagerInterface.java +0 -1
  20. package/android/src/oldarch/java/com/facebook/react/viewmanagers/StripeCurrencySelectorElementManagerDelegate.java +4 -0
  21. package/android/src/oldarch/java/com/facebook/react/viewmanagers/StripeCurrencySelectorElementManagerInterface.java +2 -0
  22. package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +8 -8
  23. package/android/src/onramp/java/com/reactnativestripesdk/OnrampErrors.kt +9 -28
  24. package/android/src/onramp/java/com/reactnativestripesdk/OnrampSdkModule.kt +10 -9
  25. package/android/src/test/java/com/reactnativestripesdk/CurrencySelectorAppearanceTest.kt +137 -0
  26. package/android/src/test/java/com/reactnativestripesdk/OnrampErrorsTest.kt +20 -18
  27. package/ios/CurrencySelectorAppearance.swift +120 -0
  28. package/ios/CurrencySelectorElementManager.m +1 -0
  29. package/ios/CurrencySelectorElementView.swift +17 -2
  30. package/ios/Mappers+Checkout.swift +3 -3
  31. package/ios/NewArch/EmbeddedPaymentElementViewComponentView.mm +0 -4
  32. package/ios/NewArch/StripeCurrencySelectorElementComponentView.mm +1 -0
  33. package/ios/OnrampErrors.swift +1 -14
  34. package/ios/StripeSdk.mm +15 -22
  35. package/ios/StripeSdkImpl+Checkout.swift +15 -39
  36. package/ios/StripeSdkImpl+Embedded.swift +0 -24
  37. package/ios/StripeSdkImpl+LinkController.swift +108 -0
  38. package/ios/StripeSdkImpl+PaymentSheet.swift +1 -2
  39. package/ios/StripeSdkImpl.swift +8 -3
  40. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  41. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  42. package/lib/commonjs/components/AddressSheet.js +1 -1
  43. package/lib/commonjs/components/AddressSheet.js.map +1 -1
  44. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  45. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  46. package/lib/commonjs/components/CardField.js +1 -1
  47. package/lib/commonjs/components/CardField.js.map +1 -1
  48. package/lib/commonjs/components/CardForm.js +1 -1
  49. package/lib/commonjs/components/CardForm.js.map +1 -1
  50. package/lib/commonjs/components/CurrencySelectorElement.js +1 -1
  51. package/lib/commonjs/components/CurrencySelectorElement.js.map +1 -1
  52. package/lib/commonjs/components/PaymentMethodMessagingElement.js +1 -1
  53. package/lib/commonjs/components/PaymentMethodMessagingElement.js.map +1 -1
  54. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  55. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  56. package/lib/commonjs/components/StripeContainer.js +1 -1
  57. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  58. package/lib/commonjs/connect/Components.js +1 -1
  59. package/lib/commonjs/connect/Components.js.map +1 -1
  60. package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
  61. package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
  62. package/lib/commonjs/connect/EmbeddedComponent.js +2 -2
  63. package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
  64. package/lib/commonjs/connect/ModalCloseButton.js +1 -1
  65. package/lib/commonjs/connect/ModalCloseButton.js.map +1 -1
  66. package/lib/commonjs/connect/NavigationBar.js +1 -1
  67. package/lib/commonjs/connect/NavigationBar.js.map +1 -1
  68. package/lib/commonjs/functions.js +1 -1
  69. package/lib/commonjs/functions.js.map +1 -1
  70. package/lib/commonjs/helpers.js +1 -1
  71. package/lib/commonjs/hooks/useCheckout.js +1 -1
  72. package/lib/commonjs/hooks/useCheckout.js.map +1 -1
  73. package/lib/commonjs/hooks/useLinkController.js +2 -0
  74. package/lib/commonjs/hooks/useLinkController.js.map +1 -0
  75. package/lib/commonjs/index.js +1 -1
  76. package/lib/commonjs/index.js.map +1 -1
  77. package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
  78. package/lib/commonjs/specs/NativeAddToWalletButton.js.map +1 -1
  79. package/lib/commonjs/specs/NativeAddressSheet.js +1 -1
  80. package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
  81. package/lib/commonjs/specs/NativeAuBECSDebitForm.js +1 -1
  82. package/lib/commonjs/specs/NativeCardField.js +1 -1
  83. package/lib/commonjs/specs/NativeCardField.js.map +1 -1
  84. package/lib/commonjs/specs/NativeCardForm.js +1 -1
  85. package/lib/commonjs/specs/NativeCardForm.js.map +1 -1
  86. package/lib/commonjs/specs/NativeConnectAccountOnboardingView.js +1 -1
  87. package/lib/commonjs/specs/NativeCurrencySelectorElement.js +1 -1
  88. package/lib/commonjs/specs/NativeCurrencySelectorElement.js.map +1 -1
  89. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
  90. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  91. package/lib/commonjs/specs/NativeGooglePayButton.js +1 -1
  92. package/lib/commonjs/specs/NativeNavigationBar.js +1 -1
  93. package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js +1 -1
  94. package/lib/commonjs/specs/NativeStripeContainer.js +1 -1
  95. package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
  96. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  97. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  98. package/lib/commonjs/types/Errors.js +1 -1
  99. package/lib/commonjs/types/Errors.js.map +1 -1
  100. package/lib/commonjs/types/LinkController.js +2 -0
  101. package/lib/commonjs/types/LinkController.js.map +1 -0
  102. package/lib/commonjs/types/PushProvisioning.js.map +1 -1
  103. package/lib/commonjs/types/index.js +1 -1
  104. package/lib/commonjs/types/index.js.map +1 -1
  105. package/lib/module/components/AddToWalletButton.js +1 -1
  106. package/lib/module/components/AddToWalletButton.js.map +1 -1
  107. package/lib/module/components/AddressSheet.js +1 -1
  108. package/lib/module/components/AddressSheet.js.map +1 -1
  109. package/lib/module/components/AuBECSDebitForm.js +1 -1
  110. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  111. package/lib/module/components/CardField.js +1 -1
  112. package/lib/module/components/CardField.js.map +1 -1
  113. package/lib/module/components/CardForm.js +1 -1
  114. package/lib/module/components/CardForm.js.map +1 -1
  115. package/lib/module/components/CurrencySelectorElement.js +1 -1
  116. package/lib/module/components/CurrencySelectorElement.js.map +1 -1
  117. package/lib/module/components/PaymentMethodMessagingElement.js +1 -1
  118. package/lib/module/components/PaymentMethodMessagingElement.js.map +1 -1
  119. package/lib/module/components/PlatformPayButton.js +1 -1
  120. package/lib/module/components/PlatformPayButton.js.map +1 -1
  121. package/lib/module/components/StripeContainer.js +1 -1
  122. package/lib/module/components/StripeContainer.js.map +1 -1
  123. package/lib/module/connect/Components.js +1 -1
  124. package/lib/module/connect/Components.js.map +1 -1
  125. package/lib/module/connect/ConnectComponentsProvider.js +1 -1
  126. package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
  127. package/lib/module/connect/EmbeddedComponent.js +2 -2
  128. package/lib/module/connect/EmbeddedComponent.js.map +1 -1
  129. package/lib/module/connect/ModalCloseButton.js +1 -1
  130. package/lib/module/connect/ModalCloseButton.js.map +1 -1
  131. package/lib/module/connect/NavigationBar.js +1 -1
  132. package/lib/module/connect/NavigationBar.js.map +1 -1
  133. package/lib/module/functions.js +1 -1
  134. package/lib/module/functions.js.map +1 -1
  135. package/lib/module/helpers.js +1 -1
  136. package/lib/module/hooks/useCheckout.js +1 -1
  137. package/lib/module/hooks/useCheckout.js.map +1 -1
  138. package/lib/module/hooks/useLinkController.js +2 -0
  139. package/lib/module/hooks/useLinkController.js.map +1 -0
  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/NativeAddToWalletButton.js.map +1 -1
  144. package/lib/module/specs/NativeAddressSheet.js +1 -1
  145. package/lib/module/specs/NativeApplePayButton.js +1 -1
  146. package/lib/module/specs/NativeAuBECSDebitForm.js +1 -1
  147. package/lib/module/specs/NativeCardField.js +1 -1
  148. package/lib/module/specs/NativeCardField.js.map +1 -1
  149. package/lib/module/specs/NativeCardForm.js +1 -1
  150. package/lib/module/specs/NativeCardForm.js.map +1 -1
  151. package/lib/module/specs/NativeConnectAccountOnboardingView.js +1 -1
  152. package/lib/module/specs/NativeCurrencySelectorElement.js +1 -1
  153. package/lib/module/specs/NativeCurrencySelectorElement.js.map +1 -1
  154. package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
  155. package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  156. package/lib/module/specs/NativeGooglePayButton.js +1 -1
  157. package/lib/module/specs/NativeNavigationBar.js +1 -1
  158. package/lib/module/specs/NativePaymentMethodMessagingElement.js +1 -1
  159. package/lib/module/specs/NativeStripeContainer.js +1 -1
  160. package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
  161. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  162. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  163. package/lib/module/types/Errors.js +1 -1
  164. package/lib/module/types/Errors.js.map +1 -1
  165. package/lib/module/types/LinkController.js +2 -0
  166. package/lib/module/types/LinkController.js.map +1 -0
  167. package/lib/module/types/PushProvisioning.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/AddToWalletButton.d.ts +2 -0
  171. package/lib/typescript/src/components/AddToWalletButton.d.ts.map +1 -1
  172. package/lib/typescript/src/components/CurrencySelectorElement.d.ts +76 -1
  173. package/lib/typescript/src/components/CurrencySelectorElement.d.ts.map +1 -1
  174. package/lib/typescript/src/connect/Components.d.ts +1 -1
  175. package/lib/typescript/src/connect/Components.d.ts.map +1 -1
  176. package/lib/typescript/src/connect/EmbeddedComponent.d.ts +23 -0
  177. package/lib/typescript/src/connect/EmbeddedComponent.d.ts.map +1 -1
  178. package/lib/typescript/src/functions.d.ts +19 -1
  179. package/lib/typescript/src/functions.d.ts.map +1 -1
  180. package/lib/typescript/src/hooks/useCheckout.d.ts.map +1 -1
  181. package/lib/typescript/src/hooks/useLinkController.d.ts +28 -0
  182. package/lib/typescript/src/hooks/useLinkController.d.ts.map +1 -0
  183. package/lib/typescript/src/index.d.ts +1 -0
  184. package/lib/typescript/src/index.d.ts.map +1 -1
  185. package/lib/typescript/src/specs/NativeAddToWalletButton.d.ts +1 -0
  186. package/lib/typescript/src/specs/NativeAddToWalletButton.d.ts.map +1 -1
  187. package/lib/typescript/src/specs/NativeCurrencySelectorElement.d.ts +4 -0
  188. package/lib/typescript/src/specs/NativeCurrencySelectorElement.d.ts.map +1 -1
  189. package/lib/typescript/src/specs/NativeEmbeddedPaymentElement.d.ts +0 -1
  190. package/lib/typescript/src/specs/NativeEmbeddedPaymentElement.d.ts.map +1 -1
  191. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +5 -3
  192. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
  193. package/lib/typescript/src/types/Checkout.d.ts +1 -14
  194. package/lib/typescript/src/types/Checkout.d.ts.map +1 -1
  195. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +1 -10
  196. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
  197. package/lib/typescript/src/types/Errors.d.ts +8 -0
  198. package/lib/typescript/src/types/Errors.d.ts.map +1 -1
  199. package/lib/typescript/src/types/LinkController.d.ts +71 -0
  200. package/lib/typescript/src/types/LinkController.d.ts.map +1 -0
  201. package/lib/typescript/src/types/Onramp.d.ts +0 -13
  202. package/lib/typescript/src/types/Onramp.d.ts.map +1 -1
  203. package/lib/typescript/src/types/PaymentMethod.d.ts +2 -0
  204. package/lib/typescript/src/types/PaymentMethod.d.ts.map +1 -1
  205. package/lib/typescript/src/types/PushProvisioning.d.ts +2 -0
  206. package/lib/typescript/src/types/PushProvisioning.d.ts.map +1 -1
  207. package/lib/typescript/src/types/index.d.ts +2 -1
  208. package/lib/typescript/src/types/index.d.ts.map +1 -1
  209. package/package.json +1 -1
  210. package/src/components/AddToWalletButton.tsx +2 -0
  211. package/src/components/CurrencySelectorElement.tsx +101 -1
  212. package/src/connect/Components.tsx +10 -0
  213. package/src/connect/EmbeddedComponent.tsx +47 -10
  214. package/src/functions.ts +48 -0
  215. package/src/hooks/useCheckout.tsx +0 -14
  216. package/src/hooks/useLinkController.tsx +54 -0
  217. package/src/index.tsx +1 -0
  218. package/src/specs/NativeAddToWalletButton.ts +1 -0
  219. package/src/specs/NativeCurrencySelectorElement.ts +5 -0
  220. package/src/specs/NativeEmbeddedPaymentElement.ts +1 -10
  221. package/src/specs/NativeStripeSdkModule.ts +11 -10
  222. package/src/types/Checkout.ts +1 -19
  223. package/src/types/EmbeddedPaymentElement.tsx +4 -27
  224. package/src/types/Errors.ts +9 -0
  225. package/src/types/LinkController.ts +75 -0
  226. package/src/types/Onramp.ts +0 -14
  227. package/src/types/PaymentMethod.ts +2 -0
  228. package/src/types/PushProvisioning.ts +2 -0
  229. package/src/types/index.ts +2 -0
  230. package/stripe-react-native.podspec +1 -1
@@ -0,0 +1,137 @@
1
+ package com.reactnativestripesdk
2
+
3
+ import android.content.Context
4
+ import androidx.test.core.app.ApplicationProvider
5
+ import com.reactnativestripesdk.utils.readableMapOf
6
+ import com.stripe.android.checkout.Checkout
7
+ import com.stripe.android.paymentelement.CheckoutSessionPreview
8
+ import org.junit.Assert.assertEquals
9
+ import org.junit.Assert.assertNotNull
10
+ import org.junit.Test
11
+ import org.junit.runner.RunWith
12
+ import org.robolectric.RobolectricTestRunner
13
+
14
+ @OptIn(CheckoutSessionPreview::class)
15
+ @RunWith(RobolectricTestRunner::class)
16
+ class CurrencySelectorAppearanceTest {
17
+ private val context: Context = ApplicationProvider.getApplicationContext()
18
+
19
+ @Test
20
+ fun buildCurrencySelectorAppearance_WithNullMap_ReturnsDefaultAppearance() {
21
+ val appearance = buildCurrencySelectorAppearance(null, context)
22
+
23
+ assertNotNull(appearance)
24
+ }
25
+
26
+ @Test
27
+ fun buildCurrencySelectorAppearance_WithEmptyMap_ReturnsDefaultAppearance() {
28
+ val appearance = buildCurrencySelectorAppearance(readableMapOf(), context)
29
+
30
+ assertNotNull(appearance)
31
+ }
32
+
33
+ @Test
34
+ fun buildCurrencySelectorAppearance_WithColorStrings_Success() {
35
+ val appearance =
36
+ buildCurrencySelectorAppearance(
37
+ readableMapOf(
38
+ "background" to "#111111",
39
+ "selectedBackground" to "#222222",
40
+ "borderColor" to "#333333",
41
+ "textColor" to "#444444",
42
+ "selectedTextColor" to "#555555",
43
+ "textSecondaryColor" to "#666666",
44
+ "dangerColor" to "#777777",
45
+ ),
46
+ context,
47
+ )
48
+
49
+ assertNotNull(getField(appearance, "background"))
50
+ assertNotNull(getField(appearance, "selectedBackground"))
51
+ assertNotNull(getField(appearance, "borderColor"))
52
+ assertNotNull(getField(appearance, "textColor"))
53
+ assertNotNull(getField(appearance, "selectedTextColor"))
54
+ assertNotNull(getField(appearance, "textSecondaryColor"))
55
+ assertNotNull(getField(appearance, "dangerColor"))
56
+ }
57
+
58
+ @Test
59
+ fun buildCurrencySelectorAppearance_WithThemedColor_UsesLightColor() {
60
+ val appearance =
61
+ buildCurrencySelectorAppearance(
62
+ readableMapOf(
63
+ "textColor" to
64
+ readableMapOf(
65
+ "light" to "#000000",
66
+ "dark" to "#FFFFFF",
67
+ ),
68
+ ),
69
+ context,
70
+ )
71
+
72
+ assertNotNull(getField(appearance, "textColor"))
73
+ }
74
+
75
+ @Test
76
+ fun buildCurrencySelectorAppearance_WithInvalidColor_IgnoresValue() {
77
+ val appearance =
78
+ buildCurrencySelectorAppearance(
79
+ readableMapOf(
80
+ "textColor" to "#FF",
81
+ ),
82
+ context,
83
+ )
84
+
85
+ // Invalid hex is silently ignored (logged as warning), color remains null/default
86
+ val field = appearance.javaClass.getDeclaredField("textColor")
87
+ field.isAccessible = true
88
+ val textColor = field.get(appearance)
89
+ assertEquals(null, textColor)
90
+ }
91
+
92
+ @Test
93
+ fun buildCurrencySelectorAppearance_WithDimensionsAndFont_Success() {
94
+ val appearance =
95
+ buildCurrencySelectorAppearance(
96
+ readableMapOf(
97
+ "cornerRadius" to 12.0,
98
+ "borderWidth" to 2.0,
99
+ "contentVerticalPadding" to 6.0,
100
+ "font" to
101
+ readableMapOf(
102
+ "scale" to 1.2,
103
+ ),
104
+ ),
105
+ context,
106
+ )
107
+
108
+ assertEquals(12f, getField(appearance, "cornerRadiusDp"))
109
+ assertEquals(2f, getField(appearance, "borderWidthDp"))
110
+ assertEquals(6f, getField(appearance, "contentVerticalPaddingDp"))
111
+ assertEquals(1.2f, getField(appearance, "sizeScaleFactor"))
112
+ }
113
+
114
+ @Test
115
+ fun buildCurrencySelectorAppearance_WithLabelContentValues_Success() {
116
+ listOf("automatic", "currencyCode", "amount").forEach { labelContent ->
117
+ val appearance =
118
+ buildCurrencySelectorAppearance(
119
+ readableMapOf(
120
+ "labelContent" to labelContent,
121
+ ),
122
+ context,
123
+ )
124
+
125
+ assertNotNull(getField(appearance, "labelContent"))
126
+ }
127
+ }
128
+
129
+ private fun getField(
130
+ appearance: Checkout.CurrencySelectorContentAppearance,
131
+ fieldName: String,
132
+ ): Any? {
133
+ val field = appearance.javaClass.getDeclaredField(fieldName)
134
+ field.isAccessible = true
135
+ return field.get(appearance)
136
+ }
137
+ }
@@ -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
  }
@@ -0,0 +1,120 @@
1
+ //
2
+ // CurrencySelectorAppearance.swift
3
+ // stripe-react-native
4
+ //
5
+ // Created by Nick Porter on 6/24/26.
6
+ //
7
+
8
+ @_spi(ReactNativeSDK) import StripePaymentSheet
9
+ import UIKit
10
+
11
+ internal class CurrencySelectorAppearance {
12
+ class func buildAppearanceFromParams(params: NSDictionary?) -> Checkout.CurrencySelectorView.Appearance {
13
+ var appearance = Checkout.CurrencySelectorView.Appearance()
14
+
15
+ guard let params = params else { return appearance }
16
+
17
+ if let background = parseThemedColor(params: params, key: CurrencySelectorAppearanceKeys.BACKGROUND) {
18
+ appearance.background = background
19
+ }
20
+ if let selectedBackground = parseThemedColor(params: params, key: CurrencySelectorAppearanceKeys.SELECTED_BACKGROUND) {
21
+ appearance.selectedBackground = selectedBackground
22
+ }
23
+ if let borderColor = parseThemedColor(params: params, key: CurrencySelectorAppearanceKeys.BORDER_COLOR) {
24
+ appearance.border = borderColor
25
+ }
26
+ if let textColor = parseThemedColor(params: params, key: CurrencySelectorAppearanceKeys.TEXT_COLOR) {
27
+ appearance.text = textColor
28
+ }
29
+ if let selectedTextColor = parseThemedColor(params: params, key: CurrencySelectorAppearanceKeys.SELECTED_TEXT_COLOR) {
30
+ appearance.selectedText = selectedTextColor
31
+ }
32
+ if let textSecondaryColor = parseThemedColor(params: params, key: CurrencySelectorAppearanceKeys.TEXT_SECONDARY_COLOR) {
33
+ appearance.textSecondary = textSecondaryColor
34
+ }
35
+ if let dangerColor = parseThemedColor(params: params, key: CurrencySelectorAppearanceKeys.DANGER_COLOR) {
36
+ appearance.danger = dangerColor
37
+ }
38
+
39
+ if let cornerRadius = params[CurrencySelectorAppearanceKeys.CORNER_RADIUS] as? CGFloat {
40
+ appearance.cornerRadius = cornerRadius
41
+ }
42
+ if let borderWidth = params[CurrencySelectorAppearanceKeys.BORDER_WIDTH] as? CGFloat {
43
+ appearance.borderWidth = borderWidth
44
+ }
45
+ if let contentVerticalPadding = params[CurrencySelectorAppearanceKeys.CONTENT_VERTICAL_PADDING] as? CGFloat {
46
+ appearance.contentVerticalPadding = contentVerticalPadding
47
+ }
48
+
49
+ if let fontParams = params[CurrencySelectorAppearanceKeys.FONT] as? NSDictionary {
50
+ if let family = fontParams[CurrencySelectorAppearanceKeys.FAMILY] as? String,
51
+ let customFont = UIFont(name: family, size: appearance.font.pointSize) {
52
+ appearance.font = customFont
53
+ }
54
+ if let scale = fontParams[CurrencySelectorAppearanceKeys.SCALE] as? CGFloat {
55
+ appearance.sizeScaleFactor = scale
56
+ }
57
+ }
58
+
59
+ if let labelContent = params[CurrencySelectorAppearanceKeys.LABEL_CONTENT] as? String {
60
+ switch labelContent {
61
+ case CurrencySelectorAppearanceKeys.CURRENCY_CODE:
62
+ appearance.labelContent = .currencyCode
63
+ case CurrencySelectorAppearanceKeys.AMOUNT:
64
+ appearance.labelContent = .amount
65
+ case CurrencySelectorAppearanceKeys.AUTOMATIC:
66
+ appearance.labelContent = .automatic
67
+ default:
68
+ break
69
+ }
70
+ }
71
+
72
+ return appearance
73
+ }
74
+
75
+ private class func parseThemedColor(params: NSDictionary, key: String) -> UIColor? {
76
+ if let colorDict = params[key] as? [String: String] {
77
+ let light = colorDict[CurrencySelectorAppearanceKeys.LIGHT]
78
+ let dark = colorDict[CurrencySelectorAppearanceKeys.DARK]
79
+ guard light != nil || dark != nil else { return nil }
80
+ return UIColor { traitCollection in
81
+ let hex = traitCollection.userInterfaceStyle == .dark
82
+ ? (dark ?? light!)
83
+ : (light ?? dark!)
84
+ return UIColor(hexString: hex)
85
+ }
86
+ }
87
+
88
+ if let colorString = params[key] as? String {
89
+ return UIColor(hexString: colorString)
90
+ }
91
+
92
+ return nil
93
+ }
94
+ }
95
+
96
+ private enum CurrencySelectorAppearanceKeys {
97
+ static let LIGHT = "light"
98
+ static let DARK = "dark"
99
+
100
+ static let BACKGROUND = "background"
101
+ static let SELECTED_BACKGROUND = "selectedBackground"
102
+ static let BORDER_COLOR = "borderColor"
103
+ static let TEXT_COLOR = "textColor"
104
+ static let SELECTED_TEXT_COLOR = "selectedTextColor"
105
+ static let TEXT_SECONDARY_COLOR = "textSecondaryColor"
106
+ static let DANGER_COLOR = "dangerColor"
107
+
108
+ static let CORNER_RADIUS = "cornerRadius"
109
+ static let BORDER_WIDTH = "borderWidth"
110
+ static let CONTENT_VERTICAL_PADDING = "contentVerticalPadding"
111
+
112
+ static let FONT = "font"
113
+ static let FAMILY = "family"
114
+ static let SCALE = "scale"
115
+
116
+ static let LABEL_CONTENT = "labelContent"
117
+ static let AUTOMATIC = "automatic"
118
+ static let CURRENCY_CODE = "currencyCode"
119
+ static let AMOUNT = "amount"
120
+ }
@@ -12,5 +12,6 @@
12
12
  @interface RCT_EXTERN_MODULE(StripeCurrencySelectorElementManager, RCTViewManager)
13
13
  RCT_EXPORT_VIEW_PROPERTY(sessionKey, NSString)
14
14
  RCT_EXPORT_VIEW_PROPERTY(disabled, BOOL)
15
+ RCT_EXPORT_VIEW_PROPERTY(appearance, NSDictionary)
15
16
  RCT_EXPORT_VIEW_PROPERTY(onHeightChange, RCTDirectEventBlock)
16
17
  @end
@@ -39,6 +39,15 @@ public class StripeCurrencySelectorElementContainerView: UIView {
39
39
  }
40
40
  }
41
41
 
42
+ @objc public var appearance: NSDictionary? {
43
+ didSet {
44
+ selectorAppearance = CurrencySelectorAppearance.buildAppearanceFromParams(params: appearance)
45
+ if let checkout = attachedCheckout {
46
+ installSelectorView(for: checkout)
47
+ }
48
+ }
49
+ }
50
+
42
51
  /// Fired whenever the inner selector's intrinsic height changes so RN
43
52
  /// can match it. Session-state changes flow through the central
44
53
  /// `checkoutSessionDidChangeState` event on `StripeSdk` (see
@@ -50,6 +59,8 @@ public class StripeCurrencySelectorElementContainerView: UIView {
50
59
  private var selectorView: Checkout.CurrencySelectorView?
51
60
  private var sessionCancellable: AnyCancellable?
52
61
  private var lastReportedHeight: CGFloat = -1
62
+ private var attachedCheckout: Checkout?
63
+ private var selectorAppearance = Checkout.CurrencySelectorView.Appearance()
53
64
 
54
65
  // MARK: - Init
55
66
 
@@ -88,6 +99,7 @@ public class StripeCurrencySelectorElementContainerView: UIView {
88
99
  return
89
100
  }
90
101
 
102
+ self.attachedCheckout = checkout
91
103
  self.installSelectorView(for: checkout)
92
104
  self.subscribeForLayoutUpdates(on: checkout)
93
105
  }
@@ -98,7 +110,7 @@ public class StripeCurrencySelectorElementContainerView: UIView {
98
110
  /// so the caption-text changes after a currency selection are reflected
99
111
  /// in our reported height back to RN.
100
112
  private func subscribeForLayoutUpdates(on checkout: Checkout) {
101
- sessionCancellable = checkout.$state
113
+ sessionCancellable = checkout.$session
102
114
  .dropFirst()
103
115
  .receive(on: DispatchQueue.main)
104
116
  .sink { [weak self] _ in
@@ -110,7 +122,9 @@ public class StripeCurrencySelectorElementContainerView: UIView {
110
122
  }
111
123
 
112
124
  private func installSelectorView(for checkout: Checkout) {
113
- let view = Checkout.CurrencySelectorView(checkout: checkout)
125
+ selectorView?.removeFromSuperview()
126
+
127
+ let view = Checkout.CurrencySelectorView(checkout: checkout, appearance: selectorAppearance)
114
128
  view.translatesAutoresizingMaskIntoConstraints = false
115
129
  view.isEnabled = !disabled
116
130
  addSubview(view)
@@ -135,6 +149,7 @@ public class StripeCurrencySelectorElementContainerView: UIView {
135
149
  sessionCancellable = nil
136
150
  selectorView?.removeFromSuperview()
137
151
  selectorView = nil
152
+ attachedCheckout = nil
138
153
  lastReportedHeight = -1
139
154
  }
140
155
 
@@ -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
 
@@ -68,10 +68,6 @@ using namespace facebook::react;
68
68
  {
69
69
  }
70
70
 
71
- - (void)updateWithCheckout:(NSString *)sessionKey
72
- {
73
- }
74
-
75
71
  #pragma mark - RCTComponentViewProtocol
76
72
 
77
73
  + (ComponentDescriptorProvider)componentDescriptorProvider
@@ -64,6 +64,7 @@ using namespace facebook::react;
64
64
 
65
65
  _view.sessionKey = RCTNSStringFromStringNilIfEmpty(newViewProps.sessionKey);
66
66
  _view.disabled = newViewProps.disabled;
67
+ _view.appearance = stripe::react::convertFollyDynamicToNSDictionaryOrNil(newViewProps.appearance);
67
68
 
68
69
  [super updateProps:props oldProps:oldProps];
69
70
  }
@@ -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
@@ -578,13 +563,6 @@ RCT_EXPORT_METHOD(updateEmbeddedPaymentElement:(NSDictionary *)intentConfig
578
563
  [StripeSdkImpl.shared updateEmbeddedPaymentElement:intentConfig resolve:resolve reject:reject];
579
564
  }
580
565
 
581
- RCT_EXPORT_METHOD(updateEmbeddedPaymentElementWithCheckout:(nonnull NSString *)sessionKey
582
- resolve:(nonnull RCTPromiseResolveBlock)resolve
583
- reject:(nonnull RCTPromiseRejectBlock)reject)
584
- {
585
- [StripeSdkImpl.shared updateEmbeddedPaymentElementWithCheckout:sessionKey resolve:resolve reject:reject];
586
- }
587
-
588
566
  RCT_EXPORT_METHOD(clearEmbeddedPaymentOption:(NSInteger)viewTag
589
567
  resolve:(nonnull RCTPromiseResolveBlock)resolve
590
568
  reject:(nonnull RCTPromiseRejectBlock)reject)
@@ -606,6 +584,21 @@ RCT_EXPORT_METHOD(createRadarSession:(nonnull RCTPromiseResolveBlock)resolve
606
584
  [StripeSdkImpl.shared createRadarSession:resolve rejecter:reject];
607
585
  }
608
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
+
609
602
  /* clang-format on */
610
603
 
611
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:
@@ -213,30 +213,6 @@ extension StripeSdkImpl {
213
213
  }
214
214
  }
215
215
 
216
- @objc(updateEmbeddedPaymentElementWithCheckout:resolve:reject:)
217
- public func updateEmbeddedPaymentElementWithCheckout(
218
- sessionKey: String,
219
- resolve: @escaping RCTPromiseResolveBlock,
220
- reject: @escaping RCTPromiseRejectBlock
221
- ) {
222
- guard let checkout = checkoutInstances[sessionKey] else {
223
- resolve(Errors.createError(ErrorType.Failed, "Checkout session not found"))
224
- return
225
- }
226
-
227
- Task {
228
- guard let updateResult = await self.embeddedInstance?.update(checkout: checkout) else {
229
- resolve(Errors.createError(
230
- ErrorType.Failed,
231
- "No EmbeddedPaymentElement instance — did you call create first?"
232
- ))
233
- return
234
- }
235
-
236
- self.resolveEmbeddedUpdateResult(updateResult, resolve: resolve)
237
- }
238
- }
239
-
240
216
  @objc(clearEmbeddedPaymentOption)
241
217
  public func clearEmbeddedPaymentOption() {
242
218
  DispatchQueue.main.async {