Skip to content

Commit

Permalink
[stripe] Upgrade stripe to 0.23.1 (#20964)
Browse files Browse the repository at this point in the history
  • Loading branch information
aleqsio committed Feb 2, 2023
1 parent d27f77b commit 86dbd8e
Show file tree
Hide file tree
Showing 52 changed files with 2,394 additions and 442 deletions.
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
)
}
}
}

0 comments on commit 86dbd8e

Please sign in to comment.