@stripe/stripe-react-native 0.10.0 → 0.13.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 (72) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +3 -3
  3. package/android/gradle.properties +1 -1
  4. package/android/src/main/java/com/reactnativestripesdk/CardFieldView.kt +46 -10
  5. package/android/src/main/java/com/reactnativestripesdk/CardFieldViewManager.kt +5 -0
  6. package/android/src/main/java/com/reactnativestripesdk/CardFormView.kt +8 -0
  7. package/android/src/main/java/com/reactnativestripesdk/CardFormViewManager.kt +6 -1
  8. package/android/src/main/java/com/reactnativestripesdk/Errors.kt +14 -1
  9. package/android/src/main/java/com/reactnativestripesdk/GooglePayFragment.kt +68 -52
  10. package/android/src/main/java/com/reactnativestripesdk/Mappers.kt +5 -7
  11. package/android/src/main/java/com/reactnativestripesdk/PaymentLauncherFragment.kt +127 -37
  12. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +173 -0
  13. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +60 -36
  14. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +67 -177
  15. package/ios/CardFieldManager.m +1 -0
  16. package/ios/CardFieldView.swift +6 -0
  17. package/ios/Mappers.swift +8 -9
  18. package/ios/PaymentSheetAppearance.swift +209 -0
  19. package/ios/StripeSdk.swift +141 -116
  20. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  21. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  22. package/lib/commonjs/components/ApplePayButton.js +1 -1
  23. package/lib/commonjs/components/ApplePayButton.js.map +1 -1
  24. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  25. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  26. package/lib/commonjs/components/CardField.js +1 -1
  27. package/lib/commonjs/components/CardField.js.map +1 -1
  28. package/lib/commonjs/components/CardForm.js +1 -1
  29. package/lib/commonjs/components/CardForm.js.map +1 -1
  30. package/lib/commonjs/components/GooglePayButton.js +1 -1
  31. package/lib/commonjs/components/GooglePayButton.js.map +1 -1
  32. package/lib/commonjs/components/StripeContainer.js +1 -1
  33. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  34. package/lib/commonjs/functions.js.map +1 -1
  35. package/lib/commonjs/types/components/CardFieldInput.js.map +1 -1
  36. package/lib/commonjs/types/index.js.map +1 -1
  37. package/lib/module/components/AddToWalletButton.js +1 -1
  38. package/lib/module/components/AddToWalletButton.js.map +1 -1
  39. package/lib/module/components/ApplePayButton.js +1 -1
  40. package/lib/module/components/ApplePayButton.js.map +1 -1
  41. package/lib/module/components/AuBECSDebitForm.js +1 -1
  42. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  43. package/lib/module/components/CardField.js +1 -1
  44. package/lib/module/components/CardField.js.map +1 -1
  45. package/lib/module/components/CardForm.js +1 -1
  46. package/lib/module/components/CardForm.js.map +1 -1
  47. package/lib/module/components/GooglePayButton.js +1 -1
  48. package/lib/module/components/GooglePayButton.js.map +1 -1
  49. package/lib/module/components/StripeContainer.js +1 -1
  50. package/lib/module/components/StripeContainer.js.map +1 -1
  51. package/lib/module/functions.js.map +1 -1
  52. package/lib/module/types/components/CardFieldInput.js.map +1 -1
  53. package/lib/module/types/index.js.map +1 -1
  54. package/lib/typescript/example/src/screens/PaymentSheetAppearance.d.ts +3 -0
  55. package/lib/typescript/src/components/CardField.d.ts +3 -0
  56. package/lib/typescript/src/components/CardForm.d.ts +2 -0
  57. package/lib/typescript/src/types/PaymentSheet.d.ts +154 -1
  58. package/lib/typescript/src/types/Token.d.ts +9 -1
  59. package/lib/typescript/src/types/components/CardFieldInput.d.ts +1 -0
  60. package/lib/typescript/src/types/components/CardFormView.d.ts +6 -0
  61. package/lib/typescript/src/types/index.d.ts +1 -4
  62. package/package.json +1 -1
  63. package/src/components/CardField.tsx +5 -0
  64. package/src/components/CardForm.tsx +6 -0
  65. package/src/functions.ts +1 -1
  66. package/src/types/PaymentSheet.ts +159 -2
  67. package/src/types/Token.ts +13 -1
  68. package/src/types/components/CardFieldInput.ts +1 -0
  69. package/src/types/components/CardFormView.ts +7 -0
  70. package/src/types/index.ts +1 -5
  71. package/stripe-react-native.podspec +1 -1
  72. package/android/src/main/java/com/reactnativestripesdk/Constants.kt +0 -10
