@stripe/stripe-react-native 0.57.3 → 0.59.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 (233) hide show
  1. package/android/build.gradle +2 -0
  2. package/android/gradle.properties +1 -1
  3. package/android/src/main/AndroidManifest.xml +27 -1
  4. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementView.kt +0 -3
  5. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +7 -3
  6. package/android/src/main/java/com/reactnativestripesdk/EventEmitterCompat.kt +8 -0
  7. package/android/src/main/java/com/reactnativestripesdk/NavigationBarView.kt +12 -1
  8. package/android/src/main/java/com/reactnativestripesdk/PaymentElementConfig.kt +26 -0
  9. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementConfig.kt +147 -0
  10. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementView.kt +164 -0
  11. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementViewManager.kt +65 -0
  12. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +1 -1
  13. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetManager.kt +60 -31
  14. package/android/src/main/java/com/reactnativestripesdk/StripeAbstractComposeView.kt +17 -5
  15. package/android/src/main/java/com/reactnativestripesdk/StripeConnectDeepLinkInterceptorActivity.kt +77 -0
  16. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +334 -24
  17. package/android/src/main/java/com/reactnativestripesdk/StripeSdkPackage.kt +1 -0
  18. package/android/src/main/java/com/reactnativestripesdk/customersheet/CustomerSheetManager.kt +3 -0
  19. package/android/src/main/java/com/reactnativestripesdk/utils/Errors.kt +8 -0
  20. package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +0 -2
  21. package/android/src/main/res/xml/file_paths.xml +4 -0
  22. package/android/src/oldarch/java/com/facebook/react/viewmanagers/PaymentMethodMessagingElementViewManagerDelegate.java +36 -0
  23. package/android/src/oldarch/java/com/facebook/react/viewmanagers/PaymentMethodMessagingElementViewManagerInterface.java +18 -0
  24. package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +20 -0
  25. package/android/src/test/java/com/reactnativestripesdk/PaymentElementConfigTest.kt +175 -1
  26. package/android/src/test/java/com/reactnativestripesdk/PaymentMethodMessagingElementConfigTest.kt +543 -0
  27. package/android/src/test/java/com/reactnativestripesdk/PaymentSheetManagerTest.kt +70 -0
  28. package/ios/ConnectAccountOnboarding/ConnectAccountOnboardingView.swift +13 -19
  29. package/ios/CustomerSheet/CustomerSheetUtils.swift +4 -0
  30. package/ios/OldArch/StripeSdkEventEmitterCompat.h +2 -0
  31. package/ios/OldArch/StripeSdkEventEmitterCompat.m +13 -1
  32. package/ios/PaymentMethodMessagingElementConfig.swift +116 -0
  33. package/ios/PaymentMethodMessagingElementHandler.m +9 -0
  34. package/ios/PaymentMethodMessagingElementView.swift +139 -0
  35. package/ios/StripeSdk.mm +40 -0
  36. package/ios/StripeSdkEmitter.swift +2 -0
  37. package/ios/StripeSdkImpl+CustomerSheet.swift +1 -0
  38. package/ios/StripeSdkImpl+Embedded.swift +8 -1
  39. package/ios/StripeSdkImpl+PaymentSheet.swift +44 -1
  40. package/ios/StripeSdkImpl.swift +158 -2
  41. package/jest/mock.js +26 -0
  42. package/jest/setup.js +30 -0
  43. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  44. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  45. package/lib/commonjs/components/AddressSheet.js +1 -1
  46. package/lib/commonjs/components/AddressSheet.js.map +1 -1
  47. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  48. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  49. package/lib/commonjs/components/CardField.js +1 -1
  50. package/lib/commonjs/components/CardField.js.map +1 -1
  51. package/lib/commonjs/components/CardForm.js +1 -1
  52. package/lib/commonjs/components/CardForm.js.map +1 -1
  53. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  54. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  55. package/lib/commonjs/components/StripeContainer.js +1 -1
  56. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  57. package/lib/commonjs/connect/Components.js +1 -1
  58. package/lib/commonjs/connect/Components.js.map +1 -1
  59. package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
  60. package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
  61. package/lib/commonjs/connect/EmbeddedComponent.js +10 -5
  62. package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
  63. package/lib/commonjs/connect/ModalCloseButton.js +1 -1
  64. package/lib/commonjs/connect/ModalCloseButton.js.map +1 -1
  65. package/lib/commonjs/connect/NavigationBar.js +1 -1
  66. package/lib/commonjs/connect/NavigationBar.js.map +1 -1
  67. package/lib/commonjs/connect/analytics/AnalyticsClient.js +2 -0
  68. package/lib/commonjs/connect/analytics/AnalyticsClient.js.map +1 -0
  69. package/lib/commonjs/connect/analytics/ComponentAnalyticsClient.js +2 -0
  70. package/lib/commonjs/connect/analytics/ComponentAnalyticsClient.js.map +1 -0
  71. package/lib/commonjs/connect/analytics/events.js +2 -0
  72. package/lib/commonjs/connect/analytics/events.js.map +1 -0
  73. package/lib/commonjs/connect/testUtils.js +2 -0
  74. package/lib/commonjs/connect/testUtils.js.map +1 -0
  75. package/lib/commonjs/events.js.map +1 -1
  76. package/lib/commonjs/functions.js +1 -1
  77. package/lib/commonjs/functions.js.map +1 -1
  78. package/lib/commonjs/helpers.js +1 -1
  79. package/lib/commonjs/hooks/useStripe.js +1 -1
  80. package/lib/commonjs/hooks/useStripe.js.map +1 -1
  81. package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
  82. package/lib/commonjs/specs/NativeAddressSheet.js +1 -1
  83. package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
  84. package/lib/commonjs/specs/NativeAuBECSDebitForm.js +1 -1
  85. package/lib/commonjs/specs/NativeCardField.js +1 -1
  86. package/lib/commonjs/specs/NativeCardField.js.map +1 -1
  87. package/lib/commonjs/specs/NativeCardForm.js +1 -1
  88. package/lib/commonjs/specs/NativeCardForm.js.map +1 -1
  89. package/lib/commonjs/specs/NativeConnectAccountOnboardingView.js +1 -1
  90. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
  91. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  92. package/lib/commonjs/specs/NativeGooglePayButton.js +1 -1
  93. package/lib/commonjs/specs/NativeNavigationBar.js +1 -1
  94. package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js +2 -0
  95. package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js.map +1 -0
  96. package/lib/commonjs/specs/NativeStripeContainer.js +1 -1
  97. package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
  98. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  99. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  100. package/lib/commonjs/types/Errors.js +1 -1
  101. package/lib/commonjs/types/Errors.js.map +1 -1
  102. package/lib/commonjs/types/FinancialConnections.js.map +1 -1
  103. package/lib/commonjs/types/PaymentSheet.js +1 -1
  104. package/lib/commonjs/types/PaymentSheet.js.map +1 -1
  105. package/lib/commonjs/types/components/PaymentMethodMessagingElementComponent.js +2 -0
  106. package/lib/commonjs/types/components/PaymentMethodMessagingElementComponent.js.map +1 -0
  107. package/lib/commonjs/types/index.js.map +1 -1
  108. package/lib/module/components/AddToWalletButton.js +1 -1
  109. package/lib/module/components/AddToWalletButton.js.map +1 -1
  110. package/lib/module/components/AddressSheet.js +1 -1
  111. package/lib/module/components/AddressSheet.js.map +1 -1
  112. package/lib/module/components/AuBECSDebitForm.js +1 -1
  113. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  114. package/lib/module/components/CardField.js +1 -1
  115. package/lib/module/components/CardField.js.map +1 -1
  116. package/lib/module/components/CardForm.js +1 -1
  117. package/lib/module/components/CardForm.js.map +1 -1
  118. package/lib/module/components/PlatformPayButton.js +1 -1
  119. package/lib/module/components/PlatformPayButton.js.map +1 -1
  120. package/lib/module/components/StripeContainer.js +1 -1
  121. package/lib/module/components/StripeContainer.js.map +1 -1
  122. package/lib/module/connect/Components.js +1 -1
  123. package/lib/module/connect/Components.js.map +1 -1
  124. package/lib/module/connect/ConnectComponentsProvider.js +1 -1
  125. package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
  126. package/lib/module/connect/EmbeddedComponent.js +10 -5
  127. package/lib/module/connect/EmbeddedComponent.js.map +1 -1
  128. package/lib/module/connect/ModalCloseButton.js +1 -1
  129. package/lib/module/connect/ModalCloseButton.js.map +1 -1
  130. package/lib/module/connect/NavigationBar.js +1 -1
  131. package/lib/module/connect/NavigationBar.js.map +1 -1
  132. package/lib/module/connect/analytics/AnalyticsClient.js +2 -0
  133. package/lib/module/connect/analytics/AnalyticsClient.js.map +1 -0
  134. package/lib/module/connect/analytics/ComponentAnalyticsClient.js +2 -0
  135. package/lib/module/connect/analytics/ComponentAnalyticsClient.js.map +1 -0
  136. package/lib/module/connect/analytics/events.js +2 -0
  137. package/lib/module/connect/analytics/events.js.map +1 -0
  138. package/lib/module/connect/testUtils.js +2 -0
  139. package/lib/module/connect/testUtils.js.map +1 -0
  140. package/lib/module/events.js.map +1 -1
  141. package/lib/module/functions.js +1 -1
  142. package/lib/module/functions.js.map +1 -1
  143. package/lib/module/helpers.js +1 -1
  144. package/lib/module/hooks/useStripe.js +1 -1
  145. package/lib/module/hooks/useStripe.js.map +1 -1
  146. package/lib/module/specs/NativeAddToWalletButton.js +1 -1
  147. package/lib/module/specs/NativeAddressSheet.js +1 -1
  148. package/lib/module/specs/NativeApplePayButton.js +1 -1
  149. package/lib/module/specs/NativeAuBECSDebitForm.js +1 -1
  150. package/lib/module/specs/NativeCardField.js +1 -1
  151. package/lib/module/specs/NativeCardField.js.map +1 -1
  152. package/lib/module/specs/NativeCardForm.js +1 -1
  153. package/lib/module/specs/NativeCardForm.js.map +1 -1
  154. package/lib/module/specs/NativeConnectAccountOnboardingView.js +1 -1
  155. package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
  156. package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  157. package/lib/module/specs/NativeGooglePayButton.js +1 -1
  158. package/lib/module/specs/NativeNavigationBar.js +1 -1
  159. package/lib/module/specs/NativePaymentMethodMessagingElement.js +2 -0
  160. package/lib/module/specs/NativePaymentMethodMessagingElement.js.map +1 -0
  161. package/lib/module/specs/NativeStripeContainer.js +1 -1
  162. package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
  163. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  164. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  165. package/lib/module/types/Errors.js +1 -1
  166. package/lib/module/types/Errors.js.map +1 -1
  167. package/lib/module/types/FinancialConnections.js.map +1 -1
  168. package/lib/module/types/PaymentSheet.js +1 -1
  169. package/lib/module/types/PaymentSheet.js.map +1 -1
  170. package/lib/module/types/components/PaymentMethodMessagingElementComponent.js +2 -0
  171. package/lib/module/types/components/PaymentMethodMessagingElementComponent.js.map +1 -0
  172. package/lib/module/types/index.js.map +1 -1
  173. package/lib/typescript/src/connect/Components.d.ts +91 -0
  174. package/lib/typescript/src/connect/Components.d.ts.map +1 -1
  175. package/lib/typescript/src/connect/ConnectComponentsProvider.d.ts +61 -0
  176. package/lib/typescript/src/connect/ConnectComponentsProvider.d.ts.map +1 -1
  177. package/lib/typescript/src/connect/EmbeddedComponent.d.ts.map +1 -1
  178. package/lib/typescript/src/connect/analytics/AnalyticsClient.d.ts +32 -0
  179. package/lib/typescript/src/connect/analytics/AnalyticsClient.d.ts.map +1 -0
  180. package/lib/typescript/src/connect/analytics/ComponentAnalyticsClient.d.ts +94 -0
  181. package/lib/typescript/src/connect/analytics/ComponentAnalyticsClient.d.ts.map +1 -0
  182. package/lib/typescript/src/connect/analytics/events.d.ts +215 -0
  183. package/lib/typescript/src/connect/analytics/events.d.ts.map +1 -0
  184. package/lib/typescript/src/connect/connectTypes.d.ts +5 -1
  185. package/lib/typescript/src/connect/connectTypes.d.ts.map +1 -1
  186. package/lib/typescript/src/connect/testUtils.d.ts +45 -0
  187. package/lib/typescript/src/connect/testUtils.d.ts.map +1 -0
  188. package/lib/typescript/src/events.d.ts +2 -0
  189. package/lib/typescript/src/events.d.ts.map +1 -1
  190. package/lib/typescript/src/functions.d.ts +13 -1
  191. package/lib/typescript/src/functions.d.ts.map +1 -1
  192. package/lib/typescript/src/hooks/useStripe.d.ts +2 -1
  193. package/lib/typescript/src/hooks/useStripe.d.ts.map +1 -1
  194. package/lib/typescript/src/specs/NativePaymentMethodMessagingElement.d.ts +16 -0
  195. package/lib/typescript/src/specs/NativePaymentMethodMessagingElement.d.ts.map +1 -0
  196. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +16 -1
  197. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
  198. package/lib/typescript/src/types/CustomerSheet.d.ts +5 -0
  199. package/lib/typescript/src/types/CustomerSheet.d.ts.map +1 -1
  200. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +11 -1
  201. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
  202. package/lib/typescript/src/types/Errors.d.ts +4 -0
  203. package/lib/typescript/src/types/Errors.d.ts.map +1 -1
  204. package/lib/typescript/src/types/FinancialConnections.d.ts +2 -0
  205. package/lib/typescript/src/types/FinancialConnections.d.ts.map +1 -1
  206. package/lib/typescript/src/types/PaymentSheet.d.ts +35 -0
  207. package/lib/typescript/src/types/PaymentSheet.d.ts.map +1 -1
  208. package/lib/typescript/src/types/components/PaymentMethodMessagingElementComponent.d.ts +69 -0
  209. package/lib/typescript/src/types/components/PaymentMethodMessagingElementComponent.d.ts.map +1 -0
  210. package/lib/typescript/src/types/index.d.ts +8 -1
  211. package/lib/typescript/src/types/index.d.ts.map +1 -1
  212. package/package.json +4 -1
  213. package/src/connect/Components.tsx +109 -11
  214. package/src/connect/ConnectComponentsProvider.tsx +69 -2
  215. package/src/connect/EmbeddedComponent.tsx +458 -23
  216. package/src/connect/analytics/AnalyticsClient.ts +75 -0
  217. package/src/connect/analytics/ComponentAnalyticsClient.ts +315 -0
  218. package/src/connect/analytics/events.ts +253 -0
  219. package/src/connect/connectTypes.ts +5 -1
  220. package/src/connect/testUtils.ts +37 -0
  221. package/src/events.ts +2 -0
  222. package/src/functions.ts +10 -0
  223. package/src/hooks/useStripe.tsx +8 -0
  224. package/src/specs/NativePaymentMethodMessagingElement.ts +25 -0
  225. package/src/specs/NativeStripeSdkModule.ts +21 -1
  226. package/src/types/CustomerSheet.ts +5 -0
  227. package/src/types/EmbeddedPaymentElement.tsx +11 -1
  228. package/src/types/Errors.ts +5 -0
  229. package/src/types/FinancialConnections.ts +2 -0
  230. package/src/types/PaymentSheet.ts +38 -1
  231. package/src/types/components/PaymentMethodMessagingElementComponent.tsx +74 -0
  232. package/src/types/index.ts +11 -0
  233. package/stripe-react-native.podspec +1 -1
