@stripe/stripe-react-native 0.20.0 → 0.21.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 (56) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/android/gradle.properties +1 -1
  3. package/android/src/main/java/com/reactnativestripesdk/CollectBankAccountLauncherFragment.kt +1 -1
  4. package/android/src/main/java/com/reactnativestripesdk/FinancialConnectionsSheetFragment.kt +4 -1
  5. package/android/src/main/java/com/reactnativestripesdk/GooglePayFragment.kt +1 -1
  6. package/android/src/main/java/com/reactnativestripesdk/PaymentLauncherFragment.kt +1 -1
  7. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +10 -9
  8. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +8 -3
  9. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +20 -14
  10. package/android/src/main/java/com/reactnativestripesdk/StripeSdkPackage.kt +3 -1
  11. package/android/src/main/java/com/reactnativestripesdk/addresssheet/AddressLauncherFragment.kt +111 -0
  12. package/android/src/main/java/com/reactnativestripesdk/addresssheet/AddressSheetEvent.kt +28 -0
  13. package/android/src/main/java/com/reactnativestripesdk/addresssheet/AddressSheetView.kt +178 -0
  14. package/android/src/main/java/com/reactnativestripesdk/addresssheet/AddressSheetViewManager.kt +67 -0
  15. package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +1 -1
  16. package/ios/AddressSheet/AddressSheetUtils.swift +98 -0
  17. package/ios/AddressSheet/AddressSheetView.swift +131 -0
  18. package/ios/AddressSheet/AddressSheetViewManager.m +25 -0
  19. package/ios/AddressSheet/AddressSheetViewManager.swift +19 -0
  20. package/ios/PaymentSheetAppearance.swift +24 -23
  21. package/ios/{pushprovisioning → PushProvisioning}/AddToWalletButtonManager.m +0 -0
  22. package/ios/{pushprovisioning → PushProvisioning}/AddToWalletButtonManager.swift +0 -0
  23. package/ios/{pushprovisioning → PushProvisioning}/AddToWalletButtonView.swift +0 -0
  24. package/ios/{pushprovisioning → PushProvisioning}/PushProvisioningUtils.swift +0 -0
  25. package/ios/StripeSdk.swift +7 -1
  26. package/ios/Tests/AddressSheetUtilsTests.swift +279 -0
  27. package/lib/commonjs/components/AddressSheet.js +2 -0
  28. package/lib/commonjs/components/AddressSheet.js.map +1 -0
  29. package/lib/commonjs/index.js +1 -1
  30. package/lib/commonjs/index.js.map +1 -1
  31. package/lib/commonjs/types/Errors.js +1 -1
  32. package/lib/commonjs/types/Errors.js.map +1 -1
  33. package/lib/commonjs/types/index.js +1 -1
  34. package/lib/commonjs/types/index.js.map +1 -1
  35. package/lib/module/components/AddressSheet.js +2 -0
  36. package/lib/module/components/AddressSheet.js.map +1 -0
  37. package/lib/module/index.js +1 -1
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/types/Errors.js +1 -1
  40. package/lib/module/types/Errors.js.map +1 -1
  41. package/lib/module/types/index.js +1 -1
  42. package/lib/module/types/index.js.map +1 -1
  43. package/lib/typescript/src/components/AddressSheet.d.ts +53 -0
  44. package/lib/typescript/src/index.d.ts +1 -0
  45. package/lib/typescript/src/types/Common.d.ts +12 -0
  46. package/lib/typescript/src/types/Errors.d.ts +4 -0
  47. package/lib/typescript/src/types/PaymentSheet.d.ts +7 -1
  48. package/lib/typescript/src/types/index.d.ts +3 -6
  49. package/package.json +1 -1
  50. package/src/components/AddressSheet.tsx +82 -0
  51. package/src/index.tsx +4 -1
  52. package/src/types/Common.ts +13 -0
  53. package/src/types/Errors.ts +5 -0
  54. package/src/types/PaymentSheet.ts +7 -1
  55. package/src/types/index.ts +5 -10
  56. package/stripe-react-native.podspec +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.21.0 - 2022-11-15