package/CHANGELOG.md CHANGED
@@ -2,12 +2,54 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.13.0
6
+
7
+ ### Breaking changes
8
+
9
+ ### New features
10
+
11
+ - Added a `defaultValues` prop to the `CardForm` component. Currently only accepts `countryCode`, and is Android-only. [#974](https://github.com/stripe/stripe-react-native/pull/974)
12
+ - Added the `countryCode` prop to the `CardField` component. [#989](https://github.com/stripe/stripe-react-native/pull/989)
13
+ - Added option to create a PII token (represents the details of personally identifiable information) to the `createToken` method. [#976](https://github.com/stripe/stripe-react-native/pull/976)
14
+
15
+ ### Fixes
16
+
17
+ - Resolve with an Error (of type `Canceled`) if no payment option is selected in the Payment Sheet custom flow (i.e., the `x` button is clicked to close the Payment Sheet). [#975](https://github.com/stripe/stripe-react-native/pull/975)
18
+ - Fixed an issue on Android where the `complete` field in the `onCardChange` callback would incorrectly be set to `true` even if the postal code wasn't filled out. [#989](https://github.com/stripe/stripe-react-native/pull/989)
19
+ - Make `SetupIntent.lastSetupError` and `PaymentIntent.lastPaymentError` object shape consistent on iOS and Android.[#990](https://github.com/stripe/stripe-react-native/pull/990)
20
+
21
+ ## 0.12.0
22
+
23
+ ### Breaking changes
24
+
25
+ - Renamed `appearance.shapes.shadow.borderRadius` to `appearance.shapes.shadow.blurRadius`, and `appearance.primaryButton.shapes.shadow.borderRadius` to `appearance.primaryButton.shapes.shadow.blurRadius`. [#962](https://github.com/stripe/stripe-react-native/pull/962)
26
+
27
+ ### New features
28
+
29
+ ### Fixes
30
+
31
+ - Fixed cases where Android apps would crash with the error: `Unable to instantiate fragment com.reactnativestripesdk.PaymentLauncherFragment`. [#965](https://github.com/stripe/stripe-react-native/pull/965)
32
+ - Fixed `appearance.shapes.shadow.offset` and `appearance.primaryButton.shapes.shadow.offset` not applying the y-coordinate in the correct direction. [#962](https://github.com/stripe/stripe-react-native/pull/962)
33
+ - Fixed a bug where `handleNextAction` wouldn't resolve on Android when using 3DS2. [#966](https://github.com/stripe/stripe-react-native/pull/966)
34
+ - Fixed a bug where the wrong CVC icon was show in the `CardForm` component on Android. [#966](https://github.com/stripe/stripe-react-native/pull/966)
35
+ - The card brand tint color is now correctly set in the `CardField` component on Android via the `cardStyle.textColor` prop. [#851](https://github.com/stripe/stripe-react-native/pull/851)
36
+
37
+ ## 0.11.0
38
+
5
39
  ### Breaking changes
6
40
 
41
+ - Removed support for `primaryButtonColor` field on `initPaymentSheet()`. Please use the new `appearance.primaryButton.colors.background` field instead. [#940](https://github.com/stripe/stripe-react-native/pull/940)
42
+
7
43
  ### New features
8
44
 
45
+ - You can now customize the appearance of your Payment Sheet via the `appearance` field on `initPaymentSheet()`. [#940](https://github.com/stripe/stripe-react-native/pull/940)
46
+ - Added Affirm and AU BECS Direct Debit support to Payment Sheet. [#940](https://github.com/stripe/stripe-react-native/pull/940)
47
+
9
48
  ### Fixes
10
49
 
50
+ - Improved error messages on Android for failed `confirmPayment` and `confirmSetupIntent` calls, and any Google Pay related methods. [#957](https://github.com/stripe/stripe-react-native/pull/957)
51
+ - Made Android card validation state consistent with iOS in the `CardField` `onCardChange` callback. [#958](https://github.com/stripe/stripe-react-native/pull/958)
52
+
11
53
  ## 0.10.0
12
54
 
13
55
  ### Breaking changes
package/README.md CHANGED
@@ -78,7 +78,7 @@ to your `app.json` file, where `merchantIdentifier` is the Apple merchant ID obt
78
78
 
79
79
  _Components_
80
80
 
81
- In order to use [CardForm](https://stripe.dev/stripe-react-native/api-reference/modules.html#CardForm) component, you need to install and configure [Material Components theme](https://github.com/material-components/material-components-android/blob/master/docs/getting-started.md#4-change-your-app-theme-to-inherit-from-a-material-components-theme) in your app.
81
+ In order to use [CardForm](https://stripe.dev/stripe-react-native/api-reference/index.html#CardForm) component, you need to install and configure [Material Components theme](https://github.com/material-components/material-components-android/blob/master/docs/getting-started.md#4-change-your-app-theme-to-inherit-from-a-material-components-theme) in your app.
82
82
 
83
83
  1. Add below dependency to your `app/build.gradle` file with specified version
84
84
 
@@ -96,7 +96,7 @@ implementation 'com.google.android.material:material:<version>'
96
96
 
97
97
  #### iOS
98
98
 
99
- Compatible with apps targeting iOS 12 or above.
99
+ The Stripe React Native SDK requires Xcode 13.2.1 or later and is compatible with apps targeting iOS 12 or above. For iOS 11 support, please use [`@stripe/stripe-react-native@0.5.0`](https://github.com/stripe/stripe-react-native/releases/tag/v0.5.0).
100
100
 
101
101
  The SDK uses TypeScript features available in Babel version `7.9.0` and above.
102
102
  Alternatively use the `plugin-transform-typescript` plugin in your project.
@@ -201,7 +201,7 @@ function App() {
201
201
  }
202
202
  ```
203
203
 
204
- You can find more details about the `StripeProvider` component in the [API reference](https://stripe.dev/stripe-react-native/api-reference/modules.html#stripeprovider).
204
+ You can find more details about the `StripeProvider` component in the [API reference](https://stripe.dev/stripe-react-native/api-reference/index.html#StripeProvider).
205
205
 
206
206
  ##### Additional steps for webhook forwarding
207
207
 
@@ -1,2 +1,2 @@
1
1
  StripeSdk_kotlinVersion=1.6.10
2
- StripeSdk_stripeVersion=20.3.+
2
+ StripeSdk_stripeVersion=20.5.+
@@ -6,7 +6,9 @@ import android.graphics.Typeface
6
6
  import android.os.Build
7
7
  import android.text.Editable
8
8
  import android.text.TextWatcher
9
+ import android.util.Log
9
10
  import android.widget.FrameLayout
11
+ import androidx.core.os.LocaleListCompat
10
12
  import com.facebook.react.bridge.ReadableMap
11
13
  import com.facebook.react.uimanager.ThemedReactContext
12
14
  import com.facebook.react.uimanager.UIManagerModule
@@ -14,12 +16,16 @@ import com.facebook.react.uimanager.events.EventDispatcher
14
16
  import com.google.android.material.shape.CornerFamily
15
17
  import com.google.android.material.shape.MaterialShapeDrawable
16
18
  import com.google.android.material.shape.ShapeAppearanceModel
19
+ import com.stripe.android.core.model.CountryCode
20
+ import com.stripe.android.core.model.CountryUtils
17
21
  import com.stripe.android.databinding.CardInputWidgetBinding
18
22
  import com.stripe.android.model.Address
19
23
  import com.stripe.android.model.PaymentMethodCreateParams
20
24
  import com.stripe.android.view.CardInputListener
21
25
  import com.stripe.android.view.CardInputWidget
22
26
  import com.stripe.android.view.CardValidCallback
27
+ import com.stripe.android.view.StripeEditText
28
+ import java.lang.Exception
23
29
 
24
30
  class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
25
31
  private var mCardWidget: CardInputWidget = CardInputWidget(context)
@@ -30,6 +36,7 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
30
36
  private var mEventDispatcher: EventDispatcher? = context.getNativeModule(UIManagerModule::class.java)?.eventDispatcher
31
37
  private var dangerouslyGetFullCardDetails: Boolean = false
32
38
  private var currentFocusedField: String? = null
39
+ private var isCardValid = false
33
40
 
34
41
  init {
35
42
  cardInputWidgetBinding.container.isFocusable = true
@@ -89,7 +96,8 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
89
96
  cardInputWidgetBinding.cardNumberEditText,
90
97
  cardInputWidgetBinding.cvcEditText,
91
98
  cardInputWidgetBinding.expiryDateEditText,
92
- cardInputWidgetBinding.postalCodeEditText)
99
+ cardInputWidgetBinding.postalCodeEditText
100
+ )
93
101
 
94
102
  textColor?.let {
95
103
  for (editTextBinding in bindings) {
@@ -105,6 +113,7 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
105
113
  for (editTextBinding in bindings) {
106
114
  editTextBinding.setHintTextColor(Color.parseColor(it))
107
115
  }
116
+ setCardBrandTint(Color.parseColor(it))
108
117
  }
109
118
  fontSize?.let {
110
119
  for (editTextBinding in bindings) {
@@ -151,6 +160,19 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
151
160
  }
152
161
  }
153
162
 
163
+ private fun setCardBrandTint(color: Int) {
164
+ try {
165
+ cardInputWidgetBinding.cardBrandView::class.java.getDeclaredField("tintColorInt").let { internalTintColor ->
166
+ internalTintColor.isAccessible = true
167
+ internalTintColor.set(cardInputWidgetBinding.cardBrandView, color)
168
+ }
169
+ } catch (e: Exception) {
170
+ Log.e(
171
+ "StripeReactNative",
172
+ "Unable to set card brand tint color: " + e.message)
173
+ }
174
+ }
175
+
154
176
  fun setPlaceHolders(value: ReadableMap) {
155
177
  val numberPlaceholder = getValOr(value, "number", null)
156
178
  val expirationPlaceholder = getValOr(value, "expiration", null)
@@ -179,6 +201,14 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
179
201
  mCardWidget.postalCodeEnabled = isEnabled
180
202
  }
181
203
 
204
+ fun setCountryCode(countryCode: String?) {
205
+ val doesCountryUsePostalCode = CountryUtils.doesCountryUsePostalCode(
206
+ CountryCode.create(value = countryCode ?: LocaleListCompat.getAdjustedDefault()[0].country)
207
+ )
208
+ mCardWidget.postalCodeRequired = doesCountryUsePostalCode
209
+ mCardWidget.postalCodeEnabled = doesCountryUsePostalCode
210
+ }
211
+
182
212
  fun getValue(): MutableMap<String, Any?> {
183
213
  return cardDetails
184
214
  }
@@ -206,7 +236,7 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
206
236
 
207
237
  private fun sendCardDetailsEvent() {
208
238
  mEventDispatcher?.dispatchEvent(
209
- CardChangedEvent(id, cardDetails, mCardWidget.postalCodeEnabled, cardParams != null, dangerouslyGetFullCardDetails))
239
+ CardChangedEvent(id, cardDetails, mCardWidget.postalCodeEnabled, isCardValid, dangerouslyGetFullCardDetails))
210
240
  }
211
241
 
212
242
  private fun setListeners() {
@@ -228,14 +258,25 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
228
258
  }
229
259
 
230
260
  mCardWidget.setCardValidCallback { isValid, invalidFields ->
231
- cardDetails["validNumber"] = if (invalidFields.contains(CardValidCallback.Fields.Number)) "Invalid" else "Valid"
232
- cardDetails["validCVC"] = if (invalidFields.contains(CardValidCallback.Fields.Cvc)) "Invalid" else "Valid"
233
- cardDetails["validExpiryDate"] = if (invalidFields.contains(CardValidCallback.Fields.Expiry)) "Invalid" else "Valid"
261
+ isCardValid = isValid
262
+ fun getCardValidationState(field: CardValidCallback.Fields, editTextField: StripeEditText): String {
263
+ if (invalidFields.contains(field)) {
264
+ return if (editTextField.shouldShowError) "Invalid"
265
+ else "Incomplete"
266
+ }
267
+ return "Valid"
268
+ }
269
+
270
+ cardDetails["validNumber"] = getCardValidationState(CardValidCallback.Fields.Number, cardInputWidgetBinding.cardNumberEditText)
271
+ cardDetails["validCVC"] = getCardValidationState(CardValidCallback.Fields.Cvc, cardInputWidgetBinding.cvcEditText)
272
+ cardDetails["validExpiryDate"] = getCardValidationState(CardValidCallback.Fields.Expiry, cardInputWidgetBinding.expiryDateEditText)
273
+
234
274
  if (isValid) {
235
275
  onValidCardChange()
236
276
  } else {
237
277
  cardParams = null
238
278
  cardAddress = null
279
+ sendCardDetailsEvent()
239
280
  }
240
281
  }
241
282
 
@@ -257,8 +298,6 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
257
298
  if (splitText.size == 2) {
258
299
  cardDetails["expiryYear"] = var1.toString().split("/")[1].toIntOrNull()
259
300
  }
260
-
261
- sendCardDetailsEvent()
262
301
  }
263
302
  })
264
303
 
@@ -267,7 +306,6 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
267
306
  override fun afterTextChanged(p0: Editable?) {}
268
307
  override fun onTextChanged(var1: CharSequence?, var2: Int, var3: Int, var4: Int) {
269
308
  cardDetails["postalCode"] = var1.toString()
270
- sendCardDetailsEvent()
271
309
  }
272
310
  })
273
311
 
@@ -278,7 +316,6 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
278
316
  if (dangerouslyGetFullCardDetails) {
279
317
  cardDetails["number"] = var1.toString().replace(" ", "")
280
318
  }
281
- sendCardDetailsEvent()
282
319
  }
283
320
  })
284
321
 
@@ -289,7 +326,6 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
289
326
  if (dangerouslyGetFullCardDetails) {
290
327
  cardDetails["cvc"] = var1.toString()
291
328
  }
292
- sendCardDetailsEvent()
293
329
  }
294
330
  })
295
331
  }
@@ -51,6 +51,11 @@ class CardFieldViewManager : SimpleViewManager<CardFieldView>() {
51
51
  view.setPlaceHolders(placeholders)
52
52
  }
53
53
 
54
+ @ReactProp(name = "countryCode")
55
+ fun setPlaceHolders(view: CardFieldView, countryCode: String?) {
56
+ view.setCountryCode(countryCode)
57
+ }
58
+
54
59
  override fun createViewInstance(reactContext: ThemedReactContext): CardFieldView {
55
60
  val stripeSdkModule: StripeSdkModule? = reactContext.getNativeModule(StripeSdkModule::class.java)
56
61
  val view = CardFieldView(reactContext)
@@ -14,6 +14,7 @@ import com.facebook.react.uimanager.events.EventDispatcher
14
14
  import com.google.android.material.shape.CornerFamily
15
15
  import com.google.android.material.shape.MaterialShapeDrawable
16
16
  import com.google.android.material.shape.ShapeAppearanceModel
17
+ import com.stripe.android.core.model.CountryCode
17
18
  import com.stripe.android.databinding.CardMultilineWidgetBinding
18
19
  import com.stripe.android.databinding.StripeCardFormViewBinding
19
20
  import com.stripe.android.model.Address
@@ -48,6 +49,13 @@ class CardFormView(context: ThemedReactContext) : FrameLayout(context) {
48
49
  cardFormViewBinding.postalCodeContainer.visibility = visibility
49
50
  }
50
51
 
52
+ fun setDefaultValues(defaults: ReadableMap) {
53
+ defaults.getString("countryCode")?.let {
54
+ cardFormViewBinding.countryLayout.setSelectedCountryCode(CountryCode(it))
55
+ cardFormViewBinding.countryLayout.updateUiForCountryEntered(CountryCode(it))
56
+ }
57
+ }
58
+
51
59
  fun setPlaceHolders(value: ReadableMap) {
52
60
  val numberPlaceholder = getValOr(value, "number", null)
53
61
  val expirationPlaceholder = getValOr(value, "expiration", null)
@@ -38,7 +38,7 @@ class CardFormViewManager : SimpleViewManager<CardFormView>() {
38
38
 
39
39
  @ReactProp(name = "placeholders")
40
40
  fun setPlaceHolders(view: CardFormView, placeholders: ReadableMap) {
41
- view.setPlaceHolders(placeholders);
41
+ view.setPlaceHolders(placeholders)
42
42
  }
43
43
 
44
44
  @ReactProp(name = "autofocus")
@@ -51,6 +51,11 @@ class CardFormViewManager : SimpleViewManager<CardFormView>() {
51
51
  view.setCardStyle(cardStyle)
52
52
  }
53
53
 
54
+ @ReactProp(name = "defaultValues")
55
+ fun setDefaultValues(view: CardFormView, defaults: ReadableMap) {
56
+ view.setDefaultValues(defaults)
57
+ }
58
+
54
59
  override fun createViewInstance(reactContext: ThemedReactContext): CardFormView {
55
60
  val stripeSdkModule: StripeSdkModule? = reactContext.getNativeModule(StripeSdkModule::class.java)
56
61
  val view = CardFormView(reactContext)
@@ -41,6 +41,8 @@ enum class GooglePayErrorType {
41
41
  Failed, Canceled
42
42
  }
43
43
 
44
+ class PaymentSheetAppearanceException(message: String) : Exception(message)
45
+
44
46
  internal fun mapError(code: String, message: String?, localizedMessage: String?, declineCode: String?, type: String?, stripeErrorCode: String?): WritableMap {
45
47
  val map: WritableMap = WritableNativeMap()
46
48
  val details: WritableMap = WritableNativeMap()
@@ -96,5 +98,16 @@ internal fun createError(code: String, error: Exception): WritableMap {
96
98
  }
97
99
 
98
100
  internal fun createError(code: String, error: Throwable): WritableMap {
99
- return mapError(code, error.message, error.localizedMessage, null, null, null)
101
+ (error as? Exception)?.let {
102
+ return createError(
103
+ code,
104
+ it)
105
+ }
106
+ return mapError(
107
+ code,
108
+ error.message,
109
+ error.localizedMessage,
110
+ null,
111
+ null,
112
+ null)
100
113
  }
@@ -1,44 +1,30 @@
1
1
  package com.reactnativestripesdk
2
2
 
3
- import android.content.Intent
4
3
  import android.os.Bundle
5
4
  import android.view.LayoutInflater
6
5
  import android.view.View
7
6
  import android.view.ViewGroup
8
7
  import android.widget.FrameLayout
9
8
  import androidx.fragment.app.Fragment
10
- import androidx.localbroadcastmanager.content.LocalBroadcastManager
9
+ import com.facebook.react.bridge.Promise
10
+ import com.facebook.react.bridge.WritableNativeMap
11
11
  import com.stripe.android.googlepaylauncher.GooglePayEnvironment
12
12
  import com.stripe.android.googlepaylauncher.GooglePayLauncher
13
13
  import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncher
14
14
 
15
- class GooglePayFragment : Fragment() {
15
+ class GooglePayFragment(private val initPromise: Promise) : Fragment() {
16
16
  private var googlePayLauncher: GooglePayLauncher? = null
17
17
  private var googlePayMethodLauncher: GooglePayPaymentMethodLauncher? = null
18
18
  private var isGooglePayMethodLauncherReady: Boolean = false
19
19
  private var isGooglePayLauncherReady: Boolean = false
20
- private lateinit var localBroadcastManager: LocalBroadcastManager
21
-
22
- private fun onGooglePayMethodLauncherReady(isReady: Boolean) {
23
- isGooglePayMethodLauncherReady = true
24
- if (isGooglePayLauncherReady) {
25
- onGooglePayReady(isReady)
26
- }
27
- }
28
-
29
- private fun onGooglePayLauncherReady(isReady: Boolean) {
30
- isGooglePayLauncherReady = true
31
- if (isGooglePayMethodLauncherReady) {
32
- onGooglePayReady(isReady)
33
- }
34
- }
20
+ private var presentPromise: Promise? = null
21
+ private var createPaymentMethodPromise: Promise? = null
35
22
 
36
23
  override fun onCreateView(
37
24
  inflater: LayoutInflater,
38
25
  container: ViewGroup?,
39
26
  savedInstanceState: Bundle?
40
27
  ): View {
41
- localBroadcastManager = LocalBroadcastManager.getInstance(requireContext())
42
28
  return FrameLayout(requireActivity()).also {
43
29
  it.visibility = View.GONE
44
30
  }
@@ -89,74 +75,104 @@ class GooglePayFragment : Fragment() {
89
75
  )
90
76
  }
91
77
 
92
- fun presentForPaymentIntent(clientSecret: String) {
78
+ private fun onGooglePayMethodLauncherReady(isReady: Boolean) {
79
+ isGooglePayMethodLauncherReady = true
80
+ if (isGooglePayLauncherReady) {
81
+ onGooglePayReady(isReady)
82
+ }
83
+ }
84
+
85
+ private fun onGooglePayLauncherReady(isReady: Boolean) {
86
+ isGooglePayLauncherReady = true
87
+ if (isGooglePayMethodLauncherReady) {
88
+ onGooglePayReady(isReady)
89
+ }
90
+ }
91
+
92
+ private fun onGooglePayReady(isReady: Boolean) {
93
+ if (isReady) {
94
+ initPromise.resolve(WritableNativeMap())
95
+ } else {
96
+ initPromise.resolve(
97
+ createError(
98
+ GooglePayErrorType.Failed.toString(),
99
+ "Google Pay is not available on this device. You can use isGooglePaySupported to preemptively check for Google Pay support."
100
+ )
101
+ )
102
+ }
103
+ }
104
+
105
+ fun presentForPaymentIntent(clientSecret: String, promise: Promise) {
93
106
  val launcher = googlePayLauncher ?: run {
94
- val intent = Intent(ON_GOOGLE_PAY_RESULT)
95
- intent.putExtra("error", "GooglePayLauncher is not initialized.")
96
- localBroadcastManager.sendBroadcast(intent)
107
+ promise.resolve(createError(GooglePayErrorType.Failed.toString(), "GooglePay is not initialized."))
97
108
  return
98
109
  }
99
110
  runCatching {
111
+ presentPromise = promise
100
112
  launcher.presentForPaymentIntent(clientSecret)
101
113
  }.onFailure {
102
- val intent = Intent(ON_GOOGLE_PAY_RESULT)
103
- intent.putExtra("error", it.localizedMessage)
104
- localBroadcastManager.sendBroadcast(intent)
114
+ promise.resolve(createError(GooglePayErrorType.Failed.toString(), it))
105
115
  }
106
116
  }
107
117
 
108
- fun presentForSetupIntent(clientSecret: String, currencyCode: String) {
118
+ fun presentForSetupIntent(clientSecret: String, currencyCode: String, promise: Promise) {
109
119
  val launcher = googlePayLauncher ?: run {
110
- val intent = Intent(ON_GOOGLE_PAY_RESULT)
111
- intent.putExtra("error", "GooglePayLauncher is not initialized.")
112
- localBroadcastManager.sendBroadcast(intent)
120
+ promise.resolve(createError(GooglePayErrorType.Failed.toString(), "GooglePay is not initialized."))
113
121
  return
114
122
  }
115
123
  runCatching {
124
+ presentPromise = promise
116
125
  launcher.presentForSetupIntent(clientSecret, currencyCode)
117
126
  }.onFailure {
118
- val intent = Intent(ON_GOOGLE_PAY_RESULT)
119
- intent.putExtra("error", it.localizedMessage)
120
- localBroadcastManager.sendBroadcast(intent)
127
+ promise.resolve(createError(GooglePayErrorType.Failed.toString(), it))
121
128
  }
122
129
  }
123
130
 
124
- fun createPaymentMethod(currencyCode: String, amount: Int) {
131
+ fun createPaymentMethod(currencyCode: String, amount: Int, promise: Promise) {
125
132
  val launcher = googlePayMethodLauncher ?: run {
126
- val intent = Intent(ON_GOOGLE_PAYMENT_METHOD_RESULT)
127
- intent.putExtra("error", "GooglePayPaymentMethodLauncher is not initialized.")
128
- localBroadcastManager.sendBroadcast(intent)
133
+ promise.resolve(createError(GooglePayErrorType.Failed.toString(), "GooglePayPaymentMethodLauncher is not initialized."))
129
134
  return
130
135
  }
131
136
 
132
137
  runCatching {
138
+ createPaymentMethodPromise = promise
133
139
  launcher.present(
134
140
  currencyCode = currencyCode,
135
141
  amount = amount
136
142
  )
137
143
  }.onFailure {
138
- val intent = Intent(ON_GOOGLE_PAYMENT_METHOD_RESULT)
139
- intent.putExtra("error", it.localizedMessage)
140
- localBroadcastManager.sendBroadcast(intent)
144
+ promise.resolve(createError(GooglePayErrorType.Failed.toString(), it))
141
145
  }
142
146
  }
143
147
 
144
- private fun onGooglePayReady(isReady: Boolean) {
145
- val intent = Intent(ON_INIT_GOOGLE_PAY)
146
- intent.putExtra("isReady", isReady)
147
- localBroadcastManager.sendBroadcast(intent)
148
- }
149
-
150
148
  private fun onGooglePayResult(result: GooglePayLauncher.Result) {
151
- val intent = Intent(ON_GOOGLE_PAY_RESULT)
152
- intent.putExtra("paymentResult", result)
153
- localBroadcastManager.sendBroadcast(intent)
149
+ when (result) {
150
+ GooglePayLauncher.Result.Completed -> {
151
+ presentPromise?.resolve(WritableNativeMap())
152
+ }
153
+ GooglePayLauncher.Result.Canceled -> {
154
+ presentPromise?.resolve(createError(GooglePayErrorType.Canceled.toString(), "Google Pay has been canceled"))
155
+ }
156
+ is GooglePayLauncher.Result.Failed -> {
157
+ presentPromise?.resolve(createError(GooglePayErrorType.Failed.toString(), result.error))
158
+ }
159
+ }
160
+ presentPromise = null
154
161
  }
155
162
 
156
163
  private fun onGooglePayResult(result: GooglePayPaymentMethodLauncher.Result) {
157
- val intent = Intent(ON_GOOGLE_PAYMENT_METHOD_RESULT)
158
- intent.putExtra("paymentResult", result)
159
- localBroadcastManager.sendBroadcast(intent)
164
+ when (result) {
165
+ is GooglePayPaymentMethodLauncher.Result.Completed -> {
166
+ createPaymentMethodPromise?.resolve(createResult("paymentMethod", mapFromPaymentMethod(result.paymentMethod)))
167
+ }
168
+ GooglePayPaymentMethodLauncher.Result.Canceled -> {
169
+ createPaymentMethodPromise?.resolve(createError(GooglePayErrorType.Canceled.toString(), "Google Pay has been canceled"))
170
+ }
171
+ is GooglePayPaymentMethodLauncher.Result.Failed -> {
172
+ createPaymentMethodPromise?.resolve(createError(GooglePayErrorType.Failed.toString(), result.error))
173
+ }
174
+ }
175
+ createPaymentMethodPromise = null
160
176
  }
161
177
 
162
178
  private fun mapToGooglePayLauncherBillingAddressConfig(formatString: String, isRequired: Boolean, isPhoneNumberRequired: Boolean): GooglePayLauncher.BillingAddressConfig {
@@ -402,14 +402,13 @@ internal fun mapFromPaymentIntentResult(paymentIntent: PaymentIntent): WritableM
402
402
 
403
403
  paymentIntent.lastPaymentError?.let {
404
404
  val paymentError: WritableMap = WritableNativeMap()
405
-
406
- paymentIntent.lastPaymentError?.paymentMethod?.let { paymentMethod ->
407
- paymentError.putMap("paymentMethod", mapFromPaymentMethod(paymentMethod))
408
- }
409
-
410
405
  paymentError.putString("code", it.code)
411
406
  paymentError.putString("message", it.message)
412
407
  paymentError.putString("type", mapFromPaymentIntentLastErrorType(it.type))
408
+ paymentError.putString("declineCode", it.declineCode)
409
+ paymentIntent.lastPaymentError?.paymentMethod?.let { paymentMethod ->
410
+ paymentError.putMap("paymentMethod", mapFromPaymentMethod(paymentMethod))
411
+ }
413
412
 
414
413
  map.putMap("lastPaymentError", paymentError)
415
414
  }
@@ -775,11 +774,10 @@ internal fun mapFromSetupIntentResult(setupIntent: SetupIntent): WritableMap {
775
774
  setupError.putString("code", it.code)
776
775
  setupError.putString("message", it.message)
777
776
  setupError.putString("type", mapFromSetupIntentLastErrorType(it.type))
778
-
777
+ setupError.putString("declineCode", it.declineCode)
779
778
  setupIntent.lastSetupError?.paymentMethod?.let { paymentMethod ->
780
779
  setupError.putMap("paymentMethod", mapFromPaymentMethod(paymentMethod))
781
780
  }
782
-
783
781
  map.putMap("lastSetupError", setupError)
784
782
  }
785
783