@@ -0,0 +1,543 @@
1
+ @file:OptIn(PaymentMethodMessagingElementPreview::class)
2
+
3
+ package com.reactnativestripesdk
4
+
5
+ import android.content.Context
6
+ import androidx.core.graphics.toColorInt
7
+ import androidx.test.core.app.ApplicationProvider
8
+ import com.reactnativestripesdk.utils.PaymentMethodMessagingElementAppearanceException
9
+ import com.reactnativestripesdk.utils.PaymentMethodMessagingElementConfigurationException
10
+ import com.reactnativestripesdk.utils.readableArrayOf
11
+ import com.reactnativestripesdk.utils.readableMapOf
12
+ import com.stripe.android.model.PaymentMethod
13
+ import com.stripe.android.paymentmethodmessaging.element.PaymentMethodMessagingElement
14
+ import com.stripe.android.paymentmethodmessaging.element.PaymentMethodMessagingElementPreview
15
+ import org.junit.Assert.assertEquals
16
+ import org.junit.Assert.assertNotNull
17
+ import org.junit.Test
18
+ import org.junit.runner.RunWith
19
+ import org.robolectric.RobolectricTestRunner
20
+
21
+ @RunWith(RobolectricTestRunner::class)
22
+ class PaymentMethodMessagingElementConfigTest {
23
+ private val context: Context = ApplicationProvider.getApplicationContext()
24
+
25
+ // ============================================
26
+ // parseElementConfiguration Tests
27
+ // ============================================
28
+
29
+ @Test(expected = PaymentMethodMessagingElementConfigurationException::class)
30
+ fun parseElementConfiguration_EmptyMap_Throws_Exception() {
31
+ val params = readableMapOf()
32
+
33
+ parseElementConfiguration(params)
34
+ }
35
+
36
+ @Test
37
+ fun parseElementConfiguration_WithAmountAndCurrency_Success() {
38
+ val params =
39
+ readableMapOf(
40
+ "amount" to 1000,
41
+ "currency" to "usd",
42
+ )
43
+
44
+ val result = parseElementConfiguration(params)
45
+
46
+ val expected =
47
+ PaymentMethodMessagingElement
48
+ .Configuration()
49
+ .amount(1000)
50
+ .currency("usd")
51
+
52
+ checkEquals(expected, result)
53
+ }
54
+
55
+ @Test
56
+ fun parseElementConfiguration_WithLocale_Success() {
57
+ val params =
58
+ readableMapOf(
59
+ "amount" to 1000,
60
+ "currency" to "usd",
61
+ "locale" to "en_US",
62
+ )
63
+
64
+ val result = parseElementConfiguration(params)
65
+ val expected =
66
+ PaymentMethodMessagingElement
67
+ .Configuration()
68
+ .amount(1000)
69
+ .currency("usd")
70
+ .locale("en_US")
71
+
72
+ checkEquals(expected, result)
73
+ }
74
+
75
+ @Test
76
+ fun parseElementConfiguration_WithCountry_Success() {
77
+ val params =
78
+ readableMapOf(
79
+ "amount" to 1000,
80
+ "currency" to "usd",
81
+ "country" to "US",
82
+ )
83
+
84
+ val result = parseElementConfiguration(params)
85
+ val expected =
86
+ PaymentMethodMessagingElement
87
+ .Configuration()
88
+ .amount(1000)
89
+ .currency("usd")
90
+ .countryCode("US")
91
+
92
+ checkEquals(expected, result)
93
+ }
94
+
95
+ @Test
96
+ fun parseElementConfiguration_WithPaymentMethodTypes_Success() {
97
+ val params =
98
+ readableMapOf(
99
+ "amount" to 1000,
100
+ "currency" to "usd",
101
+ "paymentMethodTypes" to readableArrayOf("card", "klarna", "affirm"),
102
+ )
103
+
104
+ val result = parseElementConfiguration(params)
105
+ val expected =
106
+ PaymentMethodMessagingElement
107
+ .Configuration()
108
+ .amount(1000)
109
+ .currency("usd")
110
+ .paymentMethodTypes(
111
+ listOf(PaymentMethod.Type.Card, PaymentMethod.Type.Klarna, PaymentMethod.Type.Affirm),
112
+ )
113
+
114
+ checkEquals(expected, result)
115
+ }
116
+
117
+ @Test
118
+ fun parseElementConfiguration_WithInvalidPaymentMethodTypes_FiltersInvalid() {
119
+ val params =
120
+ readableMapOf(
121
+ "amount" to 1000,
122
+ "currency" to "usd",
123
+ "paymentMethodTypes" to readableArrayOf("card", "invalid_type", "klarna"),
124
+ )
125
+
126
+ val result = parseElementConfiguration(params)
127
+ val expected =
128
+ PaymentMethodMessagingElement
129
+ .Configuration()
130
+ .amount(1000)
131
+ .currency("usd")
132
+ .paymentMethodTypes(
133
+ listOf(PaymentMethod.Type.Card, PaymentMethod.Type.Klarna),
134
+ )
135
+
136
+ checkEquals(expected, result)
137
+ }
138
+
139
+ @Test
140
+ fun parseElementConfiguration_CompleteConfiguration_Success() {
141
+ val params =
142
+ readableMapOf(
143
+ "amount" to 5000.0,
144
+ "currency" to "eur",
145
+ "locale" to "fr_FR",
146
+ "country" to "FR",
147
+ "paymentMethodTypes" to readableArrayOf("card", "klarna"),
148
+ )
149
+
150
+ val result = parseElementConfiguration(params)
151
+ val expected =
152
+ PaymentMethodMessagingElement
153
+ .Configuration()
154
+ .amount(5000)
155
+ .currency("eur")
156
+ .locale("fr_FR")
157
+ .countryCode("FR")
158
+ .paymentMethodTypes(listOf(PaymentMethod.Type.Card, PaymentMethod.Type.Klarna))
159
+
160
+ checkEquals(expected, result)
161
+ }
162
+
163
+ @Test(expected = PaymentMethodMessagingElementConfigurationException::class)
164
+ fun parseElementConfiguration_WithNullAmount_ThrowsException() {
165
+ val params =
166
+ readableMapOf(
167
+ "amount" to null,
168
+ "currency" to "usd",
169
+ )
170
+
171
+ parseElementConfiguration(params)
172
+ }
173
+
174
+ @Test(expected = PaymentMethodMessagingElementConfigurationException::class)
175
+ fun parseElementConfiguration_WithNullCurrency_ThrowsException() {
176
+ val params =
177
+ readableMapOf(
178
+ "amount" to 1000.0,
179
+ "currency" to null,
180
+ )
181
+
182
+ parseElementConfiguration(params)
183
+ }
184
+
185
+ // ============================================
186
+ // parseAppearance Tests
187
+ // ============================================
188
+
189
+ @Test
190
+ fun parseAppearance_EmptyMap_ReturnsDefaultAppearance() {
191
+ val params = readableMapOf()
192
+
193
+ val result = parseAppearance(params, context)
194
+ val expected =
195
+ PaymentMethodMessagingElement.Appearance().apply {
196
+ theme(PaymentMethodMessagingElement.Appearance.Theme.LIGHT)
197
+ colors(PaymentMethodMessagingElement.Appearance.Colors())
198
+ }
199
+
200
+ checkEquals(expected, result)
201
+ }
202
+
203
+ @Test
204
+ fun parseAppearance_WithLightStyle_Success() {
205
+ val params =
206
+ readableMapOf(
207
+ "style" to "light",
208
+ )
209
+
210
+ val result = parseAppearance(params, context)
211
+ val expected =
212
+ PaymentMethodMessagingElement.Appearance().apply {
213
+ theme(PaymentMethodMessagingElement.Appearance.Theme.LIGHT)
214
+ colors(PaymentMethodMessagingElement.Appearance.Colors())
215
+ }
216
+
217
+ checkEquals(expected, result)
218
+ }
219
+
220
+ @Test
221
+ fun parseAppearance_WithDarkStyle_Success() {
222
+ val params =
223
+ readableMapOf(
224
+ "style" to "dark",
225
+ )
226
+
227
+ val result = parseAppearance(params, context)
228
+ val expected =
229
+ PaymentMethodMessagingElement.Appearance().apply {
230
+ theme(PaymentMethodMessagingElement.Appearance.Theme.DARK)
231
+ colors(PaymentMethodMessagingElement.Appearance.Colors())
232
+ }
233
+
234
+ checkEquals(expected, result)
235
+ }
236
+
237
+ @Test
238
+ fun parseAppearance_WithFlatStyle_Success() {
239
+ val params =
240
+ readableMapOf(
241
+ "style" to "flat",
242
+ )
243
+
244
+ val result = parseAppearance(params, context)
245
+ val expected =
246
+ PaymentMethodMessagingElement.Appearance().apply {
247
+ theme(PaymentMethodMessagingElement.Appearance.Theme.FLAT)
248
+ colors(PaymentMethodMessagingElement.Appearance.Colors())
249
+ }
250
+
251
+ checkEquals(expected, result)
252
+ }
253
+
254
+ @Test
255
+ fun parseAppearance_WithInvalidStyle_DefaultsToLight() {
256
+ val params =
257
+ readableMapOf(
258
+ "style" to "invalid_style",
259
+ )
260
+
261
+ val result = parseAppearance(params, context)
262
+ val expected =
263
+ PaymentMethodMessagingElement.Appearance().apply {
264
+ theme(PaymentMethodMessagingElement.Appearance.Theme.LIGHT)
265
+ colors(PaymentMethodMessagingElement.Appearance.Colors())
266
+ }
267
+
268
+ checkEquals(expected, result)
269
+ }
270
+
271
+ @Test
272
+ fun parseAppearance_WithTextColorHex_Success() {
273
+ val params =
274
+ readableMapOf(
275
+ "textColor" to "#FF0000",
276
+ )
277
+
278
+ val result = parseAppearance(params, context)
279
+ val expected =
280
+ PaymentMethodMessagingElement.Appearance().apply {
281
+ theme(PaymentMethodMessagingElement.Appearance.Theme.LIGHT)
282
+ val colors =
283
+ PaymentMethodMessagingElement.Appearance
284
+ .Colors()
285
+ .textColor("#FF0000".toColorInt())
286
+ colors(colors)
287
+ }
288
+
289
+ checkEquals(expected, result)
290
+ }
291
+
292
+ @Test
293
+ fun parseAppearance_WithTextColorLightDarkObject_Success() {
294
+ val params =
295
+ readableMapOf(
296
+ "style" to "light",
297
+ "textColor" to
298
+ readableMapOf(
299
+ "light" to "#000000",
300
+ "dark" to "#FFFFFF",
301
+ ),
302
+ )
303
+
304
+ val result = parseAppearance(params, context)
305
+ val expected =
306
+ PaymentMethodMessagingElement.Appearance().apply {
307
+ theme(PaymentMethodMessagingElement.Appearance.Theme.LIGHT)
308
+ val colors =
309
+ PaymentMethodMessagingElement.Appearance
310
+ .Colors()
311
+ .textColor("#000000".toColorInt())
312
+ colors(colors)
313
+ }
314
+
315
+ checkEquals(expected, result)
316
+ }
317
+
318
+ @Test
319
+ fun parseAppearance_WithTextColorDarkTheme_UsesCorrectColor() {
320
+ val params =
321
+ readableMapOf(
322
+ "style" to "dark",
323
+ "textColor" to
324
+ readableMapOf(
325
+ "light" to "#000000",
326
+ "dark" to "#FFFFFF",
327
+ ),
328
+ )
329
+
330
+ val result = parseAppearance(params, context)
331
+ val expected =
332
+ PaymentMethodMessagingElement.Appearance().apply {
333
+ theme(PaymentMethodMessagingElement.Appearance.Theme.DARK)
334
+ val colors =
335
+ PaymentMethodMessagingElement.Appearance
336
+ .Colors()
337
+ .textColor("#FFFFFF".toColorInt())
338
+ colors(colors)
339
+ }
340
+
341
+ checkEquals(expected, result)
342
+ }
343
+
344
+ @Test(expected = PaymentMethodMessagingElementAppearanceException::class)
345
+ fun parseAppearance_WithInvalidTextColorHex_ThrowsException() {
346
+ val params =
347
+ readableMapOf(
348
+ "textColor" to "#FF",
349
+ )
350
+
351
+ parseAppearance(params, context)
352
+ }
353
+
354
+ @Test
355
+ fun parseAppearance_WithLinkTextColor_Success() {
356
+ val params =
357
+ readableMapOf(
358
+ "linkTextColor" to "#0000FF",
359
+ )
360
+
361
+ val result = parseAppearance(params, context)
362
+ val expected =
363
+ PaymentMethodMessagingElement.Appearance().apply {
364
+ theme(PaymentMethodMessagingElement.Appearance.Theme.LIGHT)
365
+ val colors =
366
+ PaymentMethodMessagingElement.Appearance
367
+ .Colors()
368
+ .infoIconColor("#0000FF".toColorInt())
369
+ colors(colors)
370
+ }
371
+
372
+ checkEquals(expected, result)
373
+ }
374
+
375
+ @Test
376
+ fun parseAppearance_WithLinkTextColorLightDarkObject_Success() {
377
+ val params =
378
+ readableMapOf(
379
+ "style" to "light",
380
+ "linkTextColor" to
381
+ readableMapOf(
382
+ "light" to "#0000FF",
383
+ "dark" to "#00FFFF",
384
+ ),
385
+ )
386
+
387
+ val result = parseAppearance(params, context)
388
+ val expected =
389
+ PaymentMethodMessagingElement.Appearance().apply {
390
+ theme(PaymentMethodMessagingElement.Appearance.Theme.LIGHT)
391
+ val colors =
392
+ PaymentMethodMessagingElement.Appearance
393
+ .Colors()
394
+ .infoIconColor("#0000FF".toColorInt())
395
+ colors(colors)
396
+ }
397
+
398
+ checkEquals(expected, result)
399
+ }
400
+
401
+ @Test
402
+ fun parseAppearance_WithFontScaleOnly_Success() {
403
+ val params =
404
+ readableMapOf(
405
+ "font" to
406
+ readableMapOf(
407
+ "scale" to 1.5,
408
+ ),
409
+ )
410
+
411
+ val result = parseAppearance(params, context)
412
+ val expected =
413
+ PaymentMethodMessagingElement.Appearance().apply {
414
+ theme(PaymentMethodMessagingElement.Appearance.Theme.LIGHT)
415
+ val font =
416
+ PaymentMethodMessagingElement.Appearance
417
+ .Font()
418
+ .fontFamily(null)
419
+ .fontSizeSp((16 * 1.5).toFloat())
420
+ font(font)
421
+ colors(PaymentMethodMessagingElement.Appearance.Colors())
422
+ }
423
+
424
+ checkEquals(expected, result)
425
+ }
426
+
427
+ @Test
428
+ fun parseAppearance_CompleteAppearance_Success() {
429
+ val params =
430
+ readableMapOf(
431
+ "style" to "dark",
432
+ "textColor" to "#FFFFFF",
433
+ "linkTextColor" to "#00FFFF",
434
+ "font" to
435
+ readableMapOf(
436
+ "scale" to 1.2,
437
+ ),
438
+ )
439
+
440
+ val result = parseAppearance(params, context)
441
+
442
+ // We can't easily predict the font resource ID in tests, so just verify it doesn't crash
443
+ assertNotNull(result)
444
+ }
445
+
446
+ private fun checkEquals(
447
+ expected: PaymentMethodMessagingElement.Configuration,
448
+ actual: PaymentMethodMessagingElement.Configuration,
449
+ ) {
450
+ val fieldNames = listOf("amount", "currency", "locale", "countryCode", "paymentMethodTypes")
451
+
452
+ for (fieldName in fieldNames) {
453
+ val expectedField = expected.javaClass.getDeclaredField(fieldName)
454
+ expectedField.isAccessible = true
455
+ val expectedValue = expectedField.get(expected)
456
+
457
+ val actualField = actual.javaClass.getDeclaredField(fieldName)
458
+ actualField.isAccessible = true
459
+ val actualValue = actualField.get(actual)
460
+
461
+ assertEquals(expectedValue, actualValue)
462
+ }
463
+ }
464
+
465
+ private fun checkEquals(
466
+ expected: PaymentMethodMessagingElement.Appearance,
467
+ actual: PaymentMethodMessagingElement.Appearance,
468
+ ) {
469
+ val fieldNames = listOf("theme", "font", "colors")
470
+
471
+ for (fieldName in fieldNames) {
472
+ val expectedField = expected.javaClass.getDeclaredField(fieldName)
473
+ expectedField.isAccessible = true
474
+ val expectedValue = expectedField.get(expected)
475
+
476
+ val actualField = actual.javaClass.getDeclaredField(fieldName)
477
+ actualField.isAccessible = true
478
+ val actualValue = actualField.get(actual)
479
+
480
+ when (fieldName) {
481
+ "font" -> {
482
+ if (expectedValue != null && actualValue != null) {
483
+ checkEqualsFont(
484
+ expectedValue, // as PaymentMethodMessagingElement.Appearance.Font,
485
+ actualValue, // as PaymentMethodMessagingElement.Appearance.Font
486
+ )
487
+ } else {
488
+ assertEquals(expectedValue, actualValue)
489
+ }
490
+ }
491
+ "colors" -> {
492
+ if (expectedValue != null && actualValue != null) {
493
+ checkEqualsColors(
494
+ expectedValue, // as PaymentMethodMessagingElement.Appearance.Colors,
495
+ actualValue, // as PaymentMethodMessagingElement.Appearance.Colors
496
+ )
497
+ } else {
498
+ assertEquals(expectedValue, actualValue)
499
+ }
500
+ }
501
+ else -> assertEquals(expectedValue, actualValue)
502
+ }
503
+ }
504
+ }
505
+
506
+ private fun checkEqualsFont(
507
+ expected: Any,
508
+ actual: Any,
509
+ ) {
510
+ val fieldNames = listOf("fontFamily", "fontSizeSp")
511
+
512
+ for (fieldName in fieldNames) {
513
+ val expectedField = expected.javaClass.getDeclaredField(fieldName)
514
+ expectedField.isAccessible = true
515
+ val expectedValue = expectedField.get(expected)
516
+
517
+ val actualField = actual.javaClass.getDeclaredField(fieldName)
518
+ actualField.isAccessible = true
519
+ val actualValue = actualField.get(actual)
520
+
521
+ assertEquals("Font field '$fieldName' mismatch", expectedValue, actualValue)
522
+ }
523
+ }
524
+
525
+ private fun checkEqualsColors(
526
+ expected: Any,
527
+ actual: Any,
528
+ ) {
529
+ val fieldNames = listOf("textColor", "infoIconColor")
530
+
531
+ for (fieldName in fieldNames) {
532
+ val expectedField = expected.javaClass.getDeclaredField(fieldName)
533
+ expectedField.isAccessible = true
534
+ val expectedValue = expectedField.get(expected)
535
+
536
+ val actualField = actual.javaClass.getDeclaredField(fieldName)
537
+ actualField.isAccessible = true
538
+ val actualValue = actualField.get(actual)
539
+
540
+ assertEquals("Colors field '$fieldName' mismatch", expectedValue, actualValue)
541
+ }
542
+ }
543
+ }
@@ -1,13 +1,19 @@
1
1
  package com.reactnativestripesdk
