@stripe/stripe-react-native 0.58.0 → 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 (145) hide show
  1. package/android/build.gradle +2 -0
  2. package/android/src/main/AndroidManifest.xml +27 -1
  3. package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +2 -0
  4. package/android/src/main/java/com/reactnativestripesdk/EventEmitterCompat.kt +8 -0
  5. package/android/src/main/java/com/reactnativestripesdk/PaymentElementConfig.kt +8 -0
  6. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementConfig.kt +147 -0
  7. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementView.kt +164 -0
  8. package/android/src/main/java/com/reactnativestripesdk/PaymentMethodMessagingElementViewManager.kt +65 -0
  9. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +1 -1
  10. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetManager.kt +55 -26
  11. package/android/src/main/java/com/reactnativestripesdk/StripeConnectDeepLinkInterceptorActivity.kt +77 -0
  12. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +325 -22
  13. package/android/src/main/java/com/reactnativestripesdk/StripeSdkPackage.kt +1 -0
  14. package/android/src/main/java/com/reactnativestripesdk/customersheet/CustomerSheetManager.kt +3 -0
  15. package/android/src/main/java/com/reactnativestripesdk/utils/Errors.kt +8 -0
  16. package/android/src/main/res/xml/file_paths.xml +4 -0
  17. package/android/src/oldarch/java/com/facebook/react/viewmanagers/PaymentMethodMessagingElementViewManagerDelegate.java +36 -0
  18. package/android/src/oldarch/java/com/facebook/react/viewmanagers/PaymentMethodMessagingElementViewManagerInterface.java +18 -0
  19. package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +20 -0
  20. package/android/src/test/java/com/reactnativestripesdk/PaymentElementConfigTest.kt +37 -0
  21. package/android/src/test/java/com/reactnativestripesdk/PaymentMethodMessagingElementConfigTest.kt +543 -0
  22. package/android/src/test/java/com/reactnativestripesdk/PaymentSheetManagerTest.kt +70 -0
  23. package/ios/CustomerSheet/CustomerSheetUtils.swift +4 -0
  24. package/ios/OldArch/StripeSdkEventEmitterCompat.h +2 -0
  25. package/ios/OldArch/StripeSdkEventEmitterCompat.m +13 -1
  26. package/ios/PaymentMethodMessagingElementConfig.swift +116 -0
  27. package/ios/PaymentMethodMessagingElementHandler.m +9 -0
  28. package/ios/PaymentMethodMessagingElementView.swift +139 -0
  29. package/ios/StripeSdk.mm +40 -0
  30. package/ios/StripeSdkEmitter.swift +2 -0
  31. package/ios/StripeSdkImpl+CustomerSheet.swift +1 -0
  32. package/ios/StripeSdkImpl+Embedded.swift +4 -0
  33. package/ios/StripeSdkImpl+PaymentSheet.swift +16 -0
  34. package/ios/StripeSdkImpl.swift +132 -0
  35. package/jest/mock.js +20 -0
  36. package/lib/commonjs/connect/Components.js.map +1 -1
  37. package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
  38. package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
  39. package/lib/commonjs/connect/EmbeddedComponent.js +5 -5
  40. package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
  41. package/lib/commonjs/connect/analytics/AnalyticsClient.js +2 -0
  42. package/lib/commonjs/connect/analytics/AnalyticsClient.js.map +1 -0
  43. package/lib/commonjs/connect/analytics/ComponentAnalyticsClient.js +2 -0
  44. package/lib/commonjs/connect/analytics/ComponentAnalyticsClient.js.map +1 -0
  45. package/lib/commonjs/connect/analytics/events.js +2 -0
  46. package/lib/commonjs/connect/analytics/events.js.map +1 -0
  47. package/lib/commonjs/connect/testUtils.js +2 -0
  48. package/lib/commonjs/connect/testUtils.js.map +1 -0
  49. package/lib/commonjs/events.js.map +1 -1
  50. package/lib/commonjs/functions.js +1 -1
  51. package/lib/commonjs/functions.js.map +1 -1
  52. package/lib/commonjs/hooks/useStripe.js +1 -1
  53. package/lib/commonjs/hooks/useStripe.js.map +1 -1
  54. package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js +2 -0
  55. package/lib/commonjs/specs/NativePaymentMethodMessagingElement.js.map +1 -0
  56. package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
  57. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  58. package/lib/commonjs/types/Errors.js +1 -1
  59. package/lib/commonjs/types/Errors.js.map +1 -1
  60. package/lib/commonjs/types/PaymentSheet.js.map +1 -1
  61. package/lib/commonjs/types/components/PaymentMethodMessagingElementComponent.js +2 -0
  62. package/lib/commonjs/types/components/PaymentMethodMessagingElementComponent.js.map +1 -0
  63. package/lib/commonjs/types/index.js.map +1 -1
  64. package/lib/module/connect/Components.js.map +1 -1
  65. package/lib/module/connect/ConnectComponentsProvider.js +1 -1
  66. package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
  67. package/lib/module/connect/EmbeddedComponent.js +5 -5
  68. package/lib/module/connect/EmbeddedComponent.js.map +1 -1
  69. package/lib/module/connect/analytics/AnalyticsClient.js +2 -0
  70. package/lib/module/connect/analytics/AnalyticsClient.js.map +1 -0
  71. package/lib/module/connect/analytics/ComponentAnalyticsClient.js +2 -0
  72. package/lib/module/connect/analytics/ComponentAnalyticsClient.js.map +1 -0
  73. package/lib/module/connect/analytics/events.js +2 -0
  74. package/lib/module/connect/analytics/events.js.map +1 -0
  75. package/lib/module/connect/testUtils.js +2 -0
  76. package/lib/module/connect/testUtils.js.map +1 -0
  77. package/lib/module/events.js.map +1 -1
  78. package/lib/module/functions.js +1 -1
  79. package/lib/module/functions.js.map +1 -1
  80. package/lib/module/hooks/useStripe.js +1 -1
  81. package/lib/module/hooks/useStripe.js.map +1 -1
  82. package/lib/module/specs/NativePaymentMethodMessagingElement.js +2 -0
  83. package/lib/module/specs/NativePaymentMethodMessagingElement.js.map +1 -0
  84. package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
  85. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  86. package/lib/module/types/Errors.js +1 -1
  87. package/lib/module/types/Errors.js.map +1 -1
  88. package/lib/module/types/PaymentSheet.js.map +1 -1
  89. package/lib/module/types/components/PaymentMethodMessagingElementComponent.js +2 -0
  90. package/lib/module/types/components/PaymentMethodMessagingElementComponent.js.map +1 -0
  91. package/lib/module/types/index.js.map +1 -1
  92. package/lib/typescript/src/connect/Components.d.ts +91 -0
  93. package/lib/typescript/src/connect/Components.d.ts.map +1 -1
  94. package/lib/typescript/src/connect/ConnectComponentsProvider.d.ts +61 -0
  95. package/lib/typescript/src/connect/ConnectComponentsProvider.d.ts.map +1 -1
  96. package/lib/typescript/src/connect/EmbeddedComponent.d.ts.map +1 -1
  97. package/lib/typescript/src/connect/analytics/AnalyticsClient.d.ts +32 -0
  98. package/lib/typescript/src/connect/analytics/AnalyticsClient.d.ts.map +1 -0
  99. package/lib/typescript/src/connect/analytics/ComponentAnalyticsClient.d.ts +94 -0
  100. package/lib/typescript/src/connect/analytics/ComponentAnalyticsClient.d.ts.map +1 -0
  101. package/lib/typescript/src/connect/analytics/events.d.ts +215 -0
  102. package/lib/typescript/src/connect/analytics/events.d.ts.map +1 -0
  103. package/lib/typescript/src/connect/testUtils.d.ts +45 -0
  104. package/lib/typescript/src/connect/testUtils.d.ts.map +1 -0
  105. package/lib/typescript/src/events.d.ts +2 -0
  106. package/lib/typescript/src/events.d.ts.map +1 -1
  107. package/lib/typescript/src/functions.d.ts +13 -1
  108. package/lib/typescript/src/functions.d.ts.map +1 -1
  109. package/lib/typescript/src/hooks/useStripe.d.ts +2 -1
  110. package/lib/typescript/src/hooks/useStripe.d.ts.map +1 -1
  111. package/lib/typescript/src/specs/NativePaymentMethodMessagingElement.d.ts +16 -0
  112. package/lib/typescript/src/specs/NativePaymentMethodMessagingElement.d.ts.map +1 -0
  113. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +16 -1
  114. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
  115. package/lib/typescript/src/types/CustomerSheet.d.ts +5 -0
  116. package/lib/typescript/src/types/CustomerSheet.d.ts.map +1 -1
  117. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +5 -0
  118. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
  119. package/lib/typescript/src/types/Errors.d.ts +4 -0
  120. package/lib/typescript/src/types/Errors.d.ts.map +1 -1
  121. package/lib/typescript/src/types/PaymentSheet.d.ts +5 -0
  122. package/lib/typescript/src/types/PaymentSheet.d.ts.map +1 -1
  123. package/lib/typescript/src/types/components/PaymentMethodMessagingElementComponent.d.ts +69 -0
  124. package/lib/typescript/src/types/components/PaymentMethodMessagingElementComponent.d.ts.map +1 -0
  125. package/lib/typescript/src/types/index.d.ts +8 -1
  126. package/lib/typescript/src/types/index.d.ts.map +1 -1
  127. package/package.json +1 -1
  128. package/src/connect/Components.tsx +91 -0
  129. package/src/connect/ConnectComponentsProvider.tsx +69 -2
  130. package/src/connect/EmbeddedComponent.tsx +254 -30
  131. package/src/connect/analytics/AnalyticsClient.ts +75 -0
  132. package/src/connect/analytics/ComponentAnalyticsClient.ts +315 -0
  133. package/src/connect/analytics/events.ts +253 -0
  134. package/src/connect/testUtils.ts +37 -0
  135. package/src/events.ts +2 -0
  136. package/src/functions.ts +10 -0
  137. package/src/hooks/useStripe.tsx +8 -0
  138. package/src/specs/NativePaymentMethodMessagingElement.ts +25 -0
  139. package/src/specs/NativeStripeSdkModule.ts +21 -1
  140. package/src/types/CustomerSheet.ts +5 -0
  141. package/src/types/EmbeddedPaymentElement.tsx +5 -0
  142. package/src/types/Errors.ts +5 -0
  143. package/src/types/PaymentSheet.ts +6 -1
  144. package/src/types/components/PaymentMethodMessagingElementComponent.tsx +74 -0
  145. package/src/types/index.ts +11 -0