6
+
7
+ ### Breaking changes
8
+
9
+ ### New features
10
+
11
+ - Added the `<AddressSheet />` component, which enables you to collect local and international shipping or billing addresses from your customers _with_ address autocomplete. [#1169](https://github.com/stripe/stripe-react-native/pull/1169)
12
+ - Added the `defaultShippingDetails` field to the `params` argument in `initPaymentSheet(params)`. This will allow you to collect shipping details (either in your own UI or using the new `<AddressSheet />` component) for payments in the Payment Sheet. [#1169](https://github.com/stripe/stripe-react-native/pull/1169)
13
+
14
+ ## Fixes
15
+
16
+ - Fixed a build error on Android when using Kotlin version 1.7.10. [#1195](https://github.com/stripe/stripe-react-native/pull/1195)
17
+
5
18
  ## 0.20.0 - 2022-11-03
6
19
 
7
20
  ### Breaking changes
@@ -1,2 +1,2 @@
1
1
  StripeSdk_kotlinVersion=1.6.21
2
- StripeSdk_stripeVersion=20.15.+
2
+ StripeSdk_stripeVersion=20.16.+
@@ -91,6 +91,6 @@ class CollectBankAccountLauncherFragment(
91
91
  }
92
92
 
93
93
  companion object {
94
- const val TAG = "collect_bank_account_launcher_fragment"
94
+ internal const val TAG = "collect_bank_account_launcher_fragment"
95
95
  }
96
96
  }
@@ -125,7 +125,7 @@ class FinancialConnectionsSheetFragment : Fragment() {
125
125
  private fun commitFragmentAndStartFlow(currentActivity: AppCompatActivity) {
126
126
  try {
127
127
  currentActivity.supportFragmentManager.beginTransaction()
128
- .add(this, "financial_connections_sheet_launch_fragment")
128
+ .add(this, TAG)
129
129
  .commit()
130
130
  } catch (error: IllegalStateException) {
131
131
  promise.resolve(createError(ErrorType.Failed.toString(), error.message))
@@ -133,6 +133,8 @@ class FinancialConnectionsSheetFragment : Fragment() {
133
133
  }
134
134
 
135
135
  companion object {
136
+ internal const val TAG = "financial_connections_sheet_launch_fragment"
137
+
136
138
  private fun createTokenResult(result: FinancialConnectionsSheetForTokenResult.Completed): WritableMap {
137
139
  return WritableNativeMap().also {
138
140
  it.putMap("session", mapFromSession(result.financialConnectionsSession))
@@ -239,6 +241,7 @@ class FinancialConnectionsSheetFragment : Fragment() {
239
241
  FinancialConnectionsAccount.Permissions.TRANSACTIONS -> "transactions"
240
242
  FinancialConnectionsAccount.Permissions.ACCOUNT_NUMBERS -> "accountNumbers"
241
243
  FinancialConnectionsAccount.Permissions.UNKNOWN -> "unparsable"
244
+ FinancialConnectionsAccount.Permissions.ACCOUNT_NUMBERS -> "accountNumbers"
242
245
  }
243
246
  }
244
247
 
@@ -206,6 +206,6 @@ class GooglePayFragment(private val initPromise: Promise) : Fragment() {
206
206
  }
207
207
 
208
208
  companion object {
209
- const val TAG = "google_pay_launch_fragment"
209
+ internal const val TAG = "google_pay_launch_fragment"
210
210
  }
211
211
  }
@@ -120,7 +120,7 @@ class PaymentLauncherFragment(
120
120
  }
121
121
  }
122
122
 
123
- const val TAG = "payment_launcher_fragment"
123
+ internal const val TAG = "payment_launcher_fragment"
124
124
  }
125
125
 
126
126
  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@@ -2,27 +2,28 @@ package com.reactnativestripesdk
2
2
 
3
3
  import android.graphics.Color
4
4
  import android.os.Bundle
5
+ import com.facebook.react.bridge.ReactContext
5
6
  import com.reactnativestripesdk.utils.PaymentSheetAppearanceException
6
7
  import com.stripe.android.paymentsheet.PaymentSheet
7
8
 
8
- fun PaymentSheetFragment.buildPaymentSheetAppearance(userParams: Bundle?): PaymentSheet.Appearance {
9
+ fun buildPaymentSheetAppearance(userParams: Bundle?, context: ReactContext): PaymentSheet.Appearance {
9
10
  val colorParams = userParams?.getBundle(PaymentSheetAppearanceKeys.COLORS)
10
11
  val lightColorParams = colorParams?.getBundle(PaymentSheetAppearanceKeys.LIGHT) ?: colorParams
11
12
  val darkColorParams = colorParams?.getBundle(PaymentSheetAppearanceKeys.DARK) ?: colorParams
12
13
 
13
14
  return PaymentSheet.Appearance(
14
- typography = buildTypography(userParams?.getBundle(PaymentSheetAppearanceKeys.FONT)),
15
+ typography = buildTypography(userParams?.getBundle(PaymentSheetAppearanceKeys.FONT), context),
15
16
  colorsLight = buildColors(lightColorParams, PaymentSheet.Colors.defaultLight),
16
17
  colorsDark = buildColors(darkColorParams, PaymentSheet.Colors.defaultDark),
17
18
  shapes = buildShapes(userParams?.getBundle(PaymentSheetAppearanceKeys.SHAPES)),
18
- primaryButton = buildPrimaryButton(userParams?.getBundle(PaymentSheetAppearanceKeys.PRIMARY_BUTTON))
19
+ primaryButton = buildPrimaryButton(userParams?.getBundle(PaymentSheetAppearanceKeys.PRIMARY_BUTTON), context)
19
20
  )
20
21
  }
21
22
 
22
- private fun PaymentSheetFragment.buildTypography(fontParams: Bundle?): PaymentSheet.Typography {
23
+ private fun buildTypography(fontParams: Bundle?, context: ReactContext): PaymentSheet.Typography {
23
24
  return PaymentSheet.Typography.default.copy(
24
25
  sizeScaleFactor = getFloatOr(fontParams, PaymentSheetAppearanceKeys.SCALE, PaymentSheet.Typography.default.sizeScaleFactor),
25
- fontResId = getFontResId(fontParams, PaymentSheetAppearanceKeys.FAMILY, PaymentSheet.Typography.default.fontResId)
26
+ fontResId = getFontResId(fontParams, PaymentSheetAppearanceKeys.FAMILY, PaymentSheet.Typography.default.fontResId, context)
26
27
  )
27
28
  }
28
29
 
@@ -64,7 +65,7 @@ private fun buildShapes(shapeParams: Bundle?): PaymentSheet.Shapes {
64
65
  )
65
66
  }
66
67
 
67
- private fun PaymentSheetFragment.buildPrimaryButton(params: Bundle?): PaymentSheet.PrimaryButton {
68
+ private fun buildPrimaryButton(params: Bundle?, context: ReactContext): PaymentSheet.PrimaryButton {
68
69
  if (params == null) {
69
70
  return PaymentSheet.PrimaryButton()
70
71
  }
@@ -83,7 +84,7 @@ private fun PaymentSheetFragment.buildPrimaryButton(params: Bundle?): PaymentShe
83
84
  borderStrokeWidthDp = getFloatOrNull(shapeParams, PaymentSheetAppearanceKeys.BORDER_WIDTH),
84
85
  ),
85
86
  typography = PaymentSheet.PrimaryButtonTypography(
86
- fontResId = getFontResId(fontParams, PaymentSheetAppearanceKeys.FAMILY, null)
87
+ fontResId = getFontResId(fontParams, PaymentSheetAppearanceKeys.FAMILY, null, context)
87
88
  )
88
89
  )
89
90
  }
@@ -120,7 +121,7 @@ private fun getFloatOrNull(bundle: Bundle?, key: String): Float? {
120
121
  }
121
122
 
122
123
  @Throws(PaymentSheetAppearanceException::class)
123
- private fun PaymentSheetFragment.getFontResId(bundle: Bundle?, key: String, defaultValue: Int?): Int? {
124
+ private fun getFontResId(bundle: Bundle?, key: String, defaultValue: Int?, context: ReactContext): Int? {
124
125
  val fontErrorPrefix = "Encountered an error when setting a custom font:"
125
126
  if (bundle?.containsKey(key) != true) {
126
127
  return defaultValue
@@ -134,7 +135,7 @@ private fun PaymentSheetFragment.getFontResId(bundle: Bundle?, key: String, defa
134
135
  )
135
136
  }
136
137
 
137
- val id = resources.getIdentifier(fontFileName, "font", context?.packageName)
138
+ val id = context.resources.getIdentifier(fontFileName, "font", context.packageName)
138
139
  if (id == 0) {
139
140
  throw PaymentSheetAppearanceException("$fontErrorPrefix Failed to find font: $fontFileName")
140
141
  } else {
@@ -10,7 +10,6 @@ import android.view.LayoutInflater
10
10
  import android.view.View
11
11
  import android.view.ViewGroup
12
12
  import android.widget.FrameLayout
13
- import androidx.appcompat.app.AppCompatActivity
14
13
  import androidx.appcompat.content.res.AppCompatResources
15
14
  import androidx.core.graphics.drawable.DrawableCompat
16
15
  import androidx.fragment.app.Fragment
@@ -18,6 +17,7 @@ import com.facebook.react.bridge.Promise
18
17
  import com.facebook.react.bridge.ReactApplicationContext
19
18
  import com.facebook.react.bridge.WritableMap
20
19
  import com.facebook.react.bridge.WritableNativeMap
20
+ import com.reactnativestripesdk.addresssheet.AddressSheetView
21
21
  import com.reactnativestripesdk.utils.*
22
22
  import com.reactnativestripesdk.utils.createError
23
23
  import com.reactnativestripesdk.utils.createResult
@@ -65,12 +65,16 @@ class PaymentSheetFragment(
65
65
  paymentIntentClientSecret = arguments?.getString("paymentIntentClientSecret").orEmpty()
66
66
  setupIntentClientSecret = arguments?.getString("setupIntentClientSecret").orEmpty()
67
67
  val appearance = try {
68
- buildPaymentSheetAppearance(arguments?.getBundle("appearance"))
68
+ buildPaymentSheetAppearance(arguments?.getBundle("appearance"), context)
69
69
  } catch (error: PaymentSheetAppearanceException) {
70
70
  initPromise.resolve(createError(ErrorType.Failed.toString(), error))
71
71
  return
72
72
  }
73
73
 
74
+ val shippingDetails = arguments?.getBundle("defaultShippingDetails")?.let {
75
+ AddressSheetView.buildAddressDetails(it)
76
+ }
77
+
74
78
  val paymentOptionCallback = PaymentOptionCallback { paymentOption ->
75
79
  val result = paymentOption?.let {
76
80
  val bitmap = getBitmapFromVectorDrawable(context, it.drawableResourceId)
@@ -128,6 +132,7 @@ class PaymentSheetFragment(
128
132
  ) else null,
129
133
  googlePay = googlePayConfig,
130
134
  appearance = appearance,
135
+ shippingDetails = shippingDetails,
131
136
  primaryButtonLabel = primaryButtonLabel
132
137
  )
133
138
 
@@ -198,7 +203,7 @@ class PaymentSheetFragment(
198
203
  }
199
204
 
200
205
  companion object {
201
- const val TAG = "payment_sheet_launch_fragment"
206
+ internal const val TAG = "payment_sheet_launch_fragment"
202
207
 
203
208
  internal fun buildGooglePayConfig(params: Bundle?): PaymentSheet.GooglePayConfiguration? {
204
209
  if (params == null) {
@@ -5,13 +5,11 @@ import android.content.Intent
5
5
  import android.os.Parcelable
6
6
  import android.util.Log
7
7
  import androidx.appcompat.app.AppCompatActivity
8
- import androidx.fragment.app.Fragment
9
8
  import com.facebook.react.bridge.*
10
9
  import com.facebook.react.module.annotations.ReactModule
10
+ import com.reactnativestripesdk.addresssheet.AddressLauncherFragment
11
11
  import com.reactnativestripesdk.pushprovisioning.PushProvisioningProxy
12
12
  import com.reactnativestripesdk.utils.*
13
- import com.reactnativestripesdk.utils.createError
14
- import com.reactnativestripesdk.utils.createMissingActivityError
15
13
  import com.stripe.android.*
16
14
  import com.stripe.android.core.ApiVersion
17
15
  import com.stripe.android.core.AppInfo
@@ -45,14 +43,17 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
45
43
  private var googlePayFragment: GooglePayFragment? = null
46
44
  private var paymentLauncherFragment: PaymentLauncherFragment? = null
47
45
  private var collectBankAccountLauncherFragment: CollectBankAccountLauncherFragment? = null
48
- private var financialConnectionsSheetFragment: FinancialConnectionsSheetFragment? = null
49
- private val allFragments: List<Fragment?>
46
+
47
+ // If you create a new Fragment, you must put the tag here, otherwise result callbacks for that
48
+ // Fragment will not work on RN < 0.65
49
+ private val allStripeFragmentTags: List<String>
50
50
  get() = listOf(
51
- paymentSheetFragment,
52
- googlePayFragment,
53
- paymentLauncherFragment,
54
- collectBankAccountLauncherFragment,
55
- financialConnectionsSheetFragment
51
+ PaymentSheetFragment.TAG,
52
+ GooglePayFragment.TAG,
53
+ PaymentLauncherFragment.TAG,
54
+ CollectBankAccountLauncherFragment.TAG,
55
+ FinancialConnectionsSheetFragment.TAG,
56
+ AddressLauncherFragment.TAG
56
57
  )
57
58
 
58
59
  private val mActivityEventListener = object : BaseActivityEventListener() {
@@ -77,8 +78,12 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
77
78
 
78
79
  // Necessary on older versions of React Native (~0.65 and below)
79
80
  private fun dispatchActivityResultsToFragments(requestCode: Int, resultCode: Int, data: Intent?) {
80
- for (fragment in allFragments) {
81
- fragment?.activity?.activityResultRegistry?.dispatchResult(requestCode, resultCode, data)
81
+ getCurrentActivityOrResolveWithError(null)?.supportFragmentManager?.let { fragmentManager ->
82
+ for (tag in allStripeFragmentTags) {
83
+ fragmentManager.findFragmentByTag(tag)?.let {
84
+ it.activity?.activityResultRegistry?.dispatchResult(requestCode, resultCode, data)
85
+ }
86
+ }
82
87
  }
83
88
  }
84
89
 
@@ -120,6 +125,7 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
120
125
  }
121
126
 
122
127
  this.publishableKey = publishableKey
128
+ AddressLauncherFragment.publishableKey = publishableKey
123
129
 
124
130
  val name = getValOr(appInfo, "name", "") as String
125
131
  val partnerId = getValOr(appInfo, "partnerId", "")
@@ -718,7 +724,7 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
718
724
  promise.resolve(createMissingInitError())
719
725
  return
720
726
  }
721
- financialConnectionsSheetFragment = FinancialConnectionsSheetFragment().also {
727
+ FinancialConnectionsSheetFragment().also {
722
728
  it.presentFinancialConnectionsSheet(clientSecret, FinancialConnectionsSheetFragment.Mode.ForToken, publishableKey, stripeAccountId, promise, reactApplicationContext)
723
729
  }
724
730
  }
@@ -729,7 +735,7 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
729
735
  promise.resolve(createMissingInitError())
730
736
  return
731
737
  }
732
- financialConnectionsSheetFragment = FinancialConnectionsSheetFragment().also {
738
+ FinancialConnectionsSheetFragment().also {
733
739
  it.presentFinancialConnectionsSheet(clientSecret, FinancialConnectionsSheetFragment.Mode.ForSession, publishableKey, stripeAccountId, promise, reactApplicationContext)
734
740
  }
735
741
  }
@@ -4,6 +4,7 @@ import com.facebook.react.ReactPackage
4
4
  import com.facebook.react.bridge.NativeModule
5
5
  import com.facebook.react.bridge.ReactApplicationContext
6
6
  import com.facebook.react.uimanager.ViewManager
7
+ import com.reactnativestripesdk.addresssheet.AddressSheetViewManager
7
8
  import com.reactnativestripesdk.pushprovisioning.AddToWalletButtonManager
8
9
 
9
10
  class StripeSdkPackage : ReactPackage {
@@ -18,7 +19,8 @@ class StripeSdkPackage : ReactPackage {
18
19
  StripeContainerManager(),
19
20
  CardFormViewManager(),
20
21
  GooglePayButtonManager(),
21
- AddToWalletButtonManager(reactContext)
22
+ AddToWalletButtonManager(reactContext),
23
+ AddressSheetViewManager()
22
24
  )
23
25
  }
24
26
  }
@@ -0,0 +1,111 @@
1
+ package com.reactnativestripesdk.addresssheet
2
+
3
+ import android.os.Bundle
4
+ import android.view.LayoutInflater
5
+ import android.view.View
6
+ import android.view.ViewGroup
7
+ import android.widget.FrameLayout
8
+ import androidx.appcompat.app.AppCompatActivity
9
+ import androidx.fragment.app.Fragment
10
+ import com.facebook.react.bridge.ReactContext
11
+ import com.facebook.react.bridge.WritableMap
12
+ import com.reactnativestripesdk.utils.ErrorType
13
+ import com.reactnativestripesdk.utils.createError
14
+ import com.stripe.android.paymentsheet.PaymentSheet
15
+ import com.stripe.android.paymentsheet.addresselement.AddressDetails
16
+ import com.stripe.android.paymentsheet.addresselement.AddressLauncher
17
+ import com.stripe.android.paymentsheet.addresselement.AddressLauncherResult
18
+
19
+ class AddressLauncherFragment : Fragment() {
20
+ companion object {
21
+ internal var publishableKey: String? = null
22
+ internal const val TAG = "address_launcher_fragment"
23
+ }
24
+
25
+ private lateinit var addressLauncher: AddressLauncher
26
+ private var configuration = AddressLauncher.Configuration()
27
+ private var callback: ((error: WritableMap?, address: AddressDetails?) -> Unit)? = null
28
+
29
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
30
+ savedInstanceState: Bundle?): View {
31
+ return FrameLayout(requireActivity()).also {
32
+ it.visibility = View.GONE
33
+ }
34
+ }
35
+
36
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
37
+ publishableKey?.let { publishableKey ->
38
+ addressLauncher = AddressLauncher(this,
39
+ ::onAddressLauncherResult).also {
40
+ it.present(
41
+ publishableKey = publishableKey,
42
+ configuration = configuration
43
+ )
44
+ }
45
+ } ?: run {
46
+ callback?.invoke(
47
+ createError(ErrorType.Failed.toString(), "No publishable key set. Stripe has not been initialized. Initialize Stripe in your app with the StripeProvider component or the initStripe method."),
48
+ null
49
+ )
50
+ }
51
+ }
52
+
53
+ private fun onAddressLauncherResult(result: AddressLauncherResult) {
54
+ when (result) {
55
+ is AddressLauncherResult.Canceled -> {
56
+ callback?.invoke(
57
+ createError(ErrorType.Canceled.toString(), "The flow has been canceled."),
58
+ null
59
+ )
60
+ }
61
+ is AddressLauncherResult.Succeeded -> {
62
+ callback?.invoke(
63
+ null,
64
+ result.address
65
+ )
66
+ }
67
+ }
68
+ }
69
+
70
+ fun presentAddressSheet(
71
+ context: ReactContext,
72
+ appearance: PaymentSheet.Appearance,
73
+ defaultAddress: AddressDetails?,
74
+ allowedCountries: Set<String>,
75
+ buttonTitle: String?,
76
+ title: String?,
77
+ googlePlacesApiKey: String?,
78
+ autocompleteCountries: Set<String>,
79
+ additionalFields: AddressLauncher.AdditionalFieldsConfiguration?,
80
+ callback: ((error: WritableMap?, address: AddressDetails?) -> Unit)) {
81
+ configuration = AddressLauncher.Configuration(
82
+ appearance = appearance,
83
+ address = defaultAddress,
84
+ allowedCountries = allowedCountries,
85
+ buttonTitle = buttonTitle,
86
+ additionalFields = additionalFields,
87
+ title = title,
88
+ googlePlacesApiKey = googlePlacesApiKey,
89
+ autocompleteCountries = autocompleteCountries,
90
+ )
91
+ this.callback = callback
92
+ (context.currentActivity as? AppCompatActivity)?.let {
93
+ attemptToCleanupPreviousFragment(it)
94
+ commitFragmentAndStartFlow(it)
95
+ }
96
+ }
97
+
98
+ private fun attemptToCleanupPreviousFragment(currentActivity: AppCompatActivity) {
99
+ currentActivity.supportFragmentManager.beginTransaction()
100
+ .remove(this)
101
+ .commitAllowingStateLoss()
102
+ }
103
+
104
+ private fun commitFragmentAndStartFlow(currentActivity: AppCompatActivity) {
105
+ try {
106
+ currentActivity.supportFragmentManager.beginTransaction()
107
+ .add(this, TAG)
108
+ .commit()
109
+ } catch (_: IllegalStateException) {}
110
+ }
111
+ }
@@ -0,0 +1,28 @@
1
+ package com.reactnativestripesdk.addresssheet
2
+
3
+ import com.facebook.react.bridge.WritableMap
4
+ import com.facebook.react.uimanager.events.Event
5
+ import com.facebook.react.uimanager.events.RCTEventEmitter
6
+
7
+ internal class AddressSheetEvent constructor(viewTag: Int, private val eventType: EventType, private val eventMap: WritableMap?) : Event<AddressSheetEvent>(viewTag) {
8
+ enum class EventType {
9
+ OnSubmit,
10
+ OnError
11
+ }
12
+
13
+ override fun dispatch(rctEventEmitter: RCTEventEmitter) {
14
+ rctEventEmitter.receiveEvent(viewTag, eventName, eventMap)
15
+ }
16
+
17
+ companion object {
18
+ const val ON_SUBMIT = "onSubmitAction"
19
+ const val ON_ERROR = "onErrorAction"
20
+ }
21
+
22
+ override fun getEventName(): String {
23
+ return when (eventType) {
24
+ EventType.OnSubmit -> ON_SUBMIT
25
+ EventType.OnError -> ON_ERROR
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,178 @@
1
+ package com.reactnativestripesdk.addresssheet
2
+
3
+ import android.os.Bundle
4
+ import android.util.Log
5
+ import android.widget.FrameLayout
6
+ import com.facebook.react.bridge.Arguments
7
+ import com.facebook.react.bridge.ReadableMap
8
+ import com.facebook.react.bridge.WritableMap
9
+ import com.facebook.react.bridge.WritableNativeMap
10
+ import com.facebook.react.uimanager.ThemedReactContext
11
+ import com.facebook.react.uimanager.UIManagerModule
12
+ import com.facebook.react.uimanager.events.EventDispatcher
13
+ import com.reactnativestripesdk.buildPaymentSheetAppearance
14
+ import com.reactnativestripesdk.utils.ErrorType
15
+ import com.reactnativestripesdk.utils.PaymentSheetAppearanceException
16
+ import com.reactnativestripesdk.utils.createError
17
+ import com.reactnativestripesdk.utils.toBundleObject
18
+ import com.stripe.android.paymentsheet.PaymentSheet
19
+ import com.stripe.android.paymentsheet.addresselement.AddressDetails
20
+ import com.stripe.android.paymentsheet.addresselement.AddressLauncher
21
+ import com.stripe.android.paymentsheet.addresselement.AddressLauncherResult
22
+
23
+ class AddressSheetView(private val context: ThemedReactContext) : FrameLayout(context) {
24
+ private var eventDispatcher: EventDispatcher? = context.getNativeModule(UIManagerModule::class.java)?.eventDispatcher
25
+ private var isVisible = false
26
+ private var appearanceParams: ReadableMap? = null
27
+ private var defaultAddress: AddressDetails? = null
28
+ private var allowedCountries: Set<String> = emptySet()
29
+ private var buttonTitle: String? = null
30
+ private var sheetTitle: String? = null
31
+ private var googlePlacesApiKey: String? = null
32
+ private var autocompleteCountries: Set<String> = emptySet()
33
+ private var additionalFields: AddressLauncher.AdditionalFieldsConfiguration? = null
34
+
35
+ private fun onSubmit(params: WritableMap) {
36
+ eventDispatcher?.dispatchEvent(
37
+ AddressSheetEvent(id, AddressSheetEvent.EventType.OnSubmit, params)
38
+ )
39
+ }
40
+
41
+ private fun onError(params: WritableMap?) {
42
+ eventDispatcher?.dispatchEvent(
43
+ AddressSheetEvent(id, AddressSheetEvent.EventType.OnError, params)
44
+ )
45
+ }
46
+
47
+ fun setVisible(newVisibility: Boolean) {
48
+ if (newVisibility && !isVisible) {
49
+ launchAddressSheet()
50
+ } else if (!newVisibility && isVisible) {
51
+ Log.w("StripeReactNative", "Programmatically dismissing the Address Sheet is not supported on Android.")
52
+ }
53
+ isVisible = newVisibility
54
+ }
55
+
56
+ private fun launchAddressSheet() {
57
+ val appearance = try {
58
+ buildPaymentSheetAppearance(toBundleObject(appearanceParams), context)
59
+ } catch (error: PaymentSheetAppearanceException) {
60
+ onError(createError(ErrorType.Failed.toString(), error))
61
+ return
62
+ }
63
+ AddressLauncherFragment().presentAddressSheet(
64
+ context,
65
+ appearance,
66
+ defaultAddress,
67
+ allowedCountries,
68
+ buttonTitle,
69
+ sheetTitle,
70
+ googlePlacesApiKey,
71
+ autocompleteCountries,
72
+ additionalFields
73
+ ) { error, address ->
74
+ if (address != null) {
75
+ onSubmit(buildResult(address))
76
+ } else {
77
+ onError(error)
78
+ }
79
+ isVisible = false
80
+ }
81
+ }
82
+
83
+ fun setAppearance(appearanceParams: ReadableMap) {
84
+ this.appearanceParams = appearanceParams
85
+ }
86
+
87
+ fun setDefaultValues(defaults: ReadableMap) {
88
+ defaultAddress = buildAddressDetails(defaults)
89
+ }
90
+
91
+ fun setAdditionalFields(fields: ReadableMap) {
92
+ additionalFields = buildAdditionalFieldsConfiguration(fields)
93
+ }
94
+
95
+ fun setAllowedCountries(countries: List<String>) {
96
+ allowedCountries = countries.toSet()
97
+ }
98
+
99
+ fun setAutocompleteCountries(countries: List<String>) {
100
+ autocompleteCountries = countries.toSet()
101
+ }
102
+
103
+ fun setPrimaryButtonTitle(title: String) {
104
+ buttonTitle = title
105
+ }
106
+
107
+ fun setSheetTitle(title: String) {
108
+ sheetTitle = title
109
+ }
110
+
111
+ fun setGooglePlacesApiKey(key: String) {
112
+ googlePlacesApiKey = key
113
+ }
114
+
115
+ companion object {
116
+ internal fun buildAddressDetails(bundle: Bundle): AddressDetails {
117
+ return AddressDetails(
118
+ name = bundle.getString("name"),
119
+ address = buildAddress(bundle.getBundle("address")),
120
+ phoneNumber = bundle.getString("phone"),
121
+ isCheckboxSelected = bundle.getBoolean("isCheckboxSelected"),
122
+ )
123
+ }
124
+
125
+ internal fun buildAddressDetails(map: ReadableMap): AddressDetails {
126
+ return buildAddressDetails(toBundleObject(map))
127
+ }
128
+
129
+ internal fun buildAddress(bundle: Bundle?): PaymentSheet.Address? {
130
+ if (bundle == null) {
131
+ return null
132
+ }
133
+ return PaymentSheet.Address(
134
+ city = bundle.getString("city"),
135
+ country = bundle.getString("country"),
136
+ line1 = bundle.getString("line1"),
137
+ line2 = bundle.getString("line2"),
138
+ state = bundle.getString("state"),
139
+ postalCode = bundle.getString("postalCode")
140
+ )
141
+ }
142
+
143
+ internal fun getFieldConfiguration(key: String?): AddressLauncher.AdditionalFieldsConfiguration.FieldConfiguration {
144
+ return when (key) {
145
+ "hidden" -> AddressLauncher.AdditionalFieldsConfiguration.FieldConfiguration.HIDDEN
146
+ "optional" -> AddressLauncher.AdditionalFieldsConfiguration.FieldConfiguration.OPTIONAL
147
+ "required" -> AddressLauncher.AdditionalFieldsConfiguration.FieldConfiguration.REQUIRED
148
+ else -> AddressLauncher.AdditionalFieldsConfiguration.FieldConfiguration.HIDDEN
149
+ }
150
+ }
151
+
152
+ internal fun buildAdditionalFieldsConfiguration(params: ReadableMap): AddressLauncher.AdditionalFieldsConfiguration {
153
+ val phoneConfiguration = getFieldConfiguration(params.getString("phoneNumber"))
154
+
155
+ return AddressLauncher.AdditionalFieldsConfiguration(
156
+ phone = phoneConfiguration,
157
+ checkboxLabel = params.getString("checkboxLabel")
158
+ )
159
+ }
160
+
161
+ internal fun buildResult(addressDetails: AddressDetails): WritableMap {
162
+ val result = WritableNativeMap()
163
+ result.putString("name", addressDetails.name)
164
+ WritableNativeMap().let {
165
+ it.putString("city", addressDetails.address?.city)
166
+ it.putString("country", addressDetails.address?.country)
167
+ it.putString("line1", addressDetails.address?.line1)
168
+ it.putString("line2", addressDetails.address?.line2)
169
+ it.putString("postalCode", addressDetails.address?.postalCode)
170
+ it.putString("state", addressDetails.address?.state)
171
+ result.putMap("address", it)
172
+ }
173
+ result.putString("phone", addressDetails.phoneNumber)
174
+ result.putBoolean("isCheckboxSelected", addressDetails.isCheckboxSelected ?: false)
175
+ return result
176
+ }
177
+ }
178
+ }