Skip to content

[WOOMOB-302][Mobile Payments] Update Stripe's SDK to 4.3.1 #13970

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

Open
wants to merge 17 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class MockCardReaderManagerModule {
override val id: String
get() = "ADEE123"
override val type: String
get() = "COTS_DEVICE"
get() = "TAP_TO_PAY_DEVICE"
override val currentBatteryLevel: Float
get() = 1f
override val firmwareVersion: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ open class WooCommerce : Application(), HasAndroidInjector, Configuration.Provid

// Stripe Tap to Pay library starts it's own process. That causes the crash:
// > Caused by: java.lang.IllegalStateException: Default FirebaseApp is not initialized in this process
// > com.woocommerce.android:stripelocalmobile Make sure to call FirebaseApp.initializeApp(Context) first.
// > com.woocommerce.android:stripetaptopay Make sure to call FirebaseApp.initializeApp(Context) first.
// In this case we don't want to initialize any Firebase (or any at all) features of the app in their process.
if (getCurrentProcessName() == "${BuildConfig.APPLICATION_ID}:$TAP_TO_PAY_STRIPE_PROCESS_NAME_SUFFIX") return

Expand All @@ -55,6 +55,6 @@ open class WooCommerce : Application(), HasAndroidInjector, Configuration.Provid
override fun androidInjector(): AndroidInjector<Any> = androidInjector

companion object {
private const val TAP_TO_PAY_STRIPE_PROCESS_NAME_SUFFIX = "stripelocalmobile"
private const val TAP_TO_PAY_STRIPE_PROCESS_NAME_SUFFIX = "stripetaptopay"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import com.woocommerce.android.cardreader.connection.CardReaderDiscoveryEvents.S
import com.woocommerce.android.cardreader.connection.CardReaderStatus
import com.woocommerce.android.cardreader.connection.CardReaderTypesToDiscover.SpecificReaders.BuiltInReaders
import com.woocommerce.android.cardreader.connection.CardReaderTypesToDiscover.SpecificReaders.ExternalReaders
import com.woocommerce.android.cardreader.connection.ReaderType.BuildInReader.CotsDevice
import com.woocommerce.android.cardreader.connection.ReaderType.BuildInReader.TapToPayDevice
import com.woocommerce.android.cardreader.connection.ReaderType.ExternalReader.Chipper2X
import com.woocommerce.android.cardreader.connection.ReaderType.ExternalReader.StripeM2
import com.woocommerce.android.cardreader.connection.ReaderType.ExternalReader.WisePade3
Expand Down Expand Up @@ -578,7 +578,7 @@ class CardReaderConnectViewModel @Inject constructor(

private fun buildReadersToDiscover() =
when (arguments.cardReaderType) {
BUILT_IN -> BuiltInReaders(listOf(CotsDevice))
BUILT_IN -> BuiltInReaders(listOf(TapToPayDevice))
EXTERNAL -> ExternalReaders(
listOf(
Chipper2X,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.woocommerce.android.R.drawable
import com.woocommerce.android.R.string
import com.woocommerce.android.cardreader.config.CardReaderConfigForSupportedCountry
import com.woocommerce.android.cardreader.connection.ReaderType
import com.woocommerce.android.cardreader.connection.ReaderType.BuildInReader.CotsDevice
import com.woocommerce.android.cardreader.connection.ReaderType.BuildInReader.TapToPayDevice
import com.woocommerce.android.cardreader.connection.ReaderType.ExternalReader.Chipper2X
import com.woocommerce.android.cardreader.connection.ReaderType.ExternalReader.StripeM2
import com.woocommerce.android.cardreader.connection.ReaderType.ExternalReader.WisePade3
Expand Down Expand Up @@ -34,7 +34,7 @@ class CardReaderManualsSupportedReadersMapper @Inject constructor() {
label = string.card_reader_wisepad_3_manual_card_reader,
onManualClicked = clickListeners[it]!!
)
CotsDevice -> null // This is built-in reader, we don't need to show it in the list
TapToPayDevice -> null // This is built-in reader, we don't need to show it in the list
else -> error("$it doesn't have a manual")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1534,7 +1534,7 @@ class CardReaderConnectViewModelTest : BaseUnitTest() {
verify(cardReaderManager).discoverReaders(anyBoolean(), captor.capture())
assertThat(captor.firstValue).isEqualTo(
CardReaderTypesToDiscover.SpecificReaders.BuiltInReaders(
listOf(ReaderType.BuildInReader.CotsDevice)
listOf(ReaderType.BuildInReader.TapToPayDevice)
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class CardReaderDetailViewModelTest : BaseUnitTest() {
testBlocking {
// GIVEN
initConnectedState(
readerType = ReaderType.BuildInReader.CotsDevice
readerType = ReaderType.BuildInReader.TapToPayDevice
)

// WHEN
Expand All @@ -123,7 +123,7 @@ class CardReaderDetailViewModelTest : BaseUnitTest() {
testBlocking {
// GIVEN
initConnectedState(
readerType = ReaderType.BuildInReader.CotsDevice
readerType = ReaderType.BuildInReader.TapToPayDevice
)

// WHEN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2394,7 +2394,7 @@ class CardReaderPaymentControllerTest : BaseUnitTest() {
fun `given built in payment failed state and connected BI, when user presses back, then disconnect from reader invoked`() =
testBlocking {
val cardReader: CardReader = mock {
on { type }.thenReturn("COTS_DEVICE")
on { type }.thenReturn("TAP_TO_PAY_DEVICE")
}
whenever(cardReaderManager.readerStatus).thenReturn(
MutableStateFlow(CardReaderStatus.Connected(cardReader))
Expand Down Expand Up @@ -3492,7 +3492,7 @@ class CardReaderPaymentControllerTest : BaseUnitTest() {
testBlocking {
setupControllerForInteracRefund()
val cardReader: CardReader = mock {
on { type }.thenReturn("COTS_DEVICE")
on { type }.thenReturn("TAP_TO_PAY_DEVICE")
}
whenever(cardReaderManager.readerStatus).thenReturn(
MutableStateFlow(CardReaderStatus.Connected(cardReader))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ class CardReaderStatusCheckerViewModelTest : BaseUnitTest() {
val orderId = 1L
val param = CardReaderFlowParam.PaymentOrRefund.Payment(orderId = orderId, paymentType = ORDER)
val connectedReader: CardReader = mock {
on { type }.thenReturn(ReaderType.BuildInReader.CotsDevice.name)
on { type }.thenReturn(ReaderType.BuildInReader.TapToPayDevice.name)
}
whenever(appPrefsWrapper.isCardReaderWelcomeDialogShown()).thenReturn(true)
whenever(cardReaderManager.readerStatus).thenReturn(
Expand Down Expand Up @@ -300,13 +300,13 @@ class CardReaderStatusCheckerViewModelTest : BaseUnitTest() {
}

@Test
fun `given payment flow and connected COTS reader, when vm init, then navigates to payment with built in`() =
fun `given payment flow and connected Tap To Pay reader, when vm init, then navigates to payment with built in`() =
testBlocking {
// GIVEN
val orderId = 1L
val param = CardReaderFlowParam.PaymentOrRefund.Payment(orderId = orderId, paymentType = ORDER)
val connectedReader: CardReader = mock {
on { type }.thenReturn(ReaderType.BuildInReader.CotsDevice.name)
on { type }.thenReturn(ReaderType.BuildInReader.TapToPayDevice.name)
}
whenever(cardReaderManager.readerStatus).thenReturn(
MutableStateFlow(
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ sentry = '4.10.0'
squareup-javapoet = "1.7.0"
squareup-leakcanary = '2.14'
squareup-okhttp3 = "4.9.0"
stripe-terminal = '3.7.1'
stripe-terminal = '4.3.1'
terl-lazysodium-android = '5.0.2@aar'
tinder-statemachine = '0.2.0'
volley = "1.1.1"
Expand Down Expand Up @@ -255,7 +255,7 @@ squareup-javapoet = { module = "com.squareup:javapoet", version.ref = "squareup-
squareup-leakcanary-android = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "squareup-leakcanary" }
squareup-okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "squareup-okhttp3" }
squareup-okhttp3-urlconnection = { module = "com.squareup.okhttp3:okhttp-urlconnection", version.ref = "squareup-okhttp3" }
stripe-terminal-localmobile = { group = "com.stripe", name = "stripeterminal-localmobile", version.ref = "stripe-terminal" }
stripe-terminal-taptopay = { group = "com.stripe", name = "stripeterminal-taptopay", version.ref = "stripe-terminal" }
stripe-terminal-core = { group = "com.stripe", name = "stripeterminal-core", version.ref = "stripe-terminal" }
terl-lazysodium-android = { module = "com.goterl:lazysodium-android", version.ref = "terl-lazysodium-android" }
tinder-statemachine = { group = "com.tinder.statemachine", name = "statemachine", version.ref = "tinder-statemachine" }
Expand Down
2 changes: 1 addition & 1 deletion libs/cardreader/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ android {
}

dependencies {
implementation(libs.stripe.terminal.localmobile)
implementation(libs.stripe.terminal.taptopay)
implementation(libs.stripe.terminal.core)

// Coroutines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.woocommerce.android.cardreader.internal.CardReaderManagerImpl
import com.woocommerce.android.cardreader.internal.TokenProvider
import com.woocommerce.android.cardreader.internal.connection.BluetoothReaderListenerImpl
import com.woocommerce.android.cardreader.internal.connection.ConnectionManager
import com.woocommerce.android.cardreader.internal.connection.TapToPayReaderListenerImpl
import com.woocommerce.android.cardreader.internal.connection.TerminalListenerImpl
import com.woocommerce.android.cardreader.internal.connection.UpdateErrorMapper
import com.woocommerce.android.cardreader.internal.connection.actions.DiscoverReadersAction
Expand Down Expand Up @@ -39,6 +40,7 @@ object CardReaderManagerFactory {
AdditionalInfoMapper(),
UpdateErrorMapper(batteryLevelProvider)
)
val tapToPayReaderListener = TapToPayReaderListenerImpl(logWrapper)
val terminalListener = TerminalListenerImpl(logWrapper)
val cardReaderConfigFactory = CardReaderConfigFactory()
val paymentUtils = PaymentUtils(logWrapper)
Expand Down Expand Up @@ -74,6 +76,7 @@ object CardReaderManagerFactory {
ConnectionManager(
terminal,
bluetoothReaderListener,
tapToPayReaderListener,
DiscoverReadersAction(terminal, logWrapper),
terminalListener,
application,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object CardReaderConfigForCanada : CardReaderConfigForSupportedCountry(
countryCode = "CA",
supportedReaders = listOf(
ReaderType.ExternalReader.WisePade3,
ReaderType.BuildInReader.CotsDevice,
ReaderType.BuildInReader.TapToPayDevice,
),
paymentMethodTypes = listOf(
PaymentMethodType.CARD_PRESENT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object CardReaderConfigForGB : CardReaderConfigForSupportedCountry(
countryCode = "GB",
supportedReaders = listOf(
ReaderType.ExternalReader.WisePade3,
ReaderType.BuildInReader.CotsDevice,
ReaderType.BuildInReader.TapToPayDevice,
),
paymentMethodTypes = listOf(PaymentMethodType.CARD_PRESENT),
supportedExtensions = listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ object CardReaderConfigForUSA : CardReaderConfigForSupportedCountry(
supportedReaders = listOf(
ReaderType.ExternalReader.Chipper2X,
ReaderType.ExternalReader.StripeM2,
ReaderType.BuildInReader.CotsDevice,
ReaderType.BuildInReader.TapToPayDevice,
),
paymentMethodTypes = listOf(PaymentMethodType.CARD_PRESENT),
supportedExtensions = listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ sealed class ReaderType(val name: String) {
}

sealed class BuildInReader(buildInReaderName: String) : ReaderType(buildInReaderName) {
object CotsDevice : BuildInReader("COTS_DEVICE")
object TapToPayDevice : BuildInReader("TAP_TO_PAY_DEVICE")
}

object Unknown : ReaderType("UNKNOWN")
Expand All @@ -26,7 +26,7 @@ sealed class ReaderType(val name: String) {
"VERIFONE_P400" -> ExternalReader.VerifoneP400
"WISEPAD_3" -> ExternalReader.WisePade3
"WISEPOS_E" -> ExternalReader.WisePadeE
"COTS_DEVICE" -> BuildInReader.CotsDevice
"TAP_TO_PAY_DEVICE" -> BuildInReader.TapToPayDevice
else -> Unknown
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.woocommerce.android.cardreader.internal.connection

import com.stripe.stripeterminal.external.callable.Cancelable
import com.stripe.stripeterminal.external.callable.ReaderListener
import com.stripe.stripeterminal.external.callable.MobileReaderListener
import com.stripe.stripeterminal.external.models.BatteryStatus
import com.stripe.stripeterminal.external.models.DisconnectReason
import com.stripe.stripeterminal.external.models.ReaderDisplayMessage
import com.stripe.stripeterminal.external.models.ReaderEvent
import com.stripe.stripeterminal.external.models.ReaderInputOptions
Expand All @@ -27,7 +28,7 @@ internal class BluetoothReaderListenerImpl(
private val logWrapper: LogWrapper,
private val additionalInfoMapper: AdditionalInfoMapper,
private val updateErrorMapper: UpdateErrorMapper,
) : ReaderListener {
) : MobileReaderListener {
private val _updateStatusEvents = MutableStateFlow<SoftwareUpdateStatus>(SoftwareUpdateStatus.Unknown)
val updateStatusEvents = _updateStatusEvents.asStateFlow()

Expand Down Expand Up @@ -104,6 +105,10 @@ internal class BluetoothReaderListenerImpl(
_displayMessagesEvents.value = BluetoothCardReaderMessages.CardReaderInputMessage(options.toString())
}

override fun onDisconnect(reason: DisconnectReason) {
logWrapper.d(LOG_TAG, "onDisconnect")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we shouldn't just log it. We may need to handle it properly and share it with the user

}

fun resetConnectionState() {
_updateStatusEvents.value = SoftwareUpdateStatus.Unknown
_updateAvailabilityEvents.value = SoftwareUpdateAvailability.NotAvailable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import android.os.Build
import com.stripe.stripeterminal.external.callable.Callback
import com.stripe.stripeterminal.external.callable.ReaderCallback
import com.stripe.stripeterminal.external.models.ConnectionConfiguration.BluetoothConnectionConfiguration
import com.stripe.stripeterminal.external.models.ConnectionConfiguration.LocalMobileConnectionConfiguration
import com.stripe.stripeterminal.external.models.ConnectionConfiguration.TapToPayConnectionConfiguration
import com.stripe.stripeterminal.external.models.DeviceType
import com.stripe.stripeterminal.external.models.Reader
import com.stripe.stripeterminal.external.models.TerminalErrorCode
import com.stripe.stripeterminal.external.models.TerminalException
import com.woocommerce.android.cardreader.connection.CardReader
import com.woocommerce.android.cardreader.connection.CardReaderDiscoveryEvents
Expand Down Expand Up @@ -38,6 +39,7 @@ private const val ARTIFICIAL_STATUS_UPDATE_DELAY_IN_MILLIS = 500L
internal class ConnectionManager(
private val terminal: TerminalWrapper,
private val bluetoothReaderListener: BluetoothReaderListenerImpl,
private val tapToPayReaderListener: TapToPayReaderListenerImpl,
private val discoverReadersAction: DiscoverReadersAction,
private val terminalListenerImpl: TerminalListenerImpl,
private val application: Application,
Expand Down Expand Up @@ -140,7 +142,7 @@ internal class ConnectionManager(
}

when (it.cardReader.deviceType) {
DeviceType.COTS_DEVICE -> connectToBuiltInReader(cardReader, locationId, readerCallback)
DeviceType.TAP_TO_PAY_DEVICE -> connectToBuiltInReader(cardReader, locationId, readerCallback)
else -> connectToExternalReader(cardReader, locationId, readerCallback)
}
}
Expand Down Expand Up @@ -199,9 +201,8 @@ internal class ConnectionManager(
) {
terminal.connectToReader(
cardReader.cardReader,
BluetoothConnectionConfiguration(locationId),
readerCallback,
bluetoothReaderListener,
BluetoothConnectionConfiguration(locationId, true, bluetoothReaderListener),
readerCallback
)
}

Expand All @@ -212,14 +213,18 @@ internal class ConnectionManager(
) {
terminal.connectToMobile(
cardReader.cardReader,
LocalMobileConnectionConfiguration(locationId),
TapToPayConnectionConfiguration(
locationId,
autoReconnectOnUnexpectedDisconnect = true,
tapToPayReaderListener
),
readerCallback
)
}

private fun TerminalException.TerminalErrorCode.toErrorCode(): CardReaderStatus.NotConnected.ErrorCode =
private fun TerminalErrorCode.toErrorCode(): CardReaderStatus.NotConnected.ErrorCode =
when (this) {
TerminalException.TerminalErrorCode.READER_BATTERY_CRITICALLY_LOW ->
TerminalErrorCode.READER_BATTERY_CRITICALLY_LOW ->
CardReaderStatus.NotConnected.ErrorCode.BATTERY_CRITICALLY_LOW

else -> CardReaderStatus.NotConnected.ErrorCode.OTHER
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.woocommerce.android.cardreader.internal.connection

import com.stripe.stripeterminal.external.callable.Cancelable
import com.stripe.stripeterminal.external.callable.TapToPayReaderListener
import com.stripe.stripeterminal.external.models.DisconnectReason
import com.stripe.stripeterminal.external.models.Reader
import com.woocommerce.android.cardreader.LogWrapper
import com.woocommerce.android.cardreader.internal.LOG_TAG

class TapToPayReaderListenerImpl(
Copy link
Contributor

@kidinov kidinov Apr 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can just log status changes. Probably we need to propagate this to the user and offer a correct explanation with further steps

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kidinov Thanks for the comment! Before, we didn't even have this listener, so we didn't propagate the events to the user. As this PR issue is just to update the SDK without modifying behavior, what do you think if we leave it as it is on this PR and create an issue enhancement for propagating these events to the users and letting them to handle them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@toupper 👋

For Tap to Pay readers, the TapToPayConnectionConfiguration now takes in an TapToPayReaderListener parameter, replacing ReaderReconnectionListener.

Oh, I see. We didn't have ReaderReconnectionListener either, as automatic reconnection was already added later!

As this PR issue is just to update the SDK without modifying behavior, what do you think if we leave it as it is on this PR and create an issue enhancement for propagating these events to the users and letting them to handle them?

Yep, updating to a major version often brings changes in behavior, beyond just the API modifications, indicating a need for adaptation on our side too. However, I’m uncertain in this case since it's unclear how the app will behave during the reconnection process and the duration it may require. Previously, we would display an error message when it was "not connected" and that was the end of it. Now, it will attempt to reconnect, but we will ignore the process and not present any information to the user.

It feels like if we don't want to actually handle the reconnection, we may need to disable it to keep the same behavior like now, and have a separate issue to handle this as you proposed

We recommend displaying notifications in your app to inform the users about the reader status throughout the reconnection process. To handle reader reconnection methods, the ReaderReconnectionListener has been inherited by the respective ReaderListeners. Use MobileReaderListener for mobile readers, and TapToPayReaderListener for Tap to Pay readers to handle reconnection events.

If you implemented your own reader reconnection logic and want to maintain this behavior, you can turn off auto reconnection by setting autoReconnectOnUnexpectedDisconnect to false.

private val logWrapper: LogWrapper
) : TapToPayReaderListener {
override fun onDisconnect(reason: DisconnectReason) {
logWrapper.d(LOG_TAG, "onDisconnect")
}

override fun onReaderReconnectFailed(reader: Reader) {
logWrapper.d(LOG_TAG, "onReaderReconnectFailed")
}

override fun onReaderReconnectStarted(
reader: Reader,
cancelReconnect: Cancelable,
reason: DisconnectReason
) {
logWrapper.d(LOG_TAG, "onReaderReconnectStarted")
}

override fun onReaderReconnectSucceeded(reader: Reader) {
logWrapper.d(LOG_TAG, "onReaderReconnectSucceeded")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.woocommerce.android.cardreader.internal.connection
import com.stripe.stripeterminal.external.callable.TerminalListener
import com.stripe.stripeterminal.external.models.ConnectionStatus
import com.stripe.stripeterminal.external.models.PaymentStatus
import com.stripe.stripeterminal.external.models.Reader
import com.woocommerce.android.cardreader.LogWrapper
import com.woocommerce.android.cardreader.connection.CardReaderStatus
import com.woocommerce.android.cardreader.internal.LOG_TAG
Expand All @@ -20,13 +19,12 @@ internal class TerminalListenerImpl(
_readerStatus.value = newStatus
}

override fun onUnexpectedReaderDisconnect(reader: Reader) {
_readerStatus.value = CardReaderStatus.NotConnected()
logWrapper.d(LOG_TAG, "onUnexpectedReaderDisconnect")
}

override fun onConnectionStatusChange(status: ConnectionStatus) {
logWrapper.d(LOG_TAG, "onConnectionStatusChange: ${status.name}")

if (status == ConnectionStatus.NOT_CONNECTED) {
_readerStatus.value = CardReaderStatus.NotConnected()
}
}

override fun onPaymentStatusChange(status: PaymentStatus) {
Expand Down
Loading
Loading