@@ -3,6 +3,7 @@ package com.reactnativestripesdk
3
3
  import com.reactnativestripesdk.utils.PaymentSheetException
4
4
  import com.reactnativestripesdk.utils.readableArrayOf
5
5
  import com.reactnativestripesdk.utils.readableMapOf
6
+ import com.stripe.android.model.PaymentMethod
6
7
  import com.stripe.android.paymentelement.PaymentMethodOptionsSetupFutureUsagePreview
7
8
  import com.stripe.android.paymentsheet.CardFundingFilteringPrivatePreview
8
9
  import com.stripe.android.paymentsheet.PaymentSheet
@@ -1098,6 +1099,42 @@ class PaymentElementConfigTest {
1098
1099
  assertNull(result)
1099
1100
  }
1100
1101
 
1102
+ // ============================================
1103
+ // computeTermsDisplayForUserKey Tests
1104
+ // ============================================
1105
+
1106
+ @Test
1107
+ fun computeTermsDisplayForUserKey_UserKey_ReturnsCardNever() {
1108
+ val result = computeTermsDisplayForUserKey("uk_test_123")
1109
+ assertEquals(1, result.size)
1110
+ assertEquals(PaymentSheet.TermsDisplay.NEVER, result[PaymentMethod.Type.Card])
1111
+ }
1112
+
1113
+ @Test
1114
+ fun computeTermsDisplayForUserKey_LiveUserKey_ReturnsCardNever() {
1115
+ val result = computeTermsDisplayForUserKey("uk_live_456")
1116
+ assertEquals(1, result.size)
1117
+ assertEquals(PaymentSheet.TermsDisplay.NEVER, result[PaymentMethod.Type.Card])
1118
+ }
1119
+
1120
+ @Test
1121
+ fun computeTermsDisplayForUserKey_PublishableKey_ReturnsEmpty() {
1122
+ val result = computeTermsDisplayForUserKey("pk_test_123")
1123
+ assertTrue(result.isEmpty())
1124
+ }
1125
+
1126
+ @Test
1127
+ fun computeTermsDisplayForUserKey_LivePublishableKey_ReturnsEmpty() {
1128
+ val result = computeTermsDisplayForUserKey("pk_live_456")
1129
+ assertTrue(result.isEmpty())
1130
+ }
1131
+
1132
+ @Test
1133
+ fun computeTermsDisplayForUserKey_EmptyKey_ReturnsEmpty() {
1134
+ val result = computeTermsDisplayForUserKey("")
1135
+ assertTrue(result.isEmpty())
1136
+ }
1137
+
1101
1138
  @Test
