Skip to content

Commit

Permalink
Merge pull request #12977 from woocommerce/custom-payment-ui-6-use-pa…
Browse files Browse the repository at this point in the history
…yment-controller-in-pos

[POS][Custom payment UI] – Switch to Payment Controller in POS Totals | Base PR
  • Loading branch information
samiuelson authored Dec 13, 2024
2 parents 95a74e7 + 1c90560 commit af499bf
Show file tree
Hide file tree
Showing 44 changed files with 6,157 additions and 928 deletions.
2 changes: 1 addition & 1 deletion RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
- [*] When entering a wrong WordPress.com account for login, retrying will bring the step back to the email input screen [https://github.com/woocommerce/woocommerce-android/pull/13024]
- [Internal] Replaces a function in WCSSRExt that then removes the need for commons-io dependency. [https://github.com/woocommerce/woocommerce-android/pull/13073]
- [Internal] Removes coupons feature announcement banner [https://github.com/woocommerce/woocommerce-android/pull/13077]

- [Internal] Updated androidx-compose-bom to 2024.09.00 [https://github.com/woocommerce/woocommerce-android/pull/13060]
- [**] Improved UX of card reader payment flow in Point of Sale, allowing faster payment collection [https://github.com/woocommerce/woocommerce-android/pull/12977]

-----
21.2
Expand Down
3 changes: 3 additions & 0 deletions WooCommerce/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,9 @@ dependencies {

coreLibraryDesugaring(libs.android.desugar)

// Lottie
implementation(libs.lottie.compose)

// CameraX
implementation(libs.androidx.camera.camera2)
implementation(libs.androidx.camera.lifecycle)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,10 @@ class CardReaderConnectDialogFragment : PaymentsBaseDialogFragment(R.layout.card
result = event.data as Boolean,
)
}
is CardReaderConnectEvent.PopBackStackForWooPOS -> {
is CardReaderConnectEvent.ReturnToWooPos -> {
parentFragmentManager.setFragmentResult(
WooPosCardReaderActivity.WOO_POS_CARD_CONNECTION_REQUEST_KEY,
Bundle(),
WooPosCardReaderActivity.WOO_POS_CARD_PAYMENT_REQUEST_KEY,
Bundle()
)
}
is CardReaderConnectEvent.ShowToast ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,5 @@ sealed class CardReaderConnectEvent : MultiLiveEvent.Event() {

data class OpenGenericWebView(val url: String) : CardReaderConnectEvent()

data object PopBackStackForWooPOS : CardReaderConnectEvent()
data object ReturnToWooPos : MultiLiveEvent.Event()
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectE
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.OpenLocationSettings
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.OpenPermissionsSettings
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.OpenWPComWebView
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.PopBackStackForWooPOS
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.RequestBluetoothRuntimePermissions
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.RequestEnableBluetooth
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.RequestLocationPermissions
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.ReturnToWooPos
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.ShowCardReaderTutorial
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.ShowToast
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.ShowToastString
Expand All @@ -59,9 +59,6 @@ import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectV
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectViewState.MultipleExternalReadersFoundState
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectViewState.ScanningFailedState
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.CardReadersHub
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund.Payment
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund.Refund
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderOnboardingChecker
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType.BUILT_IN
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType.EXTERNAL
Expand Down Expand Up @@ -523,25 +520,21 @@ class CardReaderConnectViewModel @Inject constructor(
}

private fun exitFlow(connected: Boolean) {
if (!connected) {
when (val param = arguments.cardReaderFlowParam) {
is CardReadersHub, is Refund -> triggerEvent(ExitWithResult(false))
is Payment -> {
if (param.paymentType == Payment.PaymentType.WOO_POS) {
returnToWooPos()
} else {
triggerEvent(ExitWithResult(false))
}
}
CardReaderFlowParam.WooPosConnection -> returnToWooPos()
}
} else {
triggerEvent(ShowCardReaderTutorial(arguments.cardReaderFlowParam, arguments.cardReaderType))
val param = arguments.cardReaderFlowParam
when {
param is CardReaderFlowParam.WooPosConnection -> returnToWooPos()
!connected -> triggerEvent(ExitWithResult(false))
else -> triggerEvent(
ShowCardReaderTutorial(
arguments.cardReaderFlowParam,
arguments.cardReaderType
)
)
}
}

private fun returnToWooPos() {
triggerEvent(PopBackStackForWooPOS)
triggerEvent(ReturnToWooPos)
}

private fun storeConnectedReader(cardReader: CardReader) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.asLiveData
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import com.woocommerce.android.AppPrefs
import com.woocommerce.android.cardreader.CardReaderManager
import com.woocommerce.android.cardreader.payments.PaymentData
Expand Down Expand Up @@ -68,7 +67,6 @@ class CardReaderPaymentViewModel @Inject constructor(
}

private val paymentController = CardReaderPaymentController(
scope = viewModelScope,
cardReaderManager = cardReaderManager,
orderRepository = orderRepository,
selectedSite = selectedSite,
Expand Down Expand Up @@ -120,15 +118,12 @@ class CardReaderPaymentViewModel @Inject constructor(
fun retry(orderId: Long, billingEmail: String, paymentData: PaymentData, amountLabel: String) =
paymentController.retry(orderId, billingEmail, paymentData, amountLabel)

@VisibleForTesting
fun reFetchOrder() = paymentController.reFetchOrder()

fun onViewCreated() = paymentController.onViewCreated()

fun onPrintResult(result: PrintJobResult) = paymentController.onPrintResult(result)

@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public override fun onCleared() = paymentController.onCleared()
public override fun onCleared() = paymentController.stop()

fun onBackPressed() = paymentController.onBackPressed()
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,16 @@ import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.FAILED
import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.STARTED
import com.woocommerce.android.util.WooLog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.wordpress.android.fluxc.store.WooCommerceStore
Expand All @@ -88,7 +92,6 @@ private const val CANADA_FEE_FLAT_IN_CENTS = 15L

@Suppress("LongParameterList", "LargeClass")
class CardReaderPaymentController(
private val scope: CoroutineScope,
private val cardReaderManager: CardReaderManager,
private val orderRepository: OrderDetailRepository,
private val selectedSite: SelectedSite,
Expand All @@ -113,6 +116,8 @@ class CardReaderPaymentController(
private val cardReaderType: CardReaderType,
private val isTTPPaymentInProgress: KMutableProperty0<Boolean>,
) {
private var scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)

private val _paymentState: MutableStateFlow<CardReaderPaymentOrRefundState> =
MutableStateFlow(CardReaderPaymentState.LoadingData(::onCancelPaymentFlow))
val paymentState: StateFlow<CardReaderPaymentOrRefundState> = _paymentState
Expand All @@ -123,10 +128,6 @@ class CardReaderPaymentController(

private var refetchOrderJob: Job? = null

private val CardReaderFlowParam.PaymentOrRefund.isPOS: Boolean
get() = this is CardReaderFlowParam.PaymentOrRefund.Payment &&
this.paymentType == CardReaderFlowParam.PaymentOrRefund.Payment.PaymentType.WOO_POS

private val _event: MutableSharedFlow<CardReaderPaymentEvent> = MutableSharedFlow()
val event: Flow<CardReaderPaymentEvent> = _event

Expand All @@ -135,6 +136,8 @@ class CardReaderPaymentController(
}

fun start() {
scope.cancel()
scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
if (cardReaderManager.readerStatus.value is CardReaderStatus.Connected) {
startFlowWhenReaderConnected()
} else {
Expand Down Expand Up @@ -457,20 +460,10 @@ class CardReaderPaymentController(
) {
paymentReceiptHelper.storeReceiptUrl(orderId, paymentStatus.receiptUrl)
appPrefs.setCardReaderSuccessfulPaymentTime()
if (paymentOrRefund.isPOS) {
scope.launch {
syncOrderStatus(orderId)
triggerEvent(CardReaderPaymentEvent.Exit)
}
} else {
triggerEvent(CardReaderPaymentEvent.PlaySuccessfulPaymentSound)
showPaymentSuccessfulState()
reFetchOrder()
}
}

private suspend fun syncOrderStatus(orderId: Long) {
orderRepository.fetchOrderById(orderId)
triggerEvent(CardReaderPaymentEvent.PlaySuccessfulPaymentSound)
showPaymentSuccessfulState()
reFetchOrder()
}

@VisibleForTesting
Expand Down Expand Up @@ -792,10 +785,11 @@ class CardReaderPaymentController(
}
}

fun onCleared() {
fun stop() {
paymentDataForRetry?.let {
cardReaderManager.cancelPayment(it)
}
scope.cancel()
}

fun onBackPressed() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.woocommerce.android.ui.payments.cardreader.payment.controller

import com.woocommerce.android.AppPrefs
import com.woocommerce.android.cardreader.CardReaderManager
import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.orders.details.OrderDetailRepository
import com.woocommerce.android.ui.payments.cardreader.CardReaderCountryConfigProvider
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund.Payment.PaymentType
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderOnboardingChecker
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType
import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderInteracRefundErrorMapper
import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderInteracRefundableChecker
import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderPaymentCollectibilityChecker
import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderPaymentErrorMapper
import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderPaymentOrderHelper
import com.woocommerce.android.ui.payments.receipt.PaymentReceiptHelper
import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare
import com.woocommerce.android.ui.payments.tracking.CardReaderTrackingInfoKeeper
import com.woocommerce.android.ui.payments.tracking.PaymentsFlowTracker
import com.woocommerce.android.util.CoroutineDispatchers
import com.woocommerce.android.util.CurrencyFormatter
import org.wordpress.android.fluxc.store.WooCommerceStore
import javax.inject.Inject
import kotlin.reflect.KMutableProperty0

class CardReaderPaymentControllerFactory @Inject constructor(
private val cardReaderManager: CardReaderManager,
private val orderRepository: OrderDetailRepository,
private val selectedSite: SelectedSite,
private val appPrefs: AppPrefs = AppPrefs,
private val paymentCollectibilityChecker: CardReaderPaymentCollectibilityChecker,
private val interacRefundableChecker: CardReaderInteracRefundableChecker,
private val tracker: PaymentsFlowTracker,
private val trackCancelledFlow: CardReaderTrackCanceledFlowAction,
private val currencyFormatter: CurrencyFormatter,
private val errorMapper: CardReaderPaymentErrorMapper,
private val interacRefundErrorMapper: CardReaderInteracRefundErrorMapper,
private val wooStore: WooCommerceStore,
private val dispatchers: CoroutineDispatchers,
private val cardReaderTrackingInfoKeeper: CardReaderTrackingInfoKeeper,
private val paymentStateProvider: CardReaderPaymentStateProvider,
private val cardReaderPaymentOrderHelper: CardReaderPaymentOrderHelper,
private val paymentReceiptHelper: PaymentReceiptHelper,
private val cardReaderOnboardingChecker: CardReaderOnboardingChecker,
private val cardReaderConfigProvider: CardReaderCountryConfigProvider,
private val paymentReceiptShare: PaymentReceiptShare,
) {
fun create(
orderId: Long,
paymentType: PaymentType,
isTTPPaymentInProgress: KMutableProperty0<Boolean>,
): CardReaderPaymentController = CardReaderPaymentController(
cardReaderManager = cardReaderManager,
orderRepository = orderRepository,
selectedSite = selectedSite,
appPrefs = appPrefs,
paymentCollectibilityChecker = paymentCollectibilityChecker,
interacRefundableChecker = interacRefundableChecker,
tracker = tracker,
trackCancelledFlow = trackCancelledFlow,
currencyFormatter = currencyFormatter,
errorMapper = errorMapper,
interacRefundErrorMapper = interacRefundErrorMapper,
wooStore = wooStore,
dispatchers = dispatchers,
cardReaderTrackingInfoKeeper = cardReaderTrackingInfoKeeper,
paymentStateProvider = paymentStateProvider,
cardReaderPaymentOrderHelper = cardReaderPaymentOrderHelper,
paymentReceiptHelper = paymentReceiptHelper,
cardReaderOnboardingChecker = cardReaderOnboardingChecker,
cardReaderConfigProvider = cardReaderConfigProvider,
paymentReceiptShare = paymentReceiptShare,
paymentOrRefund = PaymentOrRefund.Payment(
orderId = orderId,
paymentType = paymentType
),
cardReaderType = CardReaderType.EXTERNAL,
isTTPPaymentInProgress = isTTPPaymentInProgress,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import com.woocommerce.android.extensions.navigateSafely
import com.woocommerce.android.ui.payments.PaymentsBaseDialogFragment
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType.BUILT_IN
import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderActivity
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

Expand Down Expand Up @@ -60,12 +59,7 @@ class CardReaderTutorialDialogFragment : PaymentsBaseDialogFragment(R.layout.car
private fun navigateNext() {
when (val param = args.cardReaderFlowParam) {
is CardReaderFlowParam.CardReadersHub -> findNavController().popBackStack()
is CardReaderFlowParam.WooPosConnection -> {
parentFragmentManager.setFragmentResult(
WooPosCardReaderActivity.WOO_POS_CARD_CONNECTION_REQUEST_KEY,
Bundle(),
)
}
is CardReaderFlowParam.WooPosConnection -> error("Not supported param: $param")
is CardReaderFlowParam.PaymentOrRefund -> {
val action = CardReaderTutorialDialogFragmentDirections
.actionCardReaderTutorialDialogFragmentToCardReaderPaymentDialogFragment(param, args.cardReaderType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ data class NavigateToCardReaderPaymentFlow(
val cardReaderType: CardReaderType
) : MultiLiveEvent.Event()

data class SkipScreenInPosAndNavigateToCardReaderPaymentFlow(
val cardReaderFlowParam: CardReaderFlowParam.PaymentOrRefund.Payment,
val cardReaderType: CardReaderType
) : MultiLiveEvent.Event()

data class NavigateToCardReaderRefundFlow(
val cardReaderFlowParam: CardReaderFlowParam.PaymentOrRefund.Refund,
val cardReaderType: CardReaderType
Expand All @@ -41,11 +36,6 @@ data class NavigateToOrderDetails(
val orderId: Long
) : MultiLiveEvent.Event()

sealed class ReturnResultToWooPos : MultiLiveEvent.Event() {
data object Success : ReturnResultToWooPos()
data object Failure : ReturnResultToWooPos()
}

data class NavigateToTapToPaySummary(
val order: Order
) : MultiLiveEvent.Event()
Expand Down
Loading

0 comments on commit af499bf

Please sign in to comment.