@stripe/stripe-react-native 0.27.2 → 0.29.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 (141) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +2 -2
  3. package/android/gradle.properties +1 -1
  4. package/android/src/main/java/com/reactnativestripesdk/CardFieldView.kt +4 -0
  5. package/android/src/main/java/com/reactnativestripesdk/CardFieldViewManager.kt +5 -0
  6. package/android/src/main/java/com/reactnativestripesdk/CardFormView.kt +4 -0
  7. package/android/src/main/java/com/reactnativestripesdk/CardFormViewManager.kt +5 -0
  8. package/android/src/main/java/com/reactnativestripesdk/FinancialConnectionsSheetFragment.kt +33 -7
  9. package/android/src/main/java/com/reactnativestripesdk/GooglePayButtonManager.kt +0 -5
  10. package/android/src/main/java/com/reactnativestripesdk/GooglePayButtonView.kt +11 -27
  11. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +131 -6
  12. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +29 -6
  13. package/android/src/main/java/com/reactnativestripesdk/utils/Errors.kt +2 -0
  14. package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +14 -1
  15. package/ios/ApplePayButtonView.swift +4 -18
  16. package/ios/ApplePayViewController.swift +1 -28
  17. package/ios/CardFieldManager.m +1 -0
  18. package/ios/CardFieldView.swift +6 -0
  19. package/ios/CardFormManager.m +2 -2
  20. package/ios/CardFormView.swift +9 -9
  21. package/ios/StripeSdk+PaymentSheet.swift +100 -10
  22. package/ios/StripeSdk.m +15 -29
  23. package/ios/StripeSdk.swift +32 -91
  24. package/jest/mock.js +0 -6
  25. package/lib/commonjs/NativeStripeSdk.js.map +1 -1
  26. package/lib/commonjs/components/ApplePayButtonNative.js.map +1 -1
  27. package/lib/commonjs/components/CardField.js +1 -1
  28. package/lib/commonjs/components/CardField.js.map +1 -1
  29. package/lib/commonjs/components/CardForm.js +1 -1
  30. package/lib/commonjs/components/CardForm.js.map +1 -1
  31. package/lib/commonjs/components/GooglePayButtonNative.js.map +1 -1
  32. package/lib/commonjs/components/PlatformPayButton.js +1 -1
  33. package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
  34. package/lib/commonjs/functions.js +1 -1
  35. package/lib/commonjs/functions.js.map +1 -1
  36. package/lib/commonjs/hooks/useStripe.js +1 -1
  37. package/lib/commonjs/hooks/useStripe.js.map +1 -1
  38. package/lib/commonjs/index.js +1 -1
  39. package/lib/commonjs/index.js.map +1 -1
  40. package/lib/commonjs/types/ApplePay.js.map +1 -1
  41. package/lib/commonjs/types/PaymentIntent.js.map +1 -1
  42. package/lib/commonjs/types/PaymentSheet.js +1 -1
  43. package/lib/commonjs/types/PaymentSheet.js.map +1 -1
  44. package/lib/commonjs/types/PlatformPay.js.map +1 -1
  45. package/lib/commonjs/types/components/CardFieldInput.js.map +1 -1
  46. package/lib/commonjs/types/components/CardFormView.js.map +1 -1
  47. package/lib/commonjs/types/components/GooglePayButtonComponent.js.map +1 -1
  48. package/lib/commonjs/types/index.js +1 -1
  49. package/lib/commonjs/types/index.js.map +1 -1
  50. package/lib/module/NativeStripeSdk.js.map +1 -1
  51. package/lib/module/components/ApplePayButtonNative.js.map +1 -1
  52. package/lib/module/components/CardField.js +1 -1
  53. package/lib/module/components/CardField.js.map +1 -1
  54. package/lib/module/components/CardForm.js +1 -1
  55. package/lib/module/components/CardForm.js.map +1 -1
  56. package/lib/module/components/GooglePayButtonNative.js.map +1 -1
  57. package/lib/module/components/PlatformPayButton.js +1 -1
  58. package/lib/module/components/PlatformPayButton.js.map +1 -1
  59. package/lib/module/functions.js +1 -1
  60. package/lib/module/functions.js.map +1 -1
  61. package/lib/module/hooks/useStripe.js +1 -1
  62. package/lib/module/hooks/useStripe.js.map +1 -1
  63. package/lib/module/index.js +1 -1
  64. package/lib/module/index.js.map +1 -1
  65. package/lib/module/types/ApplePay.js.map +1 -1
  66. package/lib/module/types/PaymentIntent.js.map +1 -1
  67. package/lib/module/types/PaymentSheet.js +1 -1
  68. package/lib/module/types/PaymentSheet.js.map +1 -1
  69. package/lib/module/types/PlatformPay.js.map +1 -1
  70. package/lib/module/types/components/CardFieldInput.js.map +1 -1
  71. package/lib/module/types/components/CardFormView.js.map +1 -1
  72. package/lib/module/types/components/GooglePayButtonComponent.js.map +1 -1
  73. package/lib/module/types/index.js +1 -1
  74. package/lib/module/types/index.js.map +1 -1
  75. package/lib/typescript/src/NativeStripeSdk.d.ts +4 -15
  76. package/lib/typescript/src/components/ApplePayButtonNative.d.ts +1 -1
  77. package/lib/typescript/src/components/CardField.d.ts +2 -0
  78. package/lib/typescript/src/components/CardForm.d.ts +2 -0
  79. package/lib/typescript/src/components/GooglePayButtonNative.d.ts +1 -1
  80. package/lib/typescript/src/components/PlatformPayButton.d.ts +2 -1
  81. package/lib/typescript/src/functions.d.ts +8 -27
  82. package/lib/typescript/src/hooks/usePlatformPay.d.ts +1 -1
  83. package/lib/typescript/src/hooks/useStripe.d.ts +2 -18
  84. package/lib/typescript/src/index.d.ts +0 -7
  85. package/lib/typescript/src/types/ApplePay.d.ts +0 -52
  86. package/lib/typescript/src/types/PaymentIntent.d.ts +3 -2
  87. package/lib/typescript/src/types/PaymentSheet.d.ts +48 -2
  88. package/lib/typescript/src/types/PlatformPay.d.ts +35 -5
  89. package/lib/typescript/src/types/components/CardFieldInput.d.ts +1 -0
  90. package/lib/typescript/src/types/components/CardFormView.d.ts +1 -0
  91. package/lib/typescript/src/types/components/GooglePayButtonComponent.d.ts +0 -1
  92. package/lib/typescript/src/types/index.d.ts +1 -4
  93. package/package.json +1 -1
  94. package/src/NativeStripeSdk.tsx +6 -31
  95. package/src/components/ApplePayButtonNative.tsx +1 -1
  96. package/src/components/CardField.tsx +2 -0
  97. package/src/components/CardForm.tsx +2 -4
  98. package/src/components/GooglePayButtonNative.tsx +1 -1
  99. package/src/components/PlatformPayButton.tsx +2 -1
  100. package/src/functions.ts +54 -200
  101. package/src/hooks/useStripe.tsx +2 -109
  102. package/src/index.tsx +0 -7
  103. package/src/types/ApplePay.ts +0 -71
  104. package/src/types/PaymentIntent.ts +4 -2
  105. package/src/types/PaymentSheet.ts +86 -2
  106. package/src/types/PlatformPay.ts +36 -5
  107. package/src/types/components/CardFieldInput.ts +1 -0
  108. package/src/types/components/CardFormView.ts +1 -1
  109. package/src/types/components/GooglePayButtonComponent.ts +0 -1
  110. package/src/types/index.ts +0 -6
  111. package/stripe-react-native.podspec +1 -1
  112. package/lib/commonjs/components/ApplePayButton.js +0 -2
  113. package/lib/commonjs/components/ApplePayButton.js.map +0 -1
  114. package/lib/commonjs/components/GooglePayButton.js +0 -2
  115. package/lib/commonjs/components/GooglePayButton.js.map +0 -1
  116. package/lib/commonjs/hooks/useApplePay.js +0 -2
  117. package/lib/commonjs/hooks/useApplePay.js.map +0 -1
  118. package/lib/commonjs/hooks/useGooglePay.js +0 -2
  119. package/lib/commonjs/hooks/useGooglePay.js.map +0 -1
  120. package/lib/commonjs/types/GooglePay.js +0 -2
  121. package/lib/commonjs/types/GooglePay.js.map +0 -1
  122. package/lib/module/components/ApplePayButton.js +0 -2
  123. package/lib/module/components/ApplePayButton.js.map +0 -1
  124. package/lib/module/components/GooglePayButton.js +0 -2
  125. package/lib/module/components/GooglePayButton.js.map +0 -1
  126. package/lib/module/hooks/useApplePay.js +0 -2
  127. package/lib/module/hooks/useApplePay.js.map +0 -1
  128. package/lib/module/hooks/useGooglePay.js +0 -2
  129. package/lib/module/hooks/useGooglePay.js.map +0 -1
  130. package/lib/module/types/GooglePay.js +0 -2
  131. package/lib/module/types/GooglePay.js.map +0 -1
  132. package/lib/typescript/src/components/ApplePayButton.d.ts +0 -31
  133. package/lib/typescript/src/components/GooglePayButton.d.ts +0 -26
  134. package/lib/typescript/src/hooks/useApplePay.d.ts +0 -54
  135. package/lib/typescript/src/hooks/useGooglePay.d.ts +0 -11
  136. package/lib/typescript/src/types/GooglePay.d.ts +0 -47
  137. package/src/components/ApplePayButton.tsx +0 -108
  138. package/src/components/GooglePayButton.tsx +0 -58
  139. package/src/hooks/useApplePay.tsx +0 -161
  140. package/src/hooks/useGooglePay.tsx +0 -72
  141. package/src/types/GooglePay.ts +0 -74
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.29.0 - 2023-07-13
6
+
7
+ ### Breaking changes
8
+
9
+ - The Apple Pay and Google Pay APIs, which are deprecated and were replaced with the [Platform Pay API](https://github.com/stripe/stripe-react-native/blob/master/docs/Platform-Pay-Migration.md) last year, have been removed. [#1424](https://github.com/stripe/stripe-react-native/pull/1424)
10
+
11
+ ## Features
12
+
13
+ - You can now collect payment details before creating a `PaymentIntent` or `SetupIntent`. See [our docs](https://stripe.com/docs/payments/accept-a-payment-deferred?platform=react-native) for more info. This integration also allows you to [confirm the Intent on the server](https://stripe.com/docs/payments/finalize-payments-on-the-server?platform=react-native). [#1424](https://github.com/stripe/stripe-react-native/pull/1424)
14
+
15
+ ## Fixes
16
+
17
+ - Fixes `handleURLCallback` to only take action on iOS, no-op on Android. [#1423](https://github.com/stripe/stripe-react-native/pull/1423)
18
+
19
+ ## 0.28.0 - 2023-06-16
20
+
21
+ ## Features
22
+
23
+ - Added a `disabled` prop to `CardField` and `CardForm` which applies a disabled state such that user input is not accepted. [#1403](https://github.com/stripe/stripe-react-native/pull/1403)
24
+
25
+ ## Fixes
26
+
27
+ - Fixed an instance on Android where `collectBankAccountToken` or `collectFinancialConnectionsAccounts` could result in a fatal error. [#1401](https://github.com/stripe/stripe-react-native/pull/1401)
28
+ - Resolve with better error objects on iOS in `confirmPaymentSheetPayment`, `createTokenForCVCUpdate`, `createPaymentMethod`, `retrievePaymentIntent`, and `retrieveSetupIntent` [#1399](https://github.com/stripe/stripe-react-native/pull/1399)
29
+
5
30
  ## 0.27.2 - 2023-05-15
6
31
 
7
32
  ## Fixes
package/README.md CHANGED
@@ -228,11 +228,11 @@ jest.mock('@stripe/stripe-react-native', () => mock);
228
228
  To have a more control over the mocks, you can extend and override particular methods e.g.:
229
229
 
230
230
  ```tsx
231
- const presentApplePayMock = jest.fn();
231
+ const presentNativePayMock = jest.fn();
232
232
 
233
233
  jest.mock('@stripe/stripe-react-native', () => ({
234
234
  ...mock,
235
- presentApplePay: presentApplePayMock,
235
+ presentNativePay: presentNativePayMock,
236
236
  }));
237
237
  ```
238
238
 
@@ -1,3 +1,3 @@
1
1
  StripeSdk_kotlinVersion=1.8.0
2
2
  # Keep StripeSdk_stripeVersion in sync with https://github.com/stripe/stripe-identity-react-native/blob/main/android/gradle.properties
3
- StripeSdk_stripeVersion=20.25.+
3
+ StripeSdk_stripeVersion=20.27.+
@@ -209,6 +209,10 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
209
209
  }
210
210
  }
211
211
 
212
+ fun setDisabled(isDisabled: Boolean) {
213
+ mCardWidget.isEnabled = !isDisabled
214
+ }
215
+
212
216
  /**
213
217
  * We can reliable assume that setPostalCodeEnabled is called before
214
218
  * setCountryCode because of the order of the props in CardField.tsx
@@ -56,6 +56,11 @@ class CardFieldViewManager : SimpleViewManager<CardFieldView>() {
56
56
  view.setPlaceHolders(placeholders)
57
57
  }
58
58
 
59
+ @ReactProp(name = "disabled")
60
+ fun setDisabled(view: CardFieldView, isDisabled: Boolean) {
61
+ view.setDisabled(isDisabled)
62
+ }
63
+
59
64
  override fun createViewInstance(reactContext: ThemedReactContext): CardFieldView {
60
65
  val stripeSdkModule: StripeSdkModule? = reactContext.getNativeModule(StripeSdkModule::class.java)
61
66
  val view = CardFieldView(reactContext)
@@ -58,6 +58,10 @@ class CardFormView(context: ThemedReactContext) : FrameLayout(context) {
58
58
  setCountry(defaults.getString("countryCode"))
59
59
  }
60
60
 
61
+ fun setDisabled(isDisabled: Boolean) {
62
+ cardForm.isEnabled = !isDisabled
63
+ }
64
+
61
65
  private fun setCountry(countryString: String?) {
62
66
  if (countryString != null) {
63
67
  cardFormViewBinding.countryLayout.setSelectedCountryCode(CountryCode(countryString))
@@ -56,6 +56,11 @@ class CardFormViewManager : SimpleViewManager<CardFormView>() {
56
56
  view.setDefaultValues(defaults)
57
57
  }
58
58
 
59
+ @ReactProp(name = "disabled")
60
+ fun setDisabled(view: CardFormView, isDisabled: Boolean) {
61
+ view.setDisabled(isDisabled)
62
+ }
63
+
59
64
  override fun createViewInstance(reactContext: ThemedReactContext): CardFormView {
60
65
  val stripeSdkModule: StripeSdkModule? = reactContext.getNativeModule(StripeSdkModule::class.java)
61
66
  val view = CardFormView(reactContext)
@@ -180,18 +180,44 @@ class FinancialConnectionsSheetFragment : Fragment() {
180
180
  val map = WritableNativeMap()
181
181
  map.putDouble("asOf", balance.asOf * 1000.0)
182
182
  map.putString("type", mapFromBalanceType(balance.type))
183
- map.putMap("current", balance.current as ReadableMap)
184
183
  WritableNativeMap().also {
185
- it.putMap("available", balance.cash?.available as ReadableMap)
186
- map.putMap("cash", it)
187
- }
188
- WritableNativeMap().also {
189
- it.putMap("used", balance.credit?.used as ReadableMap)
190
- map.putMap("credit", it)
184
+ for (entry in balance.current.entries) {
185
+ it.putInt(entry.key, entry.value)
186
+ }
187
+ map.putMap("current", it)
191
188
  }
189
+ map.putMap("cash", mapFromCashAvailable(balance))
190
+ map.putMap("credit", mapFromCreditUsed(balance))
191
+
192
192
  return map
193
193
  }
194
194
 
195
+ private fun mapFromCashAvailable(balance: Balance): WritableNativeMap {
196
+ return WritableNativeMap().also { cashMap ->
197
+ WritableNativeMap().also { availableMap ->
198
+ balance.cash?.available?.entries?.let { entries ->
199
+ for (entry in entries) {
200
+ availableMap.putInt(entry.key, entry.value)
201
+ }
202
+ }
203
+ cashMap.putMap("available", availableMap)
204
+ }
205
+ }
206
+ }
207
+
208
+ private fun mapFromCreditUsed(balance: Balance): WritableNativeMap {
209
+ return WritableNativeMap().also { creditMap ->
210
+ WritableNativeMap().also { usedMap ->
211
+ balance.credit?.used?.entries?.let { entries ->
212
+ for (entry in entries) {
213
+ usedMap.putInt(entry.key, entry.value)
214
+ }
215
+ }
216
+ creditMap.putMap("used", usedMap)
217
+ }
218
+ }
219
+ }
220
+
195
221
  private fun mapFromAccountBalanceRefresh(balanceRefresh: BalanceRefresh?): WritableMap? {
196
222
  if (balanceRefresh == null) {
197
223
  return null
@@ -15,11 +15,6 @@ class GooglePayButtonManager : SimpleViewManager<GooglePayButtonView?>() {
15
15
  view.initialize()
16
16
  }
17
17
 
18
- @ReactProp(name = "buttonType")
19
- fun buttonType(view: GooglePayButtonView, buttonType: String) {
20
- view.setButtonType(buttonType)
21
- }
22
-
23
18
  @ReactProp(name = "type")
24
19
  fun type(view: GooglePayButtonView, buttonType: Int) {
25
20
  view.setType(buttonType)
@@ -7,33 +7,21 @@ import com.facebook.react.uimanager.ThemedReactContext
7
7
 
8
8
  class GooglePayButtonView(private val context: ThemedReactContext) : FrameLayout(context) {
9
9
  private var button: View? = null
10
- // Used in legacy GooglePayButton implementations
11
- private var buttonType: String? = null
12
-
13
- // Used in the new PlatformPayButton implementations
14
10
  private var type: Int? = null
15
11
 
16
12
  fun initialize() {
17
13
  val resAsset: Int =
18
- if (type != null) {
19
- when (type) {
20
- 0 -> R.layout.plain_googlepay_button
21
- 1 -> R.layout.buy_with_googlepay_button
22
- 6 -> R.layout.book_with_googlepay_button
23
- 5 -> R.layout.checkout_with_googlepay_button
24
- 4 -> R.layout.donate_with_googlepay_button
25
- 11 -> R.layout.order_with_googlepay_button
26
- 1000 -> R.layout.pay_with_googlepay_button
27
- 7 -> R.layout.subscribe_with_googlepay_button
28
- 1001 -> R.layout.googlepay_mark_button
29
- else -> R.layout.plain_googlepay_button
30
- }
31
- } else {
32
- when (buttonType) {
33
- "pay" -> R.layout.pay_with_googlepay_button
34
- "standard" -> R.layout.plain_googlepay_button
35
- else -> R.layout.plain_googlepay_button
36
- }
14
+ when (type) {
15
+ 0 -> R.layout.plain_googlepay_button
16
+ 1 -> R.layout.buy_with_googlepay_button
17
+ 6 -> R.layout.book_with_googlepay_button
18
+ 5 -> R.layout.checkout_with_googlepay_button
19
+ 4 -> R.layout.donate_with_googlepay_button
20
+ 11 -> R.layout.order_with_googlepay_button
21
+ 1000 -> R.layout.pay_with_googlepay_button
22
+ 7 -> R.layout.subscribe_with_googlepay_button
23
+ 1001 -> R.layout.googlepay_mark_button
24
+ else -> R.layout.plain_googlepay_button
37
25
  }
38
26
 
39
27
  button = LayoutInflater.from(context).inflate(
@@ -56,10 +44,6 @@ class GooglePayButtonView(private val context: ThemedReactContext) : FrameLayout
56
44
  button?.layout(left, top, right, bottom)
57
45
  }
58
46
 
59
- fun setButtonType(type: String) {
60
- buttonType = type
61
- }
62
-
63
47
  fun setType(type: Int) {
64
48
  this.type = type
65
49
  }
@@ -17,16 +17,15 @@ import android.widget.FrameLayout
17
17
  import androidx.appcompat.content.res.AppCompatResources
18
18
  import androidx.core.graphics.drawable.DrawableCompat
19
19
  import androidx.fragment.app.Fragment
20
- import com.facebook.react.bridge.Promise
21
- import com.facebook.react.bridge.ReactApplicationContext
22
- import com.facebook.react.bridge.WritableMap
23
- import com.facebook.react.bridge.WritableNativeMap
20
+ import com.facebook.react.bridge.*
24
21
  import com.reactnativestripesdk.addresssheet.AddressSheetView
25
22
  import com.reactnativestripesdk.utils.*
26
23
  import com.reactnativestripesdk.utils.createError
27
24
  import com.reactnativestripesdk.utils.createResult
28
25
  import com.stripe.android.paymentsheet.*
26
+ import kotlinx.coroutines.CompletableDeferred
29
27
  import java.io.ByteArrayOutputStream
28
+ import kotlin.Exception
30
29
 
31
30
  class PaymentSheetFragment(
32
31
  private val context: ReactApplicationContext,
@@ -36,10 +35,12 @@ class PaymentSheetFragment(
36
35
  private var flowController: PaymentSheet.FlowController? = null
37
36
  private var paymentIntentClientSecret: String? = null
38
37
  private var setupIntentClientSecret: String? = null
38
+ private var intentConfiguration: PaymentSheet.IntentConfiguration? = null
39
39
  private lateinit var paymentSheetConfiguration: PaymentSheet.Configuration
40
40
  private var confirmPromise: Promise? = null
41
41
  private var presentPromise: Promise? = null
42
42
  private var paymentSheetTimedOut = false
43
+ internal val paymentSheetIntentCreationCallback = CompletableDeferred<ReadableMap>()
43
44
 
44
45
  override fun onCreateView(
45
46
  inflater: LayoutInflater,
@@ -67,6 +68,12 @@ class PaymentSheetFragment(
67
68
  val billingConfigParams = arguments?.getBundle("billingDetailsCollectionConfiguration")
68
69
  paymentIntentClientSecret = arguments?.getString("paymentIntentClientSecret").orEmpty()
69
70
  setupIntentClientSecret = arguments?.getString("setupIntentClientSecret").orEmpty()
71
+ intentConfiguration = try {
72
+ buildIntentConfiguration(arguments?.getBundle("intentConfiguration"))
73
+ } catch (error: PaymentSheetException) {
74
+ initPromise.resolve(createError(ErrorType.Failed.toString(), error))
75
+ return
76
+ }
70
77
  val appearance = try {
71
78
  buildPaymentSheetAppearance(arguments?.getBundle("appearance"), context)
72
79
  } catch (error: PaymentSheetAppearanceException) {
@@ -120,6 +127,34 @@ class PaymentSheetFragment(
120
127
  }
121
128
  }
122
129
 
130
+ val createIntentCallback = CreateIntentCallback { paymentMethod, shouldSavePaymentMethod ->
131
+ val stripeSdkModule: StripeSdkModule? = context.getNativeModule(StripeSdkModule::class.java)
132
+ if (stripeSdkModule == null || stripeSdkModule.eventListenerCount == 0) {
133
+ return@CreateIntentCallback CreateIntentResult.Failure(
134
+ cause = Exception("Tried to call confirmHandler, but no callback was found. Please file an issue: https://github.com/stripe/stripe-react-native/issues"),
135
+ displayMessage = "An unexpected error occurred"
136
+ )
137
+ }
138
+ val params = Arguments.createMap().apply {
139
+ putMap("paymentMethod", mapFromPaymentMethod(paymentMethod))
140
+ putBoolean("shouldSavePaymentMethod", shouldSavePaymentMethod)
141
+ }
142
+
143
+ stripeSdkModule.sendEvent(context, "onConfirmHandlerCallback", params)
144
+
145
+ val resultFromJavascript = paymentSheetIntentCreationCallback.await()
146
+
147
+ return@CreateIntentCallback resultFromJavascript.getString("clientSecret")?.let {
148
+ CreateIntentResult.Success(clientSecret = it)
149
+ } ?: run {
150
+ val errorMap = resultFromJavascript.getMap("error")
151
+ CreateIntentResult.Failure(
152
+ cause = Exception(errorMap?.getString("message")),
153
+ displayMessage = errorMap?.getString("localizedMessage")
154
+ )
155
+ }
156
+ }
157
+
123
158
  val billingDetailsConfig = PaymentSheet.BillingDetailsCollectionConfiguration(
124
159
  name = mapToCollectionMode(billingConfigParams?.getString("name")),
125
160
  phone = mapToCollectionMode(billingConfigParams?.getString("phone")),
@@ -162,10 +197,34 @@ class PaymentSheetFragment(
162
197
  )
163
198
 
164
199
  if (arguments?.getBoolean("customFlow") == true) {
165
- flowController = PaymentSheet.FlowController.create(this, paymentOptionCallback, paymentResultCallback)
200
+ flowController = if (intentConfiguration != null) {
201
+ PaymentSheet.FlowController.create(
202
+ this,
203
+ paymentOptionCallback = paymentOptionCallback,
204
+ createIntentCallback = createIntentCallback,
205
+ paymentResultCallback = paymentResultCallback
206
+ )
207
+ } else {
208
+ PaymentSheet.FlowController.create(
209
+ this,
210
+ paymentOptionCallback = paymentOptionCallback,
211
+ paymentResultCallback = paymentResultCallback
212
+ )
213
+ }
166
214
  configureFlowController()
167
215
  } else {
168
- paymentSheet = PaymentSheet(this, paymentResultCallback)
216
+ paymentSheet = if (intentConfiguration != null) {
217
+ PaymentSheet(
218
+ this,
219
+ createIntentCallback = createIntentCallback,
220
+ paymentResultCallback = paymentResultCallback
221
+ )
222
+ } else {
223
+ PaymentSheet(
224
+ this,
225
+ callback = paymentResultCallback
226
+ )
227
+ }
169
228
  initPromise.resolve(WritableNativeMap())
170
229
  }
171
230
  }
@@ -177,6 +236,11 @@ class PaymentSheetFragment(
177
236
  paymentSheet?.presentWithPaymentIntent(paymentIntentClientSecret!!, paymentSheetConfiguration)
178
237
  } else if (!setupIntentClientSecret.isNullOrEmpty()) {
179
238
  paymentSheet?.presentWithSetupIntent(setupIntentClientSecret!!, paymentSheetConfiguration)
239
+ } else if (intentConfiguration != null) {
240
+ paymentSheet?.presentWithIntentConfiguration(
241
+ intentConfiguration = intentConfiguration!!,
242
+ configuration = paymentSheetConfiguration
243
+ )
180
244
  }
181
245
  } else if(flowController != null) {
182
246
  flowController?.presentPaymentOptions()
@@ -253,6 +317,15 @@ class PaymentSheetFragment(
253
317
  configuration = paymentSheetConfiguration,
254
318
  callback = onFlowControllerConfigure
255
319
  )
320
+ } else if (intentConfiguration != null) {
321
+ flowController?.configureWithIntentConfiguration(
322
+ intentConfiguration = intentConfiguration!!,
323
+ configuration = paymentSheetConfiguration,
324
+ callback = onFlowControllerConfigure
325
+ )
326
+ } else {
327
+ initPromise.resolve(createError(ErrorType.Failed.toString(), "One of `paymentIntentClientSecret`, `setupIntentClientSecret`, or `intentConfiguration` is required"))
328
+ return
256
329
  }
257
330
  }
258
331
 
@@ -287,6 +360,41 @@ class PaymentSheetFragment(
287
360
  currencyCode = currencyCode
288
361
  )
289
362
  }
363
+
364
+ @Throws(PaymentSheetException::class)
365
+ private fun buildIntentConfiguration(intentConfigurationParams: Bundle?): PaymentSheet.IntentConfiguration? {
366
+ if (intentConfigurationParams == null) {
367
+ return null
368
+ }
369
+ val modeParams = intentConfigurationParams.getBundle("mode")
370
+ ?: throw PaymentSheetException("If `intentConfiguration` is provided, `intentConfiguration.mode` is required")
371
+
372
+ return PaymentSheet.IntentConfiguration(
373
+ mode = buildIntentConfigurationMode(modeParams),
374
+ paymentMethodTypes = intentConfigurationParams.getStringArrayList("paymentMethodTypes")?.toList() ?: emptyList(),
375
+ )
376
+ }
377
+
378
+ private fun buildIntentConfigurationMode(modeParams: Bundle): PaymentSheet.IntentConfiguration.Mode {
379
+ val currencyCode = modeParams.getString("currencyCode")
380
+ ?: throw PaymentSheetException("You must provide a value to intentConfiguration.mode.currencyCode")
381
+
382
+ return if (modeParams.containsKey("amount")) {
383
+ PaymentSheet.IntentConfiguration.Mode.Payment(
384
+ amount = modeParams.getInt("amount").toLong(),
385
+ currency = currencyCode,
386
+ setupFutureUse = mapToSetupFutureUse(modeParams.getString("setupFutureUsage")),
387
+ captureMethod = mapToCaptureMethod(modeParams.getString("captureMethod")),
388
+ )
389
+ } else {
390
+ val setupFutureUsage = mapToSetupFutureUse(modeParams.getString("setupFutureUsage"))
391
+ ?: throw PaymentSheetException("You must provide a value to intentConfiguration.mode.setupFutureUsage")
392
+ PaymentSheet.IntentConfiguration.Mode.Setup(
393
+ currency = currencyCode,
394
+ setupFutureUse = setupFutureUsage
395
+ )
396
+ }
397
+ }
290
398
  }
291
399
  }
292
400
 
@@ -329,3 +437,20 @@ fun mapToAddressCollectionMode(str: String?): PaymentSheet.BillingDetailsCollect
329
437
  else -> PaymentSheet.BillingDetailsCollectionConfiguration.AddressCollectionMode.Automatic
330
438
  }
331
439
  }
440
+
441
+ fun mapToSetupFutureUse(type: String?): PaymentSheet.IntentConfiguration.SetupFutureUse? {
442
+ return when (type) {
443
+ "OffSession" -> PaymentSheet.IntentConfiguration.SetupFutureUse.OffSession
444
+ "OnSession" -> PaymentSheet.IntentConfiguration.SetupFutureUse.OnSession
445
+ else -> null
446
+ }
447
+ }
448
+
449
+ fun mapToCaptureMethod(type: String?): PaymentSheet.IntentConfiguration.CaptureMethod {
450
+ return when (type) {
451
+ "Automatic" -> PaymentSheet.IntentConfiguration.CaptureMethod.Automatic
452
+ "Manual" -> PaymentSheet.IntentConfiguration.CaptureMethod.Manual
453
+ "AutomaticAsync" -> PaymentSheet.IntentConfiguration.CaptureMethod.AutomaticAsync
454
+ else -> PaymentSheet.IntentConfiguration.CaptureMethod.Automatic
455
+ }
456
+ }
@@ -7,6 +7,7 @@ import android.util.Log
7
7
  import androidx.fragment.app.FragmentActivity
8
8
  import com.facebook.react.bridge.*
9
9
  import com.facebook.react.module.annotations.ReactModule
10
+ import com.facebook.react.modules.core.DeviceEventManagerModule
10
11
  import com.reactnativestripesdk.addresssheet.AddressLauncherFragment
11
12
  import com.reactnativestripesdk.pushprovisioning.PushProvisioningProxy
12
13
  import com.reactnativestripesdk.utils.*
@@ -47,6 +48,8 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
47
48
  private var paymentLauncherFragment: PaymentLauncherFragment? = null
48
49
  private var collectBankAccountLauncherFragment: CollectBankAccountLauncherFragment? = null
49
50
 
51
+ internal var eventListenerCount = 0
52
+
50
53
  // If you create a new Fragment, you must put the tag here, otherwise result callbacks for that
51
54
  // Fragment will not work on RN < 0.65
52
55
  private val allStripeFragmentTags: List<String>
@@ -204,6 +207,16 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
204
207
  promise.resolve(null)
205
208
  }
206
209
 
210
+ @ReactMethod
211
+ fun intentCreationCallback(params: ReadableMap, promise: Promise) {
212
+ if (paymentSheetFragment == null) {
213
+ promise.resolve(PaymentSheetFragment.createMissingInitError())
214
+ return
215
+ }
216
+
217
+ paymentSheetFragment?.paymentSheetIntentCreationCallback?.complete(params)
218
+ }
219
+
207
220
  private fun payWithFpx() {
208
221
  getCurrentActivityOrResolveWithError(confirmPromise)?.let {
209
222
  AddPaymentMethodActivityStarter(it)
@@ -866,14 +879,24 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
866
879
  }
867
880
  }
868
881
 
869
- /**
870
- * We need the following in order to avoid some annoying console.warns() from our Apple Pay event listeners. Otherwise,
871
- * we'd have to put our users through some annoying (if Platform.OS...) logic & null-handling logic.
872
- */
873
882
  @ReactMethod
874
- fun addListener(eventName: String) {}
883
+ fun addListener(eventName: String) {
884
+ eventListenerCount++
885
+ }
886
+
875
887
  @ReactMethod
876
- fun removeListeners(count: Int) {}
888
+ fun removeListeners(count: Int) {
889
+ eventListenerCount -= count
890
+ if (eventListenerCount < 0) {
891
+ eventListenerCount = 0
892
+ }
893
+ }
894
+
895
+ internal fun sendEvent(reactContext: ReactContext, eventName: String, params: WritableMap) {
896
+ reactContext
897
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
898
+ .emit(eventName, params)
899
+ }
877
900
 
878
901
  /**
879
902
  * Safely get and cast the current activity as an AppCompatActivity. If that fails, the promise
@@ -43,6 +43,8 @@ enum class GooglePayErrorType {
43
43
 
44
44
  class PaymentSheetAppearanceException(message: String) : Exception(message)
45
45
 
46
+ class PaymentSheetException(message: String) : Exception(message)
47
+
46
48
  internal fun mapError(code: String, message: String?, localizedMessage: String?, declineCode: String?, type: String?, stripeErrorCode: String?): WritableMap {
47
49
  val map: WritableMap = WritableNativeMap()
48
50
  val details: WritableMap = WritableNativeMap()
@@ -856,7 +856,20 @@ fun toBundleObject(readableMap: ReadableMap?): Bundle {
856
856
  }
857
857
  ReadableType.String -> result.putString(key, readableMap.getString(key))
858
858
  ReadableType.Map -> result.putBundle(key, toBundleObject(readableMap.getMap(key)))
859
- ReadableType.Array -> Log.e("toBundleException", "Cannot put arrays of objects into bundles. Failed on: $key.")
859
+ ReadableType.Array -> {
860
+ val list = readableMap.getArray(key)?.toArrayList()
861
+ if (list == null) {
862
+ result.putString(key, null)
863
+ } else if (list.isEmpty()) {
864
+ result.putStringArrayList(key, ArrayList())
865
+ } else {
866
+ when (list.first()) {
867
+ is String -> result.putStringArrayList(key, list as java.util.ArrayList<String>)
868
+ is Int -> result.putIntegerArrayList(key, list as java.util.ArrayList<Int>)
869
+ else -> Log.e("toBundleException", "Cannot put arrays of objects into bundles. Failed on: $key.")
870
+ }
871
+ }
872
+ }
860
873
  else -> Log.e("toBundleException", "Could not convert object with key: $key.")
861
874
  }
862
875
  }
@@ -17,25 +17,11 @@ class ApplePayButtonView: UIView {
17
17
  @objc var borderRadius: NSNumber?
18
18
  @objc var disabled = false
19
19
 
20
- func doesNothing(_: Optional<Dictionary<AnyHashable, Any>>) {
21
- return
22
- }
23
-
24
20
  @objc func handleApplePayButtonTapped() {
25
- if onPressAction != nil {
26
- onPressAction!(["true": true])
27
- // JS Callbacks are all no-ops since in legacy code (useApplePay hook),
28
- // this behavior is controlled via the onDidSetShippingMethod and onDidSetShippingContact
29
- // events
30
- stripeSdk?.shippingMethodUpdateJSCallback = doesNothing
31
- stripeSdk?.shippingContactUpdateJSCallback = doesNothing
32
- stripeSdk?.couponCodeEnteredJSCallback = doesNothing
33
- } else {
34
- stripeSdk?.shippingMethodUpdateJSCallback = onShippingMethodSelectedAction
35
- stripeSdk?.shippingContactUpdateJSCallback = onShippingContactSelectedAction
36
- stripeSdk?.couponCodeEnteredJSCallback = onCouponCodeEnteredAction
37
- stripeSdk?.platformPayOrderTrackingJSCallback = onOrderTrackingAction
38
- }
21
+ stripeSdk?.shippingMethodUpdateJSCallback = onShippingMethodSelectedAction
22
+ stripeSdk?.shippingContactUpdateJSCallback = onShippingContactSelectedAction
23
+ stripeSdk?.couponCodeEnteredJSCallback = onCouponCodeEnteredAction
24
+ stripeSdk?.platformPayOrderTrackingJSCallback = onOrderTrackingAction
39
25
  }
40
26
 
41
27
  override func didSetProps(_ changedProps: [String]!) {
@@ -122,10 +122,6 @@ extension StripeSdk : PKPaymentAuthorizationViewControllerDelegate, STPApplePayC
122
122
  didSelect shippingMethod: PKShippingMethod,
123
123
  handler: @escaping (PKPaymentRequestShippingMethodUpdate) -> Void
124
124
  ) {
125
- if (self.hasLegacyApplePayListeners) {
126
- // Legacy, remove when useApplePay hook is removed
127
- sendEvent(withName: "onDidSetShippingMethod", body: ["shippingMethod": Mappers.mapFromShippingMethod(shippingMethod: shippingMethod)])
128
- }
129
125
  if let callback = self.shippingMethodUpdateJSCallback {
130
126
  self.shippingMethodUpdateCompletion = handler
131
127
  callback(["shippingMethod": Mappers.mapFromShippingMethod(shippingMethod: shippingMethod)])
@@ -141,10 +137,6 @@ extension StripeSdk : PKPaymentAuthorizationViewControllerDelegate, STPApplePayC
141
137
  didSelectShippingContact contact: PKContact,
142
138
  handler: @escaping (PKPaymentRequestShippingContactUpdate) -> Void
143
139
  ) {
144
- if (self.hasLegacyApplePayListeners) {
145
- // Legacy, remove when useApplePay hook is removed
146
- sendEvent(withName: "onDidSetShippingContact", body: ["shippingContact": Mappers.mapFromShippingContact(shippingContact: contact)])
147
- }
148
140
  if let callback = self.shippingContactUpdateJSCallback {
149
141
  self.shippingContactUpdateCompletion = handler
150
142
  callback(["shippingContact": Mappers.mapFromShippingContact(shippingContact: contact)])
@@ -170,10 +162,7 @@ extension StripeSdk : PKPaymentAuthorizationViewControllerDelegate, STPApplePayC
170
162
  } else if let clientSecret = self.confirmApplePaySetupClientSecret {
171
163
  completion(clientSecret, nil)
172
164
  } else {
173
- self.applePayCompletionCallback = completion
174
- let method = Mappers.mapFromPaymentMethod(paymentMethod.splitApplePayAddressByNewline())
175
- self.deprecatedApplePayRequestResolver?(Mappers.createResult("paymentMethod", method))
176
- self.deprecatedApplePayRequestRejecter = nil
165
+ RCTMakeAndLogError("Tried to complete Apple Pay payment, but no client secret was found.", nil, nil)
177
166
  }
178
167
  }
179
168
 
@@ -233,44 +222,28 @@ extension StripeSdk : PKPaymentAuthorizationViewControllerDelegate, STPApplePayC
233
222
  }
234
223
  }
235
224
  }
236
-
237
- } else {
238
- deprecatedConfirmApplePayPaymentResolver?([])
239
225
  }
240
226
  break
241
227
  case .error:
242
228
  if let resolve = self.confirmApplePayResolver {
243
229
  resolve(Errors.createError(ErrorType.Failed, error as NSError?))
244
- } else {
245
- let message = "Payment not completed"
246
- deprecatedApplePayCompletionRejecter?(ErrorType.Failed, message, nil)
247
- deprecatedApplePayRequestRejecter?(ErrorType.Failed, message, nil)
248
230
  }
249
231
  break
250
232
  case .userCancellation:
251
233
  let message = "The payment has been canceled"
252
234
  if let resolve = self.confirmApplePayResolver {
253
235
  resolve(Errors.createError(ErrorType.Canceled, message))
254
- } else {
255
- deprecatedApplePayCompletionRejecter?(ErrorType.Canceled, message, nil)
256
- deprecatedApplePayRequestRejecter?(ErrorType.Canceled, message, nil)
257
236
  }
258
237
  break
259
238
  @unknown default:
260
239
  if let resolve = self.confirmApplePayResolver {
261
240
  resolve(Errors.createError(ErrorType.Unknown, error as NSError?))
262
- } else {
263
- let message = "Payment not completed"
264
- deprecatedApplePayCompletionRejecter?(ErrorType.Unknown, message, nil)
265
- deprecatedApplePayRequestRejecter?(ErrorType.Unknown, message, nil)
266
241
  }
267
242
  break
268
243
  }
269
244
  confirmApplePayResolver = nil
270
245
  confirmApplePayPaymentClientSecret = nil
271
246
  confirmApplePaySetupClientSecret = nil
272
- deprecatedApplePayCompletionRejecter = nil
273
- deprecatedApplePayRequestRejecter = nil
274
247
  }
275
248
 
276
249
  }
@@ -10,6 +10,7 @@ RCT_EXPORT_VIEW_PROPERTY(onFocusChange, RCTDirectEventBlock)
10
10
  RCT_EXPORT_VIEW_PROPERTY(cardStyle, NSDictionary)
11
11
  RCT_EXPORT_VIEW_PROPERTY(placeholders, NSDictionary)
12
12
  RCT_EXPORT_VIEW_PROPERTY(autofocus, BOOL)
13
+ RCT_EXPORT_VIEW_PROPERTY(disabled, BOOL)
13
14
  RCT_EXPORT_VIEW_PROPERTY(dangerouslyGetFullCardDetails, BOOL)
14
15
  RCT_EXTERN_METHOD(focus:(nonnull NSNumber*) reactTag)
15
16
  RCT_EXTERN_METHOD(blur:(nonnull NSNumber*) reactTag)
@@ -12,6 +12,12 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate {
12
12
  public var cardParams: STPPaymentMethodParams? = nil
13
13
  public var cardPostalCode: String? = nil
14
14
 
15
+ @objc var disabled: Bool = false {
16
+ didSet {
17
+ cardField.isUserInteractionEnabled = !disabled
18
+ }
19
+ }
20
+
15
21
  @objc var postalCodeEnabled: Bool = true {
16
22
  didSet {
17
23
  cardField.postalCodeEntryEnabled = postalCodeEnabled