1102
1139
  fun mapToAllowedCardFundingTypes_MissingAllowedCardFundingTypes_ReturnsNull() {
1103
1140
  val params =
@@ -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 @@ class CustomerSheetUtils {
21
21
  defaultBillingDetails: NSDictionary?,
22
22
  preferredNetworks: [Int]?,
23
23
  allowsRemovalOfLastSavedPaymentMethod: Bool?,
24
+ opensCardScannerAutomatically: Bool?,
24
25
  cardBrandAcceptance: PaymentSheet.CardBrandAcceptance
25
26
  ) -> CustomerSheet.Configuration {
26
27
  var config = CustomerSheet.Configuration()
@@ -59,6 +60,9 @@ class CustomerSheetUtils {
59
60
  if let allowsRemovalOfLastSavedPaymentMethod = allowsRemovalOfLastSavedPaymentMethod {
60
61
  config.allowsRemovalOfLastSavedPaymentMethod = allowsRemovalOfLastSavedPaymentMethod
61
62
  }
63
+ if let opensCardScannerAutomatically = opensCardScannerAutomatically {
64
+ config.opensCardScannerAutomatically = opensCardScannerAutomatically
65
+ }
62
66
  config.cardBrandAcceptance = cardBrandAcceptance
63
67
  return config
64
68
  }
@@ -24,5 +24,7 @@
24
24
  - (void)emitEmbeddedPaymentElementLoadingFailed:(NSDictionary *)value;
25
25
  - (void)emitOnCustomPaymentMethodConfirmHandlerCallback:(NSDictionary *)value;
26
26
  - (void)emitOnCheckoutClientSecretRequested:(NSDictionary *)value;
27
+ - (void)emitPaymentMethodMessagingElementDidUpdateHeight:(NSDictionary *)value;
28
+ - (void)emitPaymentMethodMessagingElementConfigureResult:(NSDictionary *)value;
27
29
  @end
28
30