2
2
 
3
+ import com.facebook.react.bridge.Promise
4
+ import com.facebook.react.bridge.WritableMap
3
5
  import com.reactnativestripesdk.utils.readableArrayOf
4
6
  import com.reactnativestripesdk.utils.readableMapOf
5
7
  import com.stripe.android.paymentelement.PaymentMethodOptionsSetupFutureUsagePreview
6
8
  import com.stripe.android.paymentsheet.PaymentSheet
7
9
  import org.junit.Assert.assertEquals
8
10
  import org.junit.Assert.assertNotNull
11
+ import org.junit.Assert.assertTrue
9
12
  import org.junit.Test
10
13
  import org.junit.runner.RunWith
14
+ import org.mockito.ArgumentCaptor
15
+ import org.mockito.Mockito.mock
16
+ import org.mockito.Mockito.verify
11
17
  import org.robolectric.RobolectricTestRunner
12
18
 
13
19
  @RunWith(RobolectricTestRunner::class)
@@ -520,4 +526,68 @@ class PaymentSheetManagerTest {
520
526
 
521
527
  assertNotNull(result)
522
528
  }
529
+
530
+ // ============================================
531
+ // handleFlowControllerConfigured Tests
532
+ // ============================================
533
+
534
+ @Test
535
+ fun handleFlowControllerConfigured_Failure_ResolvesWithError() {
536
+ val promise = mock(Promise::class.java)
537
+ val errorMessage = "Configuration failed: invalid API key"
538
+
539
+ handleFlowControllerConfigured(
540
+ success = false,
541
+ error = IllegalArgumentException(errorMessage),
542
+ promise = promise,
543
+ flowController = null,
544
+ )
545
+
546
+ val captor = ArgumentCaptor.forClass(WritableMap::class.java)
547
+ verify(promise).resolve(captor.capture())
548
+ val resolved = captor.value
549
+ assertNotNull(resolved)
550
+ val errorMap = resolved.getMap("error")
551
+ assertNotNull(errorMap)
552
+ assertEquals(errorMessage, errorMap!!.getString("message"))
553
+ assertEquals("Failed", errorMap.getString("code"))
554
+ }
555
+
556
+ @Test
557
+ fun handleFlowControllerConfigured_FailureWithNullError_UsesDefaultMessage() {
558
+ val promise = mock(Promise::class.java)
559
+
560
+ handleFlowControllerConfigured(
561
+ success = false,
562
+ error = null,
563
+ promise = promise,
564
+ flowController = null,
565
+ )
566
+
567
+ val captor = ArgumentCaptor.forClass(WritableMap::class.java)
568
+ verify(promise).resolve(captor.capture())
569
+ val resolved = captor.value
570
+ assertNotNull(resolved)
571
+ val errorMap = resolved.getMap("error")
572
+ assertNotNull(errorMap)
573
+ assertEquals("Failed to configure payment sheet", errorMap!!.getString("message"))
574
+ }
575
+
576
+ @Test
577
+ fun handleFlowControllerConfigured_SuccessWithNoPaymentOption_ResolvesEmptyMap() {
578
+ val promise = mock(Promise::class.java)
579
+
580
+ handleFlowControllerConfigured(
581
+ success = true,
582
+ error = null,
583
+ promise = promise,
584
+ flowController = null,
585
+ )
586
+
587
+ val captor = ArgumentCaptor.forClass(WritableMap::class.java)
588
+ verify(promise).resolve(captor.capture())
589
+ val resolved = captor.value
590
+ assertNotNull(resolved)
591
+ assertTrue(!resolved.hasKey("error"))
592
+ }
523
593
  }
