@stripe/stripe-react-native 0.66.0 → 0.68.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 (149) 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/PaymentSheetManager.kt +11 -4
  10. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +12 -25
  11. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt +8 -0
  12. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonView.kt +6 -0
  13. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/PushProvisioningProxy.kt +6 -5
  14. package/android/src/main/java/com/reactnativestripesdk/pushprovisioning/TapAndPayProxy.kt +91 -0
  15. package/android/src/main/java/com/reactnativestripesdk/utils/Errors.kt +11 -6
  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/NativeOnrampSdkModuleSpec.java +1 -1
  23. package/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +0 -8
  24. package/android/src/onramp/java/com/reactnativestripesdk/OnrampErrors.kt +145 -0
  25. package/android/src/onramp/java/com/reactnativestripesdk/OnrampMappers.kt +10 -1
  26. package/android/src/onramp/java/com/reactnativestripesdk/OnrampSdkModule.kt +52 -38
  27. package/android/src/test/java/com/reactnativestripesdk/CurrencySelectorAppearanceTest.kt +137 -0
  28. package/android/src/test/java/com/reactnativestripesdk/OnrampErrorsTest.kt +168 -0
  29. package/ios/CurrencySelectorAppearance.swift +120 -0
  30. package/ios/CurrencySelectorElementManager.m +1 -0
  31. package/ios/CurrencySelectorElementView.swift +16 -1
  32. package/ios/Mappers.swift +8 -6
  33. package/ios/NewArch/EmbeddedPaymentElementViewComponentView.mm +0 -4
  34. package/ios/NewArch/StripeCurrencySelectorElementComponentView.mm +1 -0
  35. package/ios/OnrampErrors.swift +132 -0
  36. package/ios/StripeOnrampSdk.mm +3 -3
  37. package/ios/StripeSdk.mm +0 -20
  38. package/ios/StripeSdkImpl+Checkout.swift +0 -17
  39. package/ios/StripeSdkImpl+Embedded.swift +0 -24
  40. package/ios/StripeSdkImpl+PaymentSheet.swift +1 -2
  41. package/ios/StripeSdkImpl.swift +34 -22
  42. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  43. package/lib/commonjs/components/CurrencySelectorElement.js +1 -1
  44. package/lib/commonjs/components/CurrencySelectorElement.js.map +1 -1
  45. package/lib/commonjs/components/StripeProvider.js +1 -1
  46. package/lib/commonjs/components/StripeProvider.js.map +1 -1
  47. package/lib/commonjs/connect/EmbeddedComponent.js +2 -2
  48. package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
  49. package/lib/commonjs/hooks/useCheckout.js +1 -1
  50. package/lib/commonjs/hooks/useCheckout.js.map +1 -1
  51. package/lib/commonjs/hooks/useOnramp.js +1 -1
  52. package/lib/commonjs/hooks/useOnramp.js.map +1 -1
  53. package/lib/commonjs/internal/stripeConfig.js +2 -0
  54. package/lib/commonjs/internal/stripeConfig.js.map +1 -0
  55. package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
  56. package/lib/commonjs/specs/NativeAddToWalletButton.js.map +1 -1
  57. package/lib/commonjs/specs/NativeCurrencySelectorElement.js +1 -1
  58. package/lib/commonjs/specs/NativeCurrencySelectorElement.js.map +1 -1
  59. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
  60. package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  61. package/lib/commonjs/specs/NativeStripeSdkModule.js.map +1 -1
  62. package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
  63. package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
  64. package/lib/commonjs/types/Errors.js +1 -1
  65. package/lib/commonjs/types/Errors.js.map +1 -1
  66. package/lib/commonjs/types/Onramp.js +1 -1
  67. package/lib/commonjs/types/Onramp.js.map +1 -1
  68. package/lib/commonjs/types/PushProvisioning.js.map +1 -1
  69. package/lib/module/components/AddToWalletButton.js.map +1 -1
  70. package/lib/module/components/CurrencySelectorElement.js +1 -1
  71. package/lib/module/components/CurrencySelectorElement.js.map +1 -1
  72. package/lib/module/components/StripeProvider.js +1 -1
  73. package/lib/module/components/StripeProvider.js.map +1 -1
  74. package/lib/module/connect/EmbeddedComponent.js +2 -2
  75. package/lib/module/connect/EmbeddedComponent.js.map +1 -1
  76. package/lib/module/hooks/useCheckout.js +1 -1
  77. package/lib/module/hooks/useCheckout.js.map +1 -1
  78. package/lib/module/hooks/useOnramp.js +1 -1
  79. package/lib/module/hooks/useOnramp.js.map +1 -1
  80. package/lib/module/internal/stripeConfig.js +2 -0
  81. package/lib/module/internal/stripeConfig.js.map +1 -0
  82. package/lib/module/specs/NativeAddToWalletButton.js +1 -1
  83. package/lib/module/specs/NativeAddToWalletButton.js.map +1 -1
  84. package/lib/module/specs/NativeCurrencySelectorElement.js +1 -1
  85. package/lib/module/specs/NativeCurrencySelectorElement.js.map +1 -1
  86. package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
  87. package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
  88. package/lib/module/specs/NativeStripeSdkModule.js.map +1 -1
  89. package/lib/module/types/EmbeddedPaymentElement.js +1 -1
  90. package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
  91. package/lib/module/types/Errors.js +1 -1
  92. package/lib/module/types/Errors.js.map +1 -1
  93. package/lib/module/types/Onramp.js +1 -1
  94. package/lib/module/types/Onramp.js.map +1 -1
  95. package/lib/module/types/PushProvisioning.js.map +1 -1
  96. package/lib/typescript/src/components/AddToWalletButton.d.ts +2 -0
  97. package/lib/typescript/src/components/AddToWalletButton.d.ts.map +1 -1
  98. package/lib/typescript/src/components/CurrencySelectorElement.d.ts +76 -1
  99. package/lib/typescript/src/components/CurrencySelectorElement.d.ts.map +1 -1
  100. package/lib/typescript/src/components/StripeProvider.d.ts.map +1 -1
  101. package/lib/typescript/src/connect/EmbeddedComponent.d.ts +23 -0
  102. package/lib/typescript/src/connect/EmbeddedComponent.d.ts.map +1 -1
  103. package/lib/typescript/src/hooks/useCheckout.d.ts.map +1 -1
  104. package/lib/typescript/src/hooks/useOnramp.d.ts +13 -13
  105. package/lib/typescript/src/hooks/useOnramp.d.ts.map +1 -1
  106. package/lib/typescript/src/internal/stripeConfig.d.ts +3 -0
  107. package/lib/typescript/src/internal/stripeConfig.d.ts.map +1 -0
  108. package/lib/typescript/src/specs/NativeAddToWalletButton.d.ts +1 -0
  109. package/lib/typescript/src/specs/NativeAddToWalletButton.d.ts.map +1 -1
  110. package/lib/typescript/src/specs/NativeCurrencySelectorElement.d.ts +4 -0
  111. package/lib/typescript/src/specs/NativeCurrencySelectorElement.d.ts.map +1 -1
  112. package/lib/typescript/src/specs/NativeEmbeddedPaymentElement.d.ts +0 -1
  113. package/lib/typescript/src/specs/NativeEmbeddedPaymentElement.d.ts.map +1 -1
  114. package/lib/typescript/src/specs/NativeOnrampSdkModule.d.ts +1 -1
  115. package/lib/typescript/src/specs/NativeOnrampSdkModule.d.ts.map +1 -1
  116. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts +0 -2
  117. package/lib/typescript/src/specs/NativeStripeSdkModule.d.ts.map +1 -1
  118. package/lib/typescript/src/types/Checkout.d.ts +0 -8
  119. package/lib/typescript/src/types/Checkout.d.ts.map +1 -1
  120. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +1 -10
  121. package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
  122. package/lib/typescript/src/types/Errors.d.ts +0 -5
  123. package/lib/typescript/src/types/Errors.d.ts.map +1 -1
  124. package/lib/typescript/src/types/Onramp.d.ts +84 -19
  125. package/lib/typescript/src/types/Onramp.d.ts.map +1 -1
  126. package/lib/typescript/src/types/PaymentMethod.d.ts +2 -0
  127. package/lib/typescript/src/types/PaymentMethod.d.ts.map +1 -1
  128. package/lib/typescript/src/types/PushProvisioning.d.ts +2 -0
  129. package/lib/typescript/src/types/PushProvisioning.d.ts.map +1 -1
  130. package/package.json +1 -1
  131. package/src/components/AddToWalletButton.tsx +2 -0
  132. package/src/components/CurrencySelectorElement.tsx +101 -1
  133. package/src/components/StripeProvider.tsx +2 -0
  134. package/src/connect/EmbeddedComponent.tsx +47 -10
  135. package/src/hooks/useCheckout.tsx +0 -9
  136. package/src/hooks/useOnramp.tsx +101 -18
  137. package/src/internal/stripeConfig.ts +9 -0
  138. package/src/specs/NativeAddToWalletButton.ts +1 -0
  139. package/src/specs/NativeCurrencySelectorElement.ts +5 -0
  140. package/src/specs/NativeEmbeddedPaymentElement.ts +1 -10
  141. package/src/specs/NativeOnrampSdkModule.ts +1 -1
  142. package/src/specs/NativeStripeSdkModule.ts +0 -9
  143. package/src/types/Checkout.ts +0 -9
  144. package/src/types/EmbeddedPaymentElement.tsx +4 -27
  145. package/src/types/Errors.ts +0 -6
  146. package/src/types/Onramp.ts +93 -19
  147. package/src/types/PaymentMethod.ts +2 -0
  148. package/src/types/PushProvisioning.ts +2 -0
  149. package/stripe-react-native.podspec +3 -3
