Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stripe] Upgrade stripe to 0.23.1 #20964

Merged
merged 7 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Package-specific changes not released in any SDK will be added here just before

### 📚 3rd party library updates

- Updated `@stripe/stripe-react-native` from `0.19.0` to `0.23.1`. ([#20964](https://github.com/expo/expo/pull/20964) by [@aleqsio](https://github.com/aleqsio))
- Updated `react-native-webview` from `11.23.1` to `11.26.0`. ([#20933](https://github.com/expo/expo/pull/20933) by [@aleqsio](https://github.com/aleqsio))
- Updated `react-native-gesture-handler` from `2.8.0` to `2.9.0`. ([#20930](https://github.com/expo/expo/pull/20930) by [@tsapeta](https://github.com/tsapeta))
- Updated `react-native-shared-element` from `0.8.4` to `0.8.7`. ([#20593](https://github.com/expo/expo/pull/20593) by [@ijzerenhein](https://github.com/ijzerenhein))
Expand Down
4 changes: 2 additions & 2 deletions android/expoview/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,8 @@ dependencies {
api 'com.github.troZee:ViewPager2:v1.0.6'

// stripe-react-native
implementation('com.stripe:stripe-android:20.12.+')
implementation('com.stripe:financial-connections:20.12.+')
implementation('com.stripe:stripe-android:20.19.+')
implementation('com.stripe:financial-connections:20.19.+')
compileOnly 'com.stripe:stripe-android-issuing-push-provisioning:1.1.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package versioned.host.exp.exponent.modules.api.components.reactnativestripesdk

import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.Typeface
import android.os.Build
import android.text.Editable
import android.text.InputFilter
Expand All @@ -11,9 +10,11 @@ import android.util.Log
import android.widget.FrameLayout
import androidx.core.os.LocaleListCompat
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerModule
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.views.text.ReactTypefaceUtils
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
Expand Down Expand Up @@ -124,7 +125,8 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
}
fontFamily?.let {
for (editTextBinding in bindings) {
editTextBinding.typeface = Typeface.create(it, Typeface.NORMAL)
// Load custom font from assets, and fallback to default system font
editTextBinding.typeface = ReactTypefaceUtils.applyStyles(null, -1, -1, it.takeIf { it.isNotEmpty() }, context.assets)
}
}
cursorColor?.let {
Expand All @@ -140,18 +142,18 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
}
}

mCardWidget.setPadding(40, 0, 40, 0)
mCardWidget.setPadding(20, 0, 20, 0)
mCardWidget.background = MaterialShapeDrawable(
ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED, (borderRadius * 2).toFloat())
.setAllCorners(CornerFamily.ROUNDED, PixelUtil.toPixelFromDIP(borderRadius.toDouble()))
.build()
).also { shape ->
shape.strokeWidth = 0.0f
shape.strokeColor = ColorStateList.valueOf(Color.parseColor("#000000"))
shape.fillColor = ColorStateList.valueOf(Color.parseColor("#FFFFFF"))
borderWidth?.let {
shape.strokeWidth = (it * 2).toFloat()
shape.strokeWidth = PixelUtil.toPixelFromDIP(it.toDouble())
}
borderColor?.let {
shape.strokeColor = ColorStateList.valueOf(Color.parseColor(it))
Expand Down Expand Up @@ -201,6 +203,10 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {

fun setPostalCodeEnabled(isEnabled: Boolean) {
mCardWidget.postalCodeEnabled = isEnabled

if (isEnabled === false) {
mCardWidget.postalCodeRequired = false
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ package versioned.host.exp.exponent.modules.api.components.reactnativestripesdk

import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.Typeface
import android.os.Build
import android.text.InputFilter
import android.view.View
import android.view.View.OnFocusChangeListener
import android.widget.FrameLayout
import androidx.core.view.setMargins
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerModule
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.views.text.ReactTypefaceUtils
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
Expand Down Expand Up @@ -39,7 +41,7 @@ class CardFormView(context: ThemedReactContext) : FrameLayout(context) {
init {
cardFormViewBinding.cardMultilineWidgetContainer.isFocusable = true
cardFormViewBinding.cardMultilineWidgetContainer.isFocusableInTouchMode = true

(cardFormViewBinding.cardMultilineWidgetContainer.layoutParams as MarginLayoutParams).setMargins(0)
addView(cardForm)
setListeners()

Expand Down Expand Up @@ -135,6 +137,12 @@ class CardFormView(context: ThemedReactContext) : FrameLayout(context) {
cardFormViewBinding.cardMultilineWidget.expiryDateEditText,
cardFormViewBinding.postalCode
)
val placeholderTextBindings = setOf(
multilineWidgetBinding.tlExpiry,
multilineWidgetBinding.tlCardNumber,
multilineWidgetBinding.tlCvc,
cardFormViewBinding.postalCodeContainer,
)

textColor?.let {
for (binding in editTextBindings) {
Expand All @@ -149,20 +157,27 @@ class CardFormView(context: ThemedReactContext) : FrameLayout(context) {
}
}
placeholderColor?.let {
multilineWidgetBinding.tlExpiry.defaultHintTextColor = ColorStateList.valueOf(Color.parseColor(it))
multilineWidgetBinding.tlCardNumber.defaultHintTextColor = ColorStateList.valueOf(Color.parseColor(it))
multilineWidgetBinding.tlCvc.defaultHintTextColor = ColorStateList.valueOf(Color.parseColor(it))
cardFormViewBinding.postalCodeContainer.defaultHintTextColor = ColorStateList.valueOf(Color.parseColor(it))
for (binding in placeholderTextBindings) {
binding.defaultHintTextColor = ColorStateList.valueOf(Color.parseColor(it))
}
}
fontSize?.let {
for (binding in editTextBindings) {
binding.textSize = it.toFloat()
}
}
fontFamily?.let {
// Load custom font from assets, and fallback to default system font
val typeface = ReactTypefaceUtils.applyStyles(null, -1, -1, it.takeIf { it.isNotEmpty() }, context.assets)
for (binding in editTextBindings) {
binding.typeface = Typeface.create(it, Typeface.NORMAL)
binding.typeface = typeface
}
for (binding in placeholderTextBindings) {
binding.typeface = typeface
}
cardFormViewBinding.countryLayout.typeface = typeface
cardFormViewBinding.countryLayout.countryAutocomplete.typeface = typeface
cardFormViewBinding.errors.typeface = typeface
}
cursorColor?.let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Expand All @@ -177,18 +192,17 @@ class CardFormView(context: ThemedReactContext) : FrameLayout(context) {
}
}

cardFormViewBinding.cardMultilineWidgetContainer.setPadding(40, 0, 40, 0)
cardFormViewBinding.cardMultilineWidgetContainer.background = MaterialShapeDrawable(
ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED, (borderRadius * 2).toFloat())
.setAllCorners(CornerFamily.ROUNDED, PixelUtil.toPixelFromDIP(borderRadius.toDouble()))
.build()
).also { shape ->
shape.strokeWidth = 0.0f
shape.strokeColor = ColorStateList.valueOf(Color.parseColor("#000000"))
shape.fillColor = ColorStateList.valueOf(Color.parseColor("#FFFFFF"))
borderWidth?.let {
shape.strokeWidth = (it * 2).toFloat()
shape.strokeWidth = PixelUtil.toPixelFromDIP(it.toDouble())
}
borderColor?.let {
shape.strokeColor = ColorStateList.valueOf(Color.parseColor(it))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,6 @@ class CollectBankAccountLauncherFragment(
}

companion object {
const val TAG = "collect_bank_account_launcher_fragment"
internal const val TAG = "collect_bank_account_launcher_fragment"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,16 @@ class FinancialConnectionsSheetFragment : Fragment() {
private fun commitFragmentAndStartFlow(currentActivity: AppCompatActivity) {
try {
currentActivity.supportFragmentManager.beginTransaction()
.add(this, "financial_connections_sheet_launch_fragment")
.add(this, TAG)
.commit()
} catch (error: IllegalStateException) {
promise.resolve(createError(ErrorType.Failed.toString(), error.message))
}
}

companion object {
internal const val TAG = "financial_connections_sheet_launch_fragment"

private fun createTokenResult(result: FinancialConnectionsSheetForTokenResult.Completed): WritableMap {
return WritableNativeMap().also {
it.putMap("session", mapFromSession(result.financialConnectionsSession))
Expand Down Expand Up @@ -237,7 +239,9 @@ class FinancialConnectionsSheetFragment : Fragment() {
FinancialConnectionsAccount.Permissions.BALANCES -> "balances"
FinancialConnectionsAccount.Permissions.OWNERSHIP -> "ownership"
FinancialConnectionsAccount.Permissions.TRANSACTIONS -> "transactions"
FinancialConnectionsAccount.Permissions.ACCOUNT_NUMBERS -> "accountNumbers"
FinancialConnectionsAccount.Permissions.UNKNOWN -> "unparsable"
FinancialConnectionsAccount.Permissions.ACCOUNT_NUMBERS -> "accountNumbers"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,6 @@ class GooglePayFragment(private val initPromise: Promise) : Fragment() {
}

companion object {
const val TAG = "google_pay_launch_fragment"
internal const val TAG = "google_pay_launch_fragment"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package versioned.host.exp.exponent.modules.api.components.reactnativestripesdk

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import com.facebook.react.bridge.*
import versioned.host.exp.exponent.modules.api.components.reactnativestripesdk.utils.*
import versioned.host.exp.exponent.modules.api.components.reactnativestripesdk.utils.createError
import versioned.host.exp.exponent.modules.api.components.reactnativestripesdk.utils.createMissingActivityError
import com.stripe.android.googlepaylauncher.GooglePayEnvironment
import com.stripe.android.googlepaylauncher.GooglePayLauncher

class GooglePayLauncherFragment : Fragment() {
enum class Mode {
ForSetup, ForPayment
}

private lateinit var launcher: GooglePayLauncher
private lateinit var clientSecret: String
private lateinit var mode: Mode
private lateinit var configuration: GooglePayLauncher.Config
private lateinit var currencyCode: String
private lateinit var callback: (result: GooglePayLauncher.Result?, error: WritableMap?) -> Unit

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
return FrameLayout(requireActivity()).also {
it.visibility = View.GONE
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
launcher = GooglePayLauncher(
fragment = this,
config = configuration,
readyCallback = ::onGooglePayReady,
resultCallback = ::onGooglePayResult
)
}

fun presentGooglePaySheet(clientSecret: String, mode: Mode, googlePayParams: ReadableMap, context: ReactApplicationContext, callback: (GooglePayLauncher.Result?, error: WritableMap?) -> Unit) {
this.clientSecret = clientSecret
this.mode = mode
this.callback = callback
this.currencyCode = googlePayParams.getString("currencyCode") ?: "USD"
this.configuration = GooglePayLauncher.Config(
environment = if (googlePayParams.getBoolean("testEnv")) GooglePayEnvironment.Test else GooglePayEnvironment.Production,
merchantCountryCode = googlePayParams.getString("merchantCountryCode").orEmpty(),
merchantName = googlePayParams.getString("merchantName").orEmpty(),
isEmailRequired = googlePayParams.getBooleanOr("isEmailRequired", false),
billingAddressConfig = buildBillingAddressParameters(googlePayParams.getMap("billingAddressConfig")),
existingPaymentMethodRequired = googlePayParams.getBooleanOr("existingPaymentMethodRequired", false),
allowCreditCards = googlePayParams.getBooleanOr("allowCreditCards", true),
)

(context.currentActivity as? AppCompatActivity)?.let {
attemptToCleanupPreviousFragment(it)
commitFragmentAndStartFlow(it)
} ?: run {
callback(null, createMissingActivityError())
return
}
}

private fun attemptToCleanupPreviousFragment(currentActivity: AppCompatActivity) {
currentActivity.supportFragmentManager.beginTransaction()
.remove(this)
.commitAllowingStateLoss()
}

private fun commitFragmentAndStartFlow(currentActivity: AppCompatActivity) {
try {
currentActivity.supportFragmentManager.beginTransaction()
.add(this, TAG)
.commit()
} catch (error: IllegalStateException) {
callback(
null,
createError(ErrorType.Failed.toString(), error.message)
)
}
}

private fun onGooglePayReady(isReady: Boolean) {
if (isReady) {
when (mode) {
Mode.ForSetup -> {
launcher.presentForSetupIntent(clientSecret, currencyCode)
}
Mode.ForPayment -> {
launcher.presentForPaymentIntent(clientSecret)
}
}
} else {
callback(
null,
createError(
GooglePayErrorType.Failed.toString(),
"Google Pay is not available on this device. You can use isPlatformPaySupported to preemptively check for Google Pay support."
)
)
}
}

private fun onGooglePayResult(result: GooglePayLauncher.Result) {
callback(result, null)
}

companion object {
const val TAG = "google_pay_launcher_fragment"

private fun buildBillingAddressParameters(params: ReadableMap?): GooglePayLauncher.BillingAddressConfig {
val isRequired = params?.getBooleanOr("isRequired", false)
val isPhoneNumberRequired = params?.getBooleanOr("isPhoneNumberRequired", false)
val format = when (params?.getString("format").orEmpty()) {
"FULL" -> GooglePayLauncher.BillingAddressConfig.Format.Full
"MIN" -> GooglePayLauncher.BillingAddressConfig.Format.Min
else -> GooglePayLauncher.BillingAddressConfig.Format.Min
}

return GooglePayLauncher.BillingAddressConfig(
isRequired = isRequired ?: false,
format = format,
isPhoneNumberRequired = isPhoneNumberRequired ?: false
)
}
}
}