@@ -21,6 +21,7 @@ public class ConnectAccountOnboardingView: UIView {
21
21
  override public init(frame: CGRect) {
22
22
  super.init(frame: frame)
23
23
  super.backgroundColor = .clear
24
+ self.isHidden = true // Hide until modal animation completes
24
25
  }
25
26
 
26
27
  required init?(coder: NSCoder) {
@@ -29,10 +30,7 @@ public class ConnectAccountOnboardingView: UIView {
29
30
 
30
31
  @objc public func didSetProps() {
31
32
  if visible && !wasVisible {
32
- // Delay presentation to ensure React Native has rendered children
33
- DispatchQueue.main.async { [weak self] in
34
- self?.presentModal()
35
- }
33
+ presentModal()
36
34
  wasVisible = true
37
35
  } else if !visible && wasVisible {
38
36
  dismissModal()
@@ -47,6 +45,9 @@ public class ConnectAccountOnboardingView: UIView {
47
45
  }
48
46
 
49
47
  private func presentModal() {
48
+ // Keep view hidden during modal presentation
49
+ self.isHidden = true
50
+
50
51
  // Create the view controller that wraps THIS view
51
52
  viewController = ConnectAccountOnboardingViewController()
52
53
  viewController?.title = title
@@ -56,9 +57,6 @@ public class ConnectAccountOnboardingView: UIView {
56
57
  self?.handleClose()
57
58
  }
58
59
 
59
- // Add this entire React Native view to the view controller
60
- viewController?.setReactContentView(self)
61
-
62
60
  // Wrap in a navigation controller
63
61
  navigationController = UINavigationController(rootViewController: viewController!)
64
62
  navigationController?.modalPresentationStyle = .fullScreen
@@ -66,7 +64,14 @@ public class ConnectAccountOnboardingView: UIView {
66
64
 
67
65
  // Find the presenting view controller and present
68
66
  let presenter = findViewControllerPresenter(from: RCTKeyWindow()?.rootViewController ?? UIViewController())
69
- presenter.present(navigationController!, animated: true)
67
+
68
+ // Present the empty modal first, then add React content after animation completes
69
+ presenter.present(navigationController!, animated: true) { [weak self] in
70
+ guard let self = self else { return }
71
+ // Add React content after presentation animation finishes
72
+ self.viewController?.setReactContentView(self)
73
+ self.isHidden = false // Show the view now that modal is presented
74
+ }
70
75
  }
71
76
 
72
77
  private func dismissModal() {
@@ -83,15 +88,4 @@ public class ConnectAccountOnboardingView: UIView {
83
88
  dismissModal()
84
89
  }
85
90
 
86
- override public func didMoveToSuperview() {
87
- super.didMoveToSuperview()
88
-
89
- // When added to native view hierarchy, ensure layout is triggered
90
- if let superview = superview {
91
- // Match the superview's bounds immediately
92
- self.frame = superview.bounds
93
- setNeedsLayout()
94
- layoutIfNeeded()
95
- }
96
- }
97
91
  }