@@ -164,11 +164,11 @@ dependencies {
164
164
  implementation("com.stripe:stripe-android:$stripeVersion")
165
165
  implementation("com.stripe:financial-connections:$stripeVersion")
166
166
  implementation("com.stripe:payment-method-messaging:$stripeVersion")
167
-
167
+
168
168
  if (isOnrampIncluded()) {
169
169
  implementation("com.stripe:crypto-onramp:$stripeVersion")
170
170
  }
171
-
171
+
172
172
  implementation "com.google.android.material:material:1.3.0"
173
173
  implementation "androidx.appcompat:appcompat:1.4.1"
174
174
  implementation "androidx.legacy:legacy-support-v4:1.0.0"
@@ -179,7 +179,7 @@ dependencies {
179
179
  compileOnly "com.google.android.gms:play-services-wallet:19.3.0"
180
180
 
181
181
  // Users need to declare this dependency on their own, otherwise all methods are a no-op
182
- compileOnly "com.stripe:stripe-android-issuing-push-provisioning:1.1.0"
182
+ compileOnly "com.stripe:stripe-android-issuing-push-provisioning:1.3.0"
183
183
 
184
184
  testImplementation "junit:junit:4.13"
185
185
  testImplementation "org.mockito:mockito-core:3.+"
@@ -3,4 +3,4 @@ StripeSdk_compileSdkVersion=36
3
3
  StripeSdk_targetSdkVersion=36
4
4
  StripeSdk_minSdkVersion=23
5
5
  # Keep StripeSdk_stripeVersion in sync with https://github.com/stripe/stripe-identity-react-native/blob/main/android/gradle.properties
6
- StripeSdk_stripeVersion=23.9.+
6
+ StripeSdk_stripeVersion=23.11.0
@@ -5,3 +5,7 @@
5
5
  -keepclassmembers class com.stripe.android.pushProvisioning.** {
6
6
  public *;
7
7
  }
8
+
9
+ # play-services-tapandpay is a private Google SDK that must be provided by the app.
10
+ # Suppress R8 warnings when it is absent (e.g. in the example app build).
11
+ -dontwarn com.google.android.gms.tapandpay.**
@@ -0,0 +1,229 @@
1
+ package com.reactnativestripesdk
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
5
+ import android.content.res.Configuration
6
+ import androidx.compose.ui.graphics.Color
7
+ import androidx.core.graphics.toColorInt
8
+ import com.facebook.react.bridge.ReadableMap
9
+ import com.facebook.react.bridge.ReadableType
10
+ import com.reactnativestripesdk.utils.PaymentSheetAppearanceException
11
+ import com.stripe.android.checkout.Checkout
12
+ import com.stripe.android.paymentelement.CheckoutSessionPreview
13
+
14
+ @OptIn(CheckoutSessionPreview::class)
15
+ @SuppressLint("RestrictedApi")
16
+ fun buildCurrencySelectorAppearance(
17
+ params: ReadableMap?,
18
+ context: Context,
19
+ ): Checkout.CurrencySelectorContentAppearance {
20
+ val appearance = Checkout.CurrencySelectorContentAppearance()
21
+
22
+ params ?: return appearance
23
+
24
+ applyCurrencySelectorColors(appearance, params, context)
25
+ applyCurrencySelectorDimensions(appearance, params)
26
+ applyCurrencySelectorFont(appearance, params, context)
27
+ applyCurrencySelectorLabelContent(appearance, params)
28
+
29
+ return appearance
30
+ }
31
+
32
+ @OptIn(CheckoutSessionPreview::class)
33
+ @SuppressLint("RestrictedApi")
34
+ private fun applyCurrencySelectorColors(
35
+ appearance: Checkout.CurrencySelectorContentAppearance,
36
+ params: ReadableMap,
37
+ context: Context,
38
+ ) {
39
+ dynamicColorFromParams(context, params, CurrencySelectorAppearanceKeys.BACKGROUND)?.let {
40
+ appearance.background(it)
41
+ }
42
+ dynamicColorFromParams(context, params, CurrencySelectorAppearanceKeys.SELECTED_BACKGROUND)?.let {
43
+ appearance.selectedBackground(it)
44
+ }
45
+ dynamicColorFromParams(context, params, CurrencySelectorAppearanceKeys.BORDER_COLOR)?.let {
46
+ appearance.borderColor(it)
47
+ }
48
+ dynamicColorFromParams(context, params, CurrencySelectorAppearanceKeys.TEXT_COLOR)?.let {
49
+ appearance.textColor(it)
50
+ }
51
+ dynamicColorFromParams(context, params, CurrencySelectorAppearanceKeys.SELECTED_TEXT_COLOR)?.let {
52
+ appearance.selectedTextColor(it)
53
+ }
54
+ dynamicColorFromParams(context, params, CurrencySelectorAppearanceKeys.TEXT_SECONDARY_COLOR)?.let {
55
+ appearance.textSecondaryColor(it)
56
+ }
57
+ dynamicColorFromParams(context, params, CurrencySelectorAppearanceKeys.DANGER_COLOR)?.let {
58
+ appearance.dangerColor(it)
59
+ }
60
+ }
61
+
62
+ @OptIn(CheckoutSessionPreview::class)
63
+ @SuppressLint("RestrictedApi")
64
+ private fun applyCurrencySelectorDimensions(
65
+ appearance: Checkout.CurrencySelectorContentAppearance,
66
+ params: ReadableMap,
67
+ ) {
68
+ getFloatOrNull(params, CurrencySelectorAppearanceKeys.CORNER_RADIUS)?.let {
69
+ appearance.cornerRadiusDp(it)
70
+ }
71
+ getFloatOrNull(params, CurrencySelectorAppearanceKeys.BORDER_WIDTH)?.let {
72
+ appearance.borderWidthDp(it)
73
+ }
74
+ getFloatOrNull(params, CurrencySelectorAppearanceKeys.CONTENT_VERTICAL_PADDING)?.let {
75
+ appearance.contentVerticalPaddingDp(it)
76
+ }
77
+ }
78
+
79
+ @OptIn(CheckoutSessionPreview::class)
80
+ @SuppressLint("RestrictedApi")
81
+ private fun applyCurrencySelectorFont(
82
+ appearance: Checkout.CurrencySelectorContentAppearance,
83
+ params: ReadableMap,
84
+ context: Context,
85
+ ) {
86
+ params.getMap(CurrencySelectorAppearanceKeys.FONT)?.let { font ->
87
+ getCurrencySelectorFontResId(font, CurrencySelectorAppearanceKeys.FAMILY, context)?.let {
88
+ appearance.fontResId(it)
89
+ }
90
+ getFloatOrNull(font, CurrencySelectorAppearanceKeys.SCALE)?.let {
91
+ appearance.sizeScaleFactor(it)
92
+ }
93
+ }
94
+ }
95
+
96
+ @OptIn(CheckoutSessionPreview::class)
97
+ @SuppressLint("RestrictedApi")
98
+ private fun applyCurrencySelectorLabelContent(
99
+ appearance: Checkout.CurrencySelectorContentAppearance,
100
+ params: ReadableMap,
101
+ ) {
102
+ when (params.getString(CurrencySelectorAppearanceKeys.LABEL_CONTENT)) {
103
+ CurrencySelectorAppearanceKeys.AMOUNT -> {
104
+ appearance.labelContent(Checkout.CurrencySelectorContentAppearance.LabelContent.AMOUNT)
105
+ }
106
+ CurrencySelectorAppearanceKeys.CURRENCY_CODE -> {
107
+ appearance.labelContent(Checkout.CurrencySelectorContentAppearance.LabelContent.CURRENCY_CODE)
108
+ }
109
+ CurrencySelectorAppearanceKeys.AUTOMATIC -> {
110
+ appearance.labelContent(Checkout.CurrencySelectorContentAppearance.LabelContent.AUTOMATIC)
111
+ }
112
+ }
113
+ }
114
+
115
+ private fun dynamicColorFromParams(
116
+ context: Context,
117
+ params: ReadableMap?,
118
+ key: String,
119
+ ): Color? {
120
+ if (params == null) {
121
+ return null
122
+ }
123
+
124
+ if (params.hasKey(key) && params.getType(key) == ReadableType.Map) {
125
+ val colorMap = params.getMap(key)
126
+ val isDark =
127
+ (
128
+ context.resources.configuration.uiMode
129
+ and Configuration.UI_MODE_NIGHT_MASK
130
+ ) == Configuration.UI_MODE_NIGHT_YES
131
+
132
+ val hex =
133
+ if (isDark) {
134
+ colorMap?.getString(CurrencySelectorAppearanceKeys.DARK)
135
+ } else {
136
+ colorMap?.getString(CurrencySelectorAppearanceKeys.LIGHT)
137
+ }
138
+
139
+ return colorFromHex(hex)?.let { Color(it) }
140
+ }
141
+
142
+ return colorFromHex(params.getString(key))?.let { Color(it) }
143
+ }
144
+
145
+ private fun colorFromHex(hexString: String?): Int? =
146
+ hexString?.trim()?.replace("#", "")?.let {
147
+ if (it.length == HEX_COLOR_LENGTH_RGB || it.length == HEX_COLOR_LENGTH_ARGB) {
148
+ "#$it".toColorInt()
149
+ } else {
150
+ android.util.Log.w(
151
+ "StripeCurrencySelector",
152
+ "Invalid hex color: expected length 6 or 8, but received: $it",
153
+ )
154
+ null
155
+ }
156
+ }
157
+
158
+ private fun getFloatOrNull(
159
+ map: ReadableMap?,
160
+ key: String,
161
+ ): Float? =
162
+ if (map?.hasKey(key) == true && map.getType(key) == ReadableType.Number) {
163
+ map.getDouble(key).toFloat()
164
+ } else {
165
+ null
166
+ }
167
+
168
+ @SuppressLint("DiscouragedApi")
169
+ private fun getCurrencySelectorFontResId(
170
+ map: ReadableMap?,
171
+ key: String,
172
+ context: Context,
173
+ ): Int? {
174
+ val fontErrorPrefix = "Encountered an error when setting a custom font:"
175
+ if (map?.hasKey(key) != true) {
176
+ return null
177
+ }
178
+
179
+ val fontFileName = map.getString(key)
180
+ val validationError =
181
+ when {
182
+ fontFileName == null ->
183
+ "$fontErrorPrefix expected String for font.$key, but received null."
184
+ Regex("[^a-z0-9_]").containsMatchIn(fontFileName) ->
185
+ "$fontErrorPrefix appearance.font.$key should only contain " +
186
+ "lowercase alphanumeric characters and underscores on Android, " +
187
+ "but received '$fontFileName'. " +
188
+ "This value must match the filename in android/app/src/main/res/font"
189
+ else -> null
190
+ }
191
+ if (validationError != null) {
192
+ throw PaymentSheetAppearanceException(validationError)
193
+ }
194
+
195
+ val id = context.resources.getIdentifier(fontFileName, "font", context.packageName)
196
+ if (id == 0) {
197
+ throw PaymentSheetAppearanceException("$fontErrorPrefix Failed to find font: $fontFileName")
198
+ }
199
+ return id
200
+ }
201
+
202
+ private object CurrencySelectorAppearanceKeys {
203
+ const val LIGHT = "light"
204
+ const val DARK = "dark"
205
+
206
+ const val BACKGROUND = "background"
207
+ const val SELECTED_BACKGROUND = "selectedBackground"
208
+ const val BORDER_COLOR = "borderColor"
209
+ const val TEXT_COLOR = "textColor"
210
+ const val SELECTED_TEXT_COLOR = "selectedTextColor"
211
+ const val TEXT_SECONDARY_COLOR = "textSecondaryColor"
212
+ const val DANGER_COLOR = "dangerColor"
213
+
214
+ const val CORNER_RADIUS = "cornerRadius"
215
+ const val BORDER_WIDTH = "borderWidth"
216
+ const val CONTENT_VERTICAL_PADDING = "contentVerticalPadding"
217
+
218
+ const val FONT = "font"
219
+ const val FAMILY = "family"
220
+ const val SCALE = "scale"
221
+
222
+ const val LABEL_CONTENT = "labelContent"
223
+ const val AUTOMATIC = "automatic"
224
+ const val CURRENCY_CODE = "currencyCode"
225
+ const val AMOUNT = "amount"
226
+ }
227
+
228
+ private const val HEX_COLOR_LENGTH_RGB = 6
229
+ private const val HEX_COLOR_LENGTH_ARGB = 8
@@ -20,6 +20,7 @@ import com.facebook.react.bridge.Arguments
20
20
  import com.facebook.react.bridge.ReactContext
21
21
  import com.facebook.react.bridge.WritableMap
22
22
  import com.facebook.react.uimanager.UIManagerHelper
23
+ import com.stripe.android.checkout.Checkout
23
24
  import com.stripe.android.paymentelement.CheckoutSessionPreview
24
25
 
25
26
  @OptIn(CheckoutSessionPreview::class)
@@ -31,6 +32,7 @@ class CurrencySelectorElementView(
31
32
  // Backing state for props. mutableStateOf so Compose recomposes on changes.
32
33
  private var sessionKeyState = mutableStateOf<String?>(null)
33
34
  private var disabledState = mutableStateOf(false)
35
+ private var appearanceState = mutableStateOf(Checkout.CurrencySelectorContentAppearance())
34
36
 
35
37
  fun setSessionKey(value: String?) {
36
38
  sessionKeyState.value = value?.takeIf { it.isNotEmpty() }
@@ -40,11 +42,16 @@ class CurrencySelectorElementView(
40
42
  disabledState.value = value
41
43
  }
42
44
 
45
+ fun setAppearance(value: Checkout.CurrencySelectorContentAppearance) {
46
+ appearanceState.value = value
47
+ }
48
+
43
49
  @SuppressLint("RestrictedApi")
44
50
  @Composable
45
51
  override fun Content() {
46
52
  val sessionKey by remember { sessionKeyState }
47
53
  val disabled by remember { disabledState }
54
+ val appearance by remember { appearanceState }
48
55
 
49
56
  val checkout = remember(sessionKey) {
50
57
  sessionKey?.let { key ->
@@ -63,7 +70,7 @@ class CurrencySelectorElementView(
63
70
  reportHeightChange = { reportHeightChange(it) },
64
71
  ) {
65
72
  Box {
66
- checkout.CurrencySelectorContent()
73
+ checkout.CurrencySelectorContent(appearance = appearance)
67
74
  if (disabled) {
68
75
  // The Stripe Android composable doesn't yet expose an `isEnabled`
69
76
  // parameter, so swallow taps with a transparent pointer-input
@@ -1,5 +1,7 @@
1
1
  package com.reactnativestripesdk
2
2
 
3
+ import com.facebook.react.bridge.Dynamic
4
+ import com.facebook.react.bridge.ReadableType
3
5
  import com.facebook.react.module.annotations.ReactModule
4
6
  import com.facebook.react.uimanager.ThemedReactContext
5
7
  import com.facebook.react.uimanager.ViewGroupManager
@@ -56,4 +58,13 @@ class CurrencySelectorElementViewManager :
56
58
  ) {
57
59
  view.setDisabled(value)
58
60
  }
61
+
62
+ @ReactProp(name = "appearance")
63
+ override fun setAppearance(
64
+ view: CurrencySelectorElementView,
65
+ value: Dynamic?,
66
+ ) {
67
+ val appearanceParams = if (value?.type == ReadableType.Map) value.asMap() else null
68
+ view.setAppearance(buildCurrencySelectorAppearance(appearanceParams, view.context))
69
+ }
59
70
  }
@@ -64,10 +64,6 @@ class EmbeddedPaymentElementView(
64
64
  val intentConfiguration: PaymentSheet.IntentConfiguration,
65
65
  ) : Event
66
66
 
67
- data class UpdateWithCheckout(
68
- val checkout: Checkout,
69
- ) : Event
70
-
71
67
  data object Confirm : Event
72
68
 
73
69
  data object ClearPaymentOption : Event
@@ -347,20 +343,6 @@ class EmbeddedPaymentElementView(
347
343
  latestIntentConfig = ev.intentConfiguration
348
344
  }
349
345
 
350
- is Event.UpdateWithCheckout -> {
351
- val elemConfig = latestElementConfig ?: return@collect emitUpdateMissingConfiguration()
352
-
353
- val result =
354
- embedded.configure(
355
- checkout = ev.checkout,
356
- configuration = elemConfig,
357
- )
358
-
359
- handleUpdateResult(result)
360
-
361
- latestCheckout = ev.checkout
362
- }
363
-
364
346
  is Event.Confirm -> {
365
347
  embedded.confirm()
366
348
  }
@@ -506,10 +488,6 @@ class EmbeddedPaymentElementView(
506
488
  events.trySend(Event.Update(intentConfig))
507
489
  }
508
490
 
509
- fun updateWithCheckout(checkout: Checkout) {
510
- events.trySend(Event.UpdateWithCheckout(checkout))
511
- }
512
-
513
491
  fun confirm() {
514
492
  events.trySend(Event.Confirm)
515
493
  }
@@ -244,28 +244,6 @@ class EmbeddedPaymentElementViewManager :
244
244
  }
245
245
  }
246
246
 
247
- override fun updateWithCheckout(
248
- view: EmbeddedPaymentElementView,
249
- sessionKey: String?,
250
- ) {
251
- if (sessionKey == null) return
252
-
253
- val stripeSdkModule =
254
- (view.context as ThemedReactContext).getNativeModule(StripeSdkModule::class.java)
255
- val checkout = stripeSdkModule?.checkoutInstances?.get(sessionKey)
256
- if (checkout == null) {
257
- val payload =
258
- Arguments.createMap().apply {
259
- putString("message", "Checkout session not found.")
260
- }
261
- stripeSdkModule?.eventEmitter?.emitEmbeddedPaymentElementLoadingFailed(payload)
262
- stripeSdkModule?.eventEmitter?.emitEmbeddedPaymentElementUpdateComplete(null)
263
- return
264
- }
265
-
266
- view.updateWithCheckout(checkout)
267
- }
268
-
269
247
  private fun jsonToWritableMap(json: JSONObject): WritableMap {
270
248
  val map = Arguments.createMap()
271
249
  val keys = json.keys()
@@ -239,6 +239,16 @@ class PaymentSheetManager(
239
239
  return
240
240
  }
241
241
 
242
+ if (checkoutSessionKey != null) {
243
+ promise.resolve(
244
+ createError(
245
+ ErrorType.Failed.toString(),
246
+ "PaymentSheet with checkout is not supported. Use customFlow: true instead.",
247
+ ),
248
+ )
249
+ return
250
+ }
251
+
242
252
  lastConfigureWasCustomFlow = false
243
253
  if (paymentSheet == null) {
244
254
  initPaymentSheet(args, promise)
@@ -437,10 +447,7 @@ class PaymentSheetManager(
437
447
  override fun onPresent() {
438
448
  keepJsAwake = KeepJsAwakeTask(context).apply { start() }
439
449
  if (lastConfigureWasCustomFlow == false) {
440
- val checkout = getCheckoutInstanceOrResolveError(promise)
441
- if (checkout != null) {
442
- paymentSheet?.presentWithCheckout(checkout, paymentSheetConfiguration)
443
- } else if (!paymentIntentClientSecret.isNullOrEmpty()) {
450
+ if (!paymentIntentClientSecret.isNullOrEmpty()) {
444
451
  paymentSheet?.presentWithPaymentIntent(
445
452
  paymentIntentClientSecret!!,
446
453
  paymentSheetConfiguration,
@@ -27,6 +27,7 @@ import com.facebook.react.modules.systeminfo.ReactNativeVersion
27
27
  import com.reactnativestripesdk.addresssheet.AddressLauncherManager
28
28
  import com.reactnativestripesdk.customersheet.CustomerSheetManager
29
29
  import com.reactnativestripesdk.pushprovisioning.PushProvisioningProxy
30
+ import com.reactnativestripesdk.pushprovisioning.TapAndPayProxy
30
31
  import com.reactnativestripesdk.utils.ConfirmPaymentErrorType
31
32
  import com.reactnativestripesdk.utils.CreateTokenErrorType
32
33
  import com.reactnativestripesdk.utils.DefaultActivityLifecycleCallbacks
@@ -955,6 +956,12 @@ class StripeSdkModule(
955
956
  return
956
957
  }
957
958
 
959
+ val cardBrand =
960
+ getValOr(params, "cardBrand", null) ?: run {
961
+ promise.resolve(createError("Failed", "You must provide cardBrand"))
962
+ return
963
+ }
964
+
958
965
  if (params.getBooleanOr("supportsTapToPay", true) &&
959
966
  !PushProvisioningProxy.isNFCEnabled(
960
967
  reactApplicationContext,
@@ -964,14 +971,14 @@ class StripeSdkModule(
964
971
  return
965
972
  }
966
973
 
967
- getCurrentActivityOrResolveWithError(promise)?.let {
968
- PushProvisioningProxy.isCardInWallet(it, last4) { isCardInWallet, token, error ->
974
+ getCurrentActivityOrResolveWithError(promise)?.let { activity ->
975
+ TapAndPayProxy.checkEligibility(activity, last4, cardBrand) { canAdd, token, error ->
969
976
  val result =
970
977
  error?.let {
971
- createCanAddCardResult(false, "MISSING_CONFIGURATION", null)
978
+ createCanAddCardResult(false, "MISSING_CONFIGURATION")
972
979
  } ?: run {
973
- val status = if (isCardInWallet) "CARD_ALREADY_EXISTS" else null
974
- createCanAddCardResult(!isCardInWallet, status, token)
980
+ val status = if (!canAdd) "CARD_ALREADY_EXISTS" else null
981
+ createCanAddCardResult(canAdd, status, token)
975
982
  }
976
983
  promise.resolve(result)
977
984
  }
@@ -1443,15 +1450,6 @@ class StripeSdkModule(
1443
1450
  // TODO:
1444
1451
  }
1445
1452
 
1446
- @ReactMethod
1447
- override fun updateEmbeddedPaymentElementWithCheckout(
1448
- sessionKey: String,
1449
- promise: Promise,
1450
- ) {
1451
- // No-op on Android. JS dispatches Checkout updates through the view command instead.
1452
- promise.resolve(null)
1453
- }
1454
-
1455
1453
  @ReactMethod
1456
1454
  override fun clearEmbeddedPaymentOption(
1457
1455
  viewTag: Double,
@@ -1869,17 +1867,6 @@ class StripeSdkModule(
1869
1867
  }
1870
1868
  }
1871
1869
 
1872
- override fun checkoutUpdateTaxId(
1873
- sessionKey: String,
1874
- type: String,
1875
- value: String,
1876
- promise: Promise,
1877
- ) {
1878
- performCheckoutMutation(sessionKey, promise) { checkout ->
1879
- checkout.updateTaxId(type = type, value = value)
1880
- }
1881
- }
1882
-
1883
1870
  override fun checkoutRunServerUpdateStart(
1884
1871
  sessionKey: String,
1885
1872
  promise: Promise,
@@ -83,6 +83,14 @@ class AddToWalletButtonManager :
83
83
  // noop, iOS only.
84
84
  }
85
85
 
86
+ @ReactProp(name = "isBounceProvisioned")
87
+ override fun setIsBounceProvisioned(
88
+ view: AddToWalletButtonView,
89
+ value: Boolean,
90
+ ) {
91
+ view.setIsBounceProvisioned(value)
92
+ }
93
+
86
94
  @ReactProp(name = "testEnv")
87
95
  override fun setTestEnv(
88
96
  view: AddToWalletButtonView,
@@ -33,6 +33,7 @@ class AddToWalletButtonView(
33
33
  private var ephemeralKey: String? = null
34
34
  private var sourceMap: ReadableMap? = null
35
35
  private var token: ReadableMap? = null
36
+ private var isBounceProvisioned: Boolean = false
36
37
 
37
38
  private var loadedSource: String? = null
38
39
  private var currentDataSource: DataSource<CloseableReference<CloseableImage>>? = null
@@ -54,6 +55,7 @@ class AddToWalletButtonView(
54
55
  cardDescription,
55
56
  ephemeralKey,
56
57
  token,
58
+ isBounceProvisioned,
57
59
  )
58
60
  }
59
61
  ?: run {
@@ -189,6 +191,10 @@ class AddToWalletButtonView(
189
191
  token = map
190
192
  }
191
193
 
194
+ fun setIsBounceProvisioned(value: Boolean) {
195
+ isBounceProvisioned = value
196
+ }
197
+
192
198
  fun dispatchEvent(error: WritableMap?) {
193
199
  UIManagerHelper
194
200
  .getEventDispatcherForReactTag(context, id)
@@ -42,6 +42,7 @@ object PushProvisioningProxy {
42
42
  cardDescription: String,
43
43
  ephemeralKey: String,
44
44
  token: ReadableMap?,
45
+ isBounceProvisioned: Boolean,
45
46
  ) {
46
47
  try {
47
48
  Class.forName("com.stripe.android.pushProvisioning.PushProvisioningActivityStarter")
@@ -50,7 +51,7 @@ object PushProvisioningProxy {
50
51
  createActivityEventListener(context, view)
51
52
  context.currentActivity?.let {
52
53
  DefaultPushProvisioningProxy()
53
- .beginPushProvisioning(it, description, EphemeralKeyProvider(ephemeralKey))
54
+ .beginPushProvisioning(it, description, EphemeralKeyProvider(ephemeralKey), isBounceProvisioned)
54
55
  }
55
56
  ?: run {
56
57
  view.dispatchEvent(
@@ -136,10 +137,10 @@ class DefaultPushProvisioningProxy {
136
137
  activity: Activity,
137
138
  description: String,
138
139
  provider: EphemeralKeyProvider,
140
+ isBounceProvisioned: Boolean,
139
141
  ) {
140
- PushProvisioningActivityStarter(
141
- activity,
142
- PushProvisioningActivityStarter.Args(description, provider, false),
143
- ).startForResult()
142
+ val args = PushProvisioningActivityStarter.Args(description, provider, true)
143
+ args.setIsBounceProvisioned(isBounceProvisioned)
144
+ PushProvisioningActivityStarter(activity, args).startForResult()
144
145
  }
145
146
  }