Facebook
From Subtle Bird, 3 Days ago, written in Java 5.
Embed
Download Paste or View Raw
Hits: 28
  1. package com.app.features.checkout
  2.  
  3. import androidx.lifecycle.LiveData
  4. import androidx.lifecycle.MutableLiveData
  5. import com.app.base.viewmodel.AppBaseViewModel
  6. import com.app.core.enums.DeliveryTime
  7. import com.app.core.enums.ShippingType
  8. import com.app.core.errors.AppError
  9. import com.app.core.graphql.errorhandling.NetworkErrorHandlingImpl
  10. import com.app.core.models.*
  11. import com.app.core.repositories.AuthenticationRepo
  12. import com.app.core.repositories.CheckoutRepository
  13. import com.app.core.repositories.StoreConfigRepo
  14. import com.app.core.repositories.UserRepository
  15. import com.app.core.usecases.*
  16. import com.app.core.utils.LocaleManager
  17. import com.app.features.checkout.models.AppTempAddress
  18. import com.app.features.checkout.models.AppTempContactInfo
  19. import com.app.features.checkout.models.UIDeliveryAddress
  20. import com.app.features.checkout.usecases.*
  21. import com.payfort.sdk.android.dependancies.base.FortInterfaces
  22. import com.payfort.sdk.android.dependancies.models.FortRequest
  23. import com.paymob.acceptsdk.IntentConstants.*
  24. import com.robusta.bootstrap.data.api.observable.addTo
  25. import com.robusta.bootstrap.service.schedulers.SchedulersService
  26. import com.robusta.bootstrap.service.session.SessionService
  27. import com.robustastudio.checkout_feat.CartPriceDetails
  28. import com.robustastudio.ecommerce.analytics_feat.AnalyticsConstants
  29. import com.robustastudio.ecommerce.analytics_feat.AnalyticsService
  30. import com.robustastudio.magentocore.model.IAvailablePaymentMethod
  31. import com.robustastudio.magentocore.model.product.ProductSectionType
  32. import com.robustastudio.magentocore.utils.ProductInfoCache
  33. import com.robustastudio.networkclient.utils.Errors
  34. import io.reactivex.rxjava3.core.Completable
  35. import io.reactivex.rxjava3.core.Single
  36. import org.koin.core.component.inject
  37. import java.util.HashMap
  38.  
  39. const val FREE_PAYMENT_KEY = "free"
  40.  
  41. class CheckoutViewModel(
  42.     private val checkoutRepository: CheckoutRepository,
  43.     private val sessionService: SessionService,
  44.     schedulersService: SchedulersService,
  45.     private val getUserAddressesUseCase: GetUserAddressesUseCase,
  46.     private val getDefaultUserLocationUseCase: GetDefaultUserLocationUseCase,
  47.     private val getAvailableDeliveryMethodsUseCase: GetAvailableDeliveryMethodsUseCase,
  48.     private val setShippingMethodUseCase: SetShippingMethodUseCase,
  49.     private val setDeliveryTimeUseCase: SetDeliveryTimeUseCase,
  50.     private val getAvailablePaymentMethodsUseCase: GetAvailablePaymentMethodsUseCase,
  51.     private val authenticationRepo: AuthenticationRepo,
  52.     private val setPaymentMethodOnCartUseCase: SetPaymentMethodOnCartUseCase,
  53.     private val getAvailableRegionsUseCase: GetAvailableRegionsUseCase,
  54.     private val fetchGovernoratesUseCase: FetchGovernoratesUseCase,
  55.     private val proceedWithCreditCardUseCase: ProceedWithCreditCardUseCase,
  56.     private val proceedWithCashUseCase: ProceedWithCashUseCase,
  57.     private val fetchCartPriceDetailsUseCase: FetchCartPriceDetailsUseCase,
  58.     private val fetchDeliveryDatesUseCase: FetchDeliveryDatesUseCase,
  59.     private val fetchCartItemTypesUseCase: FetchCartItemTypesUseCase,
  60.     private val setOrderSourceOnCartUseCase: SetOrderSourceOnCartUseCase,
  61.     private val getPayFortSdkTokenUseCase: GetPayFortSdkTokenUseCase,
  62.     private val applyLoyaltyPointsUseCase: ApplyLoyaltyPointsUseCase,
  63.     private val getPaymobIframeUrlOrReferenceNumberUseCase: GetPaymobIframeUrlOrReferenceNumberUseCase,
  64.     private val userRepository: UserRepository,
  65.     private val getLoyaltyPointsExchangeRatesUseCase: GetLoyaltyPointsExchangeRatesUseCase,
  66.     private val storeConfigRepo: StoreConfigRepo,
  67. ) : AppBaseViewModel<CheckoutState, CheckoutEvent, CheckoutSideEffect>(schedulersService),
  68.     FortInterfaces.OnTnxProcessed {
  69.  
  70.     override val stateMachine = getCheckoutStateMachine()
  71.  
  72.     private val analyticsService: AnalyticsService by inject()
  73.  
  74.     private var selectedDeliveryAddress: UIDeliveryAddress? = null
  75.     private var cachedUserAddresses: List<UIDeliveryAddress> = mutableListOf()
  76.  
  77.     private var selectedPaymentMethod: AppAvailablePaymentMethod? = null
  78.     private var availablePaymentMethods: List<AppAvailablePaymentMethod>? = null
  79.  
  80.     private var selectedShippingMethod: AppAvailableDeliveryMethod? = null
  81.     private var availableShippingMethods: List<AppAvailableDeliveryMethod>? = null
  82.  
  83.     private var selectedDeliveryDay: AppDeliveryDay? = null
  84.     var availableDeliveryDays: List<AppDeliveryDay>? = null
  85.  
  86.     private var selectedDeliveryTime: DeliveryTime = DeliveryTime.ASAP
  87.     private var selectedDeliveryInterval: AppDeliveryInterval? = null
  88.     private var availableDeliveryIntervals: List<AppDeliveryInterval>? = null
  89.  
  90.     private var selectedStore: AppShippingLocation? = null
  91.  
  92.     private var availableCities: List<AppRegion>? = null
  93.     private var guestContactInfo: AppTempContactInfo? = null
  94.     private var guestTempAddress: AppTempAddress? = null
  95.     private var cartPriceDetails: CartPriceDetails? = null
  96.  
  97.     private var selectedShippingType: ShippingType = ShippingType.HOME
  98.  
  99.     private var hasNonGroceryItems: Boolean = false
  100.     private var hasGroceryItems: Boolean = false
  101.  
  102.     val defaultLocation = getDefaultUserLocationUseCase.execute()
  103.  
  104.  
  105.     private val _priceDetails = MutableLiveData<CartPriceDetails>()
  106.     val priceDetails: LiveData<CartPriceDetails>
  107.         get() = _priceDetails
  108.  
  109.     private val _promoCodeState = MutableLiveData<PromoCodeState>(PromoCodeState.Input)
  110.     val promoCodeStateLiveData: LiveData<PromoCodeState>
  111.         get() = _promoCodeState
  112.  
  113.     init {
  114.         analyticsService.logBeginCheckout()
  115.         analyticsService.logEventToFB(AnalyticsConstants.FACEBOOK_ANALYTICS_EVENTS.EVENT_INITIATE_CHECKOUT)
  116.         loadShippingDataForDelivery()
  117.     }
  118.  
  119.     fun loadShippingDataForDelivery(shippingAddress: UIDeliveryAddress? = null) {
  120.         selectedShippingType = ShippingType.HOME
  121.         if (isGuest()) {
  122.             fetchGovernoratesUseCase.execute()
  123.                 .zipWith(fetchCartPriceDetailsUseCase.execute()) { regions, prices ->
  124.                     Pair(regions, prices)
  125.                 }.compose(getIOTransformer()).doOnSubscribe {
  126.                     //SHOW LOADING
  127.                     handleEvent(CheckoutEvent.StartLoadingCheckoutData)
  128.                 }.subscribe({
  129.                     cartPriceDetails = it.second
  130.                     availableCities = it.first
  131. //                    handleEvent(
  132. //                        CheckoutEvent.OnShippingDataLoaded(
  133. //                            availableDeliveryDays = emptyList(),
  134. //                            availableAddresses = emptyList(),
  135. //                            isGuest = true,
  136. //                            selectedShippingType = ShippingType.HOME,
  137. //                            cities = it.first.sortedBy { it.cities.isEmpty() },
  138. //                            cartPriceDetails = it.second,
  139. //                            electronicsCount = electronicsCount,
  140. //                            groceriesCount = groceriesCount
  141. //                        )
  142. //                    )
  143.                 }, NetworkErrorHandlingImpl {
  144.                     handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
  145.                 }).addTo(compositeDisposable)
  146.         } else {
  147.             val location = getDefaultUserLocationUseCase.execute()
  148.             getUserAddressesUseCase.execute().zipWith(getAvailableRegionsUseCase.execute(), ::Pair)
  149.                 .flatMap {
  150.                     cachedUserAddresses = it.first.mapIndexed { index, address ->
  151.                         val isInDeliveryZone = isAddressSupportedByStoreCode(
  152.                             address = address,
  153.                             storeCode = location?.availableStore?.storeCode!!,
  154.                             it.second
  155.                         )
  156.                         UIDeliveryAddress(
  157.                             isInDeliveryZone = isInDeliveryZone,
  158.                             id = address.id,
  159.                             addressName = if (address.addressName != null && address.addressName!!.isNotEmpty()) address.addressName else "Address #${index + 1}",
  160.                             district = address.district,
  161.                             city = address.city,
  162.                             region = address.region,
  163.                             apartment = address.apartment,
  164.                             lastName = address.lastName,
  165.                             firstName = address.firstName,
  166.                             defaultShipping = address.defaultShipping,
  167.                             floor = address.floor,
  168.                             defaultBilling = address.defaultBilling,
  169.                             building = address.building,
  170.                             countryCode = address.countryCode,
  171.                             customerNotes = address.customerNotes,
  172.                             street = address.street,
  173.                             phone = address.phone,
  174.                             lat = address.lat,
  175.                             lng = address.lng,
  176.                             compound = address.compound,
  177.                         )
  178.                     }
  179.                     if (cachedUserAddresses.isNotEmpty()) {
  180.                         //WHAT IF THE SHIPPING ADDRESS IS NOT IN THE LOADED LIST?
  181.                         selectedDeliveryAddress =
  182.                             shippingAddress ?: cachedUserAddresses.first { it.isInDeliveryZone }
  183.                         getAvailableDeliveryMethodsUseCase.execute(
  184.                             selectedDeliveryAddress!!.id!!
  185.                         ).zipWith(
  186.                             fetchDeliveryDatesUseCase.execute(
  187.                                 selectedDeliveryAddress!!.city!!.code,
  188.                                 selectedDeliveryAddress!!.district!!.id.toString()
  189.                             )
  190.                         ) { methods: List<AppAvailableDeliveryMethod>, days: List<AppDeliveryDay> ->
  191.                             Pair(methods, days)
  192.                         }
  193.                     } else Single.just(
  194.                         Pair(
  195.                             emptyList(), emptyList()
  196.                         )
  197.                     )
  198.                 }.flatMapCompletable {
  199.                     availableShippingMethods = it.first
  200.                     availableDeliveryDays = it.second
  201.                     if (it.first.isNotEmpty() && it.second.isNotEmpty()) {
  202.                         selectedShippingMethod =
  203.                             it.first.filterNot { it.methodCode.contains("store") }.first()
  204.                         selectedDeliveryDay = it.second.first()
  205.                         availableDeliveryIntervals = selectedDeliveryDay!!.intervals
  206.                         selectedDeliveryInterval = availableDeliveryIntervals!!.first()
  207.                         setShippingMethodUseCase.execute(
  208.                             selectedShippingMethod!!
  209.                         ).andThen(
  210.                             setDeliveryTimeUseCase.execute(
  211.                                 selectedDeliveryDay!!.date, selectedDeliveryInterval!!.id
  212.                             )
  213.                         )
  214.                     } else Completable.complete()
  215.                 }.andThen(Single.zip(
  216.                     fetchCartPriceDetailsUseCase.execute(),
  217.                     fetchCartItemTypesUseCase.execute(),
  218.                     getLoyaltyPointsExchangeRatesUseCase.execute()
  219.                 ) { price, items, pointsRate ->
  220.                     Triple(price, items, pointsRate)
  221.                 }).flatMap {
  222.                     if (it.first.grandTotal == 0.0) {
  223.                         selectedPaymentMethod = AppAvailablePaymentMethod(
  224.                             FREE_PAYMENT_KEY, "", 0f
  225.                         )
  226.                         setPaymentMethodOnCartUseCase.execute(
  227.                             AppAvailablePaymentMethod(
  228.                                 FREE_PAYMENT_KEY, "", 0f
  229.                             )
  230.                         ).andThen(Single.just(it))
  231.                     } else {
  232.                         getAvailablePaymentMethodsUseCase.execute().flatMapCompletable { list ->
  233.                             availablePaymentMethods = list
  234.                             selectedPaymentMethod = list.first()
  235.                             setPaymentMethodOnCartUseCase.execute(list.first())
  236.                         }.andThen(Single.just(it))
  237.                     }
  238.                 }.compose(getIOTransformer()).doOnSubscribe {
  239.                     //SHOW LOADING
  240.                     handleEvent(CheckoutEvent.StartLoadingCheckoutData)
  241.                 }.subscribe({
  242.                     cartPriceDetails = it.first
  243.                     hasNonGroceryItems =
  244.                         it.second.contains(ProductSectionType.ELECTRONICS) || it.second.contains(
  245.                             ProductSectionType.CLOTHING
  246.                         )
  247.                     hasNonGroceryItems =
  248.                         it.second.contains(ProductSectionType.NON_ELECTRONICS)
  249.                     handleEvent(
  250.                         CheckoutEvent.OnShippingDataLoaded(
  251.                             availableDeliveryDays = availableDeliveryDays,
  252.                             availableAddresses = cachedUserAddresses,
  253.                             isGuest = isGuest(),
  254.                             hasNonGroceryItems = hasNonGroceryItems,
  255.                             hasGroceryItems = hasGroceryItems,
  256.                             selectedShippingType = ShippingType.HOME,
  257.                             selectedDeliveryAddress = selectedDeliveryAddress!!,
  258.                             cartPriceDetails = cartPriceDetails!!,
  259.                             availablePaymentMethods = if (selectedPaymentMethod!!.code == FREE_PAYMENT_KEY) emptyList() else availablePaymentMethods!!,
  260.                             selectedPaymentMethod = selectedPaymentMethod!!,
  261.                             selectedDeliveryDay = selectedDeliveryDay!!,
  262.                             selectedDeliveryInterval = selectedDeliveryInterval!!,
  263.                             user = userRepository.getUserFromCache()!!,
  264.                             pointsRate = it.third
  265.                         )
  266.                     )
  267.                 }, NetworkErrorHandlingImpl {
  268.                     handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
  269.                 }).addTo(compositeDisposable)
  270.         }
  271.     }
  272.  
  273.     private fun canProceedToPayment(): Boolean = if (isGuest()) {
  274.         (selectedDeliveryAddress != null || selectedStore != null || guestTempAddress != null) && if (selectedShippingType == ShippingType.STORE) guestContactInfo != null
  275.         else selectedShippingMethod != null
  276.     } else selectedDeliveryAddress != null
  277.  
  278.     private fun loadShippingDataForPickup() {
  279. //        selectedShippingType = ShippingType.STORE
  280. //        getAvailableStoresUseCase.execute()
  281. //            .compose(getIOTransformer())
  282. //            .doOnSubscribe {
  283. //                //SHOW LOADING
  284. //                handleEvent(CheckoutEvent.StartLoadingCheckoutData)
  285. //            }
  286. //            .subscribe({
  287. //                availableStores = it
  288. //                selectedStore = it.first()
  289. //                handleEvent(
  290. //                    CheckoutEvent.OnShippingDataLoaded(
  291. //                        availableDeliveryDays = availableDeliveryDays,
  292. //                        availableAddresses = cachedUserAddresses,
  293. //                        isGuest = isGuest(),
  294. //                        selectedShippingType = ShippingType.STORE,
  295. //                        selectedDeliveryAddress = selectedDeliveryAddress,
  296. //                        selectedMethod = selectedShippingMethod,
  297. //                        availableStores = availableStores,
  298. //                        selectedStore = selectedStore,
  299. //                        canProceedToPayment = canProceedToPayment(),
  300. //                        cartPriceDetails = cartPriceDetails!!,
  301. //                        electronicsCount = electronicsCount,
  302. //                        groceriesCount = groceriesCount
  303. //                    )
  304. //                )
  305. //            }, NetworkErrorHandlingImpl {
  306. //                handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
  307. //            }).addTo(compositeDisposable)
  308.     }
  309.  
  310.     private fun isGuest() = !authenticationRepo.isUserLoggedIn()
  311.  
  312.     private fun fetchPaymentMethods(): Single<List<AppAvailablePaymentMethod>> {
  313.         return checkoutRepository.getAvailablePaymentMethods().map {
  314.             it.map(IAvailablePaymentMethod::toModel)
  315.         }
  316.     }
  317.  
  318.     fun onPaymentMethodSelected(paymentMethod: AppAvailablePaymentMethod) {
  319.         setPaymentMethodOnCartUseCase.execute(
  320.             paymentMethod
  321.         ).andThen(fetchCartPriceDetailsUseCase.execute()).compose(getIOTransformer())
  322.             .doOnSubscribe {
  323.                 selectedPaymentMethod = paymentMethod
  324.                 handleEvent(CheckoutEvent.OnPaymentMethodSelected(paymentMethod))
  325.             }.subscribe({
  326.                 handleEvent(CheckoutEvent.OnCartPricesUpdated(it))
  327.                 cartPriceDetails = it
  328.             }, NetworkErrorHandlingImpl {
  329.                 simpleFeedbackLiveEvent.value = it
  330.             }).addTo(compositeDisposable)
  331.         selectedPaymentMethod = paymentMethod
  332.     }
  333. //
  334. //    //SETS ADDRESS ON CART AND GET AVAILABLE DELIVERY METHODS
  335. //    //THEN SETS THE FIRST DELIVERY METHOD AS SELECTED
  336. //    private fun setDeliveryAddressAndGetAvailableMethods(): Single<AppAvailableDeliveryMethod> {
  337. //        // There are 2 cases when adding a shipping address:
  338. //        // 1. If there is a default address, send the default address id.
  339. //        // 2. If there is no default address, send the first address id.
  340. //        val address = cachedUserAddresses.find { address ->
  341. //            address.defaultShipping == true
  342. //        } ?: cachedUserAddresses.first()
  343. //        selectShippingAddress(address)
  344. //        return checkoutRepository.setBillingAddressAndGetDeliveryMethods(address.id)
  345. //            .map {
  346. //                it.map(IAvailableDeliveryMethod::toModel)
  347. //            }.doOnSuccess {
  348. //                availableShippingMethods = it
  349. //            }
  350. //            .flatMap {
  351. //                checkoutRepository.setShippingMethodOnCart(
  352. //                    carrierCode = selectedShippingMethod?.carrierCode ?: it.first().carrierCode,
  353. //                    methodCode = selectedShippingMethod?.methodCode ?: it.first().methodCode,
  354. //                ).andThen(Single.just(it.first()))
  355. //            }
  356. //    }
  357.  
  358.     fun onASAPDeliverySelected() {
  359.         selectedDeliveryTime = DeliveryTime.ASAP
  360.         selectedDeliveryDay = availableDeliveryDays!!.first()
  361.         selectedDeliveryInterval = selectedDeliveryDay!!.intervals.first()
  362.         setDeliveryTimeUseCase.execute(
  363.             selectedDeliveryDay!!.date, selectedDeliveryInterval!!.id
  364.         ).compose(getIOTransformer<Completable>()).doOnSubscribe {
  365.             handleEvent(
  366.                 CheckoutEvent.OnDeliveryTimeSelected(
  367.                     DeliveryTime.ASAP,
  368.                     selectedDeliveryInterval = selectedDeliveryInterval!!,
  369.                     selectedDeliveryDay = selectedDeliveryDay!!,
  370.                     blockPlaceOrder = true
  371.                 )
  372.             )
  373.         }.subscribe({
  374.             handleEvent(
  375.                 CheckoutEvent.OnDeliveryTimeSelected(
  376.                     DeliveryTime.ASAP,
  377.                     selectedDeliveryInterval = selectedDeliveryInterval!!,
  378.                     selectedDeliveryDay = selectedDeliveryDay!!,
  379.                     blockPlaceOrder = false
  380.                 )
  381.             )
  382.         }, NetworkErrorHandlingImpl {
  383.             handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
  384.         }).addTo(compositeDisposable)
  385.     }
  386.  
  387.  
  388.     fun clearCart() {
  389.         ProductInfoCache.resetCartCount()
  390.         sessionService.removeCartId()
  391.     }
  392.  
  393.     fun handleTransaction(resultCode: Int, paymentId: String?) {
  394.         when (resultCode) {
  395.             TRANSACTION_SUCCESSFUL, TRANSACTION_SUCCESSFUL_PARSING_ISSUE -> {
  396.                 handleEvent(
  397.                     CheckoutEvent.OnOrderPlacedSuccessfully(
  398.                         listOf(paymentId!!)
  399.                     )
  400.                 )
  401.             }
  402.             TRANSACTION_REJECTED, TRANSACTION_REJECTED_PARSING_ISSUE -> {
  403.                 handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR("تم رفض المعاملة")))
  404.             }
  405.             USER_CANCELED, USER_CANCELED_3D_SECURE_VERIFICATION, USER_CANCELED_3D_SECURE_VERIFICATION_PARSING_ISSUE -> {
  406.                 handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR("تم إلغاء المعاملة")))
  407.             }
  408.             else -> {
  409.                 handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR("حدث خطأ محهول")))
  410.             }
  411.         }
  412.     }
  413.  
  414.     private fun proceedWithCreditCard() {
  415.         analyticsService.logPaymentMethodOptionSelection(selectedPaymentMethod!!.title)
  416.         proceedWithCreditCardUseCase.execute(selectedPaymentMethod!!.code)
  417.             .compose(getIOTransformer()).doOnSubscribe {
  418.                 handleEvent(CheckoutEvent.OnPlaceOrderClicked)
  419.             }.subscribe({
  420.                 handleEvent(CheckoutEvent.PlaceOrderWithCC(it))
  421.             }, NetworkErrorHandlingImpl {
  422.                 handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
  423.             }).addTo(compositeDisposable)
  424.     }
  425.  
  426.  
  427.     private fun proceedWithPayFort() {
  428.         analyticsService.logPaymentMethodOptionSelection(selectedPaymentMethod!!.title)
  429.         (checkoutRepository.setOrderSource())
  430.             .andThen(
  431.                 getPayFortSdkTokenUseCase.execute()
  432.             )
  433.             .compose(getIOTransformer<Pair<String, String>>())
  434.             .doOnSubscribe {
  435.                 handleEvent(CheckoutEvent.OnPlaceOrderClicked)
  436.             }
  437.             .subscribe({
  438.                 // prepare payment request
  439.                 val fortRequest = FortRequest()
  440.                 fortRequest.isShowResponsePage = true
  441.                 fortRequest.requestMap = collectRequestMap(
  442.                     it.first,
  443.                     it.second,
  444.                     sessionService.getCurrentUser(AppUser::class.java)!!.emailAddress,
  445.                     cartPriceDetails!!.grandTotal.toFloat()
  446.                 )
  447.                 // to [display/use] the SDK response page
  448.                 simpleFeedbackLiveEvent.value =
  449.                     CheckoutClickActions.ProceedWithPayFortPayment(
  450.                         fortRequest
  451.                     )
  452.  
  453.             }, NetworkErrorHandlingImpl {
  454.                 handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
  455.             })
  456.             .addTo(compositeDisposable)
  457.     }
  458.  
  459.     private fun proceedWithPaymob() {
  460.         analyticsService.logPaymentMethodOptionSelection(selectedPaymentMethod!!.title)
  461.         setOrderSourceOnCartUseCase.execute()
  462.             .andThen(getPaymobIframeUrlOrReferenceNumberUseCase.execute())
  463.             .compose(getIOTransformer()).doOnSubscribe {
  464.                 handleEvent(CheckoutEvent.OnPlaceOrderClicked)
  465.             }.subscribe({
  466.                 handleEvent(CheckoutEvent.OnPendingGateway)
  467.                 simpleFeedbackLiveEvent.value = CheckoutClickActions.ProceedWithPaymobPayment(
  468.                     iFrameUrl = if (selectedPaymentMethod!!.code != PaymentMethod.PAYMOB_KIOSK.code) it else null,
  469.                     referenceNumber = if (selectedPaymentMethod!!.code == PaymentMethod.PAYMOB_KIOSK.code) it else null
  470.                 )
  471.  
  472.             }, NetworkErrorHandlingImpl {
  473.                 handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
  474.             }).addTo(compositeDisposable)
  475.  
  476.     }
  477.  
  478.     fun proceedWithCash() {
  479.         analyticsService.logPaymentMethodOptionSelection(selectedPaymentMethod!!.title)
  480.         setOrderSourceOnCartUseCase.execute().andThen(
  481.             proceedWithCashUseCase.execute()
  482.         ).doOnSuccess {
  483.             clearCart()
  484.         }.compose(getIOTransformer<List<String>>()).doOnSubscribe {
  485.             handleEvent(CheckoutEvent.OnPlaceOrderClicked)
  486.         }.subscribe({
  487.             handleEvent(CheckoutEvent.OnOrderPlacedSuccessfully(it))
  488.         }, NetworkErrorHandlingImpl {
  489.             handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
  490.         }).addTo(compositeDisposable)
  491.     }
  492.  
  493.     fun placeOrder() {
  494.         val minimumOrderAmount = getMinimumOrderAmount()
  495.         if (cartPriceDetails!!.subtotalWithDiscountExcludingTax < minimumOrderAmount) {
  496.             showSideEffect(CheckoutSideEffect.ShowMinimumOrderAmountError(minimumOrderAmount))
  497.         } else {
  498. //        if (selectedDeliveryAddress?.phoneVerified == true) {
  499.             if (selectedPaymentMethod?.code?.contains("accept") == true) {
  500.                 proceedWithPaymob()
  501.             } else if (selectedPaymentMethod?.code?.contains("fort") == true) {
  502.                 proceedWithPayFort()
  503.             } else {
  504.                 proceedWithCash()
  505.             }
  506. //        } else {
  507. //            simpleFeedbackLiveEvent.value =
  508. //                CheckoutClickActions.VerifyAddress(selectedDeliveryAddress!!)
  509. //        }
  510.         }
  511.     }
  512.  
  513.     fun onAddressSelected(shippingAddress: UIDeliveryAddress) {
  514.         fetchDeliveryDatesUseCase.execute(
  515.             selectedDeliveryAddress!!.city!!.code,
  516.             selectedDeliveryAddress!!.district!!.id.toString()
  517.         ).flatMap {
  518.             availableDeliveryDays = it
  519.             if (it.isNotEmpty()) {
  520.                 selectedDeliveryDay = it.first()
  521.                 availableDeliveryIntervals = selectedDeliveryDay!!.intervals
  522.                 selectedDeliveryInterval = availableDeliveryIntervals!!.first()
  523.                 setDeliveryTimeUseCase.execute(
  524.                     selectedDeliveryDay!!.date, selectedDeliveryInterval!!.id
  525.                 ).andThen(Single.just(it))
  526.             } else Single.just(it)
  527.         }.compose(getIOTransformer()).doOnSubscribe {
  528.             selectedDeliveryAddress = shippingAddress
  529.             handleEvent(CheckoutEvent.OnAddressSelected(shippingAddress))
  530.         }.subscribe({
  531.             handleEvent(
  532.                 CheckoutEvent.OnDeliveryDaysUpdated(
  533.                     it,
  534.                     selectedDeliveryDay = selectedDeliveryDay!!,
  535.                     selectedDeliveryInterval = selectedDeliveryInterval!!
  536.                 )
  537.             )
  538.         }, NetworkErrorHandlingImpl {
  539.             handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
  540.         }).addTo(compositeDisposable)
  541.     }
  542.  
  543.     fun onAddressVerified() {
  544.         val location = getDefaultUserLocationUseCase.execute()
  545.         getUserAddressesUseCase.execute().zipWith(getAvailableRegionsUseCase.execute(), ::Pair)
  546.             .doOnSuccess {
  547.                 cachedUserAddresses = it.first.mapIndexed { index, address ->
  548.                     val isInDeliveryZone = isAddressSupportedByStoreCode(
  549.                         address = address,
  550.                         storeCode = location?.availableStore?.storeCode!!,
  551.                         it.second
  552.                     )
  553.                     UIDeliveryAddress(
  554.                         isInDeliveryZone = isInDeliveryZone,
  555.                         id = address.id,
  556.                         addressName = if (address.addressName != null && address.addressName!!.isNotEmpty()) address.addressName else "Address #${index + 1}",
  557.                         district = address.district,
  558.                         city = address.city,
  559.                         region = address.region,
  560.                         apartment = address.apartment,
  561.                         lastName = address.lastName,
  562.                         firstName = address.firstName,
  563.                         defaultShipping = address.defaultShipping,
  564.                         floor = address.floor,
  565.                         defaultBilling = address.defaultBilling,
  566.                         building = address.building,
  567.                         countryCode = address.countryCode,
  568.                         customerNotes = address.customerNotes,
  569.                         street = address.street,
  570.                         phone = address.phone,
  571.                         lat = address.lat,
  572.                         lng = address.lng,
  573.                         compound = address.compound,
  574.                     )
  575.                 }
  576.                 selectedDeliveryAddress =
  577.                     cachedUserAddresses.find { it.id == selectedDeliveryAddress?.id }
  578.             }.compose(getIOTransformer()).doOnSubscribe {
  579.                 handleEvent(CheckoutEvent.StartLoadingCheckoutData)
  580.             }.subscribe({}, NetworkErrorHandlingImpl {
  581.                 handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
  582.             }).addTo(compositeDisposable)
  583.     }
  584.  
  585.     fun onAddressCreated(shippingAddress: AppShippingAddress) {
  586.         val location = getDefaultUserLocationUseCase.execute()
  587.         getAvailableRegionsUseCase.execute().compose(getIOTransformer()).subscribe({
  588.             loadShippingDataForDelivery(shippingAddress.let { address ->
  589.                 val isInDeliveryZone = isAddressSupportedByStoreCode(
  590.                     address = address, storeCode = location?.availableStore?.storeCode!!, it
  591.                 )
  592.                 UIDeliveryAddress(
  593.                     isInDeliveryZone = isInDeliveryZone,
  594.                     id = address.id,
  595.                     addressName = if (address.addressName != null && address.addressName!!.isNotEmpty()) address.addressName else "",
  596.                     district = address.district,
  597.                     city = address.city,
  598.                     region = address.region,
  599.                     apartment = address.apartment,
  600.                     lastName = address.lastName,
  601.                     firstName = address.firstName,
  602.                     defaultShipping = address.defaultShipping,
  603.                     floor = address.floor,
  604.                     defaultBilling = address.defaultBilling,
  605.                     building = address.building,
  606.                     countryCode = address.countryCode,
  607.                     customerNotes = address.customerNotes,
  608.                     street = address.street,
  609.                     phone = address.phone,
  610.                     lat = address.lat,
  611.                     lng = address.lng,
  612.                     compound = address.compound,
  613.                 )
  614.             })
  615.         }, {
  616.  
  617.         }).addTo(compositeDisposable)
  618.     }
  619.  
  620.     fun applyCouponToCart(couponCode: String) {
  621.         checkoutRepository.applyCouponToCart(couponCode).compose(getIOTransformer()).doOnSubscribe {
  622.             _promoCodeState.value = PromoCodeState.Applying
  623.             handleEvent(CheckoutEvent.OnCouponStateChange)
  624.         }.subscribe({
  625.             _promoCodeState.value = PromoCodeState.Showing(it.appliedCouponCode!!)
  626.             handleEvent(CheckoutEvent.OnCartPricesUpdated(it))
  627.         }, NetworkErrorHandlingImpl {
  628.             if (it is AppError.MagentoError && it.magentoThrowable.getWhichErrors() == Errors.WRONG_COUPON) _promoCodeState.value =
  629.                 PromoCodeState.InvalidPromoCode
  630.             else if (it is AppError.MagentoError && it.magentoThrowable.getWhichErrors() == Errors.COUPON_ERROR) _promoCodeState.value =
  631.                 PromoCodeState.AlreadyAppliedAnotherPromoCode
  632.             else {
  633.                 _promoCodeState.value = PromoCodeState.Input
  634.                 simpleFeedbackLiveEvent.value = it
  635.             }
  636.         }).addTo(compositeDisposable)
  637.     }
  638.  
  639.     fun removeCouponFromCart() {
  640.         checkoutRepository.removeCouponFromCart().compose(getIOTransformer()).doOnSubscribe {
  641.             handleEvent(CheckoutEvent.OnCouponStateChange)
  642.         }.subscribe({
  643.             handleEvent(CheckoutEvent.OnCartPricesUpdated(it))
  644.             _priceDetails.value = it
  645.             _promoCodeState.value = PromoCodeState.Input
  646.         }, NetworkErrorHandlingImpl {
  647.             simpleFeedbackLiveEvent.value = it
  648.             if (priceDetails.value?.appliedCouponCode != null) _promoCodeState.value =
  649.                 PromoCodeState.Showing(priceDetails.value?.appliedCouponCode!!)
  650.             else _promoCodeState.value = PromoCodeState.Input
  651.         }).addTo(compositeDisposable)
  652.     }
  653.  
  654.     fun onFailedToInitializeOpaySdk(exception: Exception) {
  655.         loggingService.e("Checkout Fragment", "opay Exception during payment")
  656.         loggingService.stackTrack(exception)
  657.         handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(exception.message)))
  658.     }
  659.  
  660.     fun onSuccess(
  661.         orderNumbers: List<String>
  662.     ) {
  663.         //VERY IMPORTANT TO CLEAR CART FROM ANY CACHED COUNTS
  664.         clearCart()
  665.         handleEvent(
  666.             CheckoutEvent.OnOrderPlacedSuccessfully(
  667.                 orderNumbers
  668.             )
  669.         )
  670.     }
  671.  
  672.     fun onFailure(message: String) {
  673.         handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(message)))
  674.     }
  675.  
  676.     fun proceedToPayment(note: String?) {
  677. //        if (selectedShippingType == ShippingType.STORE)
  678. //            setPickupFromStoreUseCase.execute(selectedStore!!, guestContactInfo)
  679. //                .zipWith(getAvailablePaymentMethodsUseCase.execute()) { shippingMethod, paymentMethods ->
  680. //                    Pair(shippingMethod, paymentMethods)
  681. //                }
  682. //                .flatMap { result ->
  683. //                    selectedPaymentMethod = result.second.first()
  684. //                    setPaymentMethodOnCartUseCase.execute(result.second.first())
  685. //                        .andThen(fetchCartPriceDetailsUseCase.execute()).flatMap {
  686. //                            cartPriceDetails = it
  687. //                            addNoteToCartUseCase.execute(note).andThen(
  688. //                                Single.just(result)
  689. //                            )
  690. //                        }
  691. //                }
  692. //                .compose(getIOTransformer())
  693. //                .doOnSubscribe {
  694. //                    handleEvent(CheckoutEvent.OnProceedToPayment)
  695. //                }
  696. //                .subscribe({
  697. //                    selectedShippingMethod = it.first
  698. //                    availablePaymentMethods = it.second
  699. //                    handleEvent(
  700. //                        CheckoutEvent.OnPaymentMethodsLoaded(
  701. //                            it.second,
  702. //                            selectedPaymentMethod!!,
  703. //                            selectedShippingMethod!!,
  704. //                            cartPriceDetails!!
  705. //                        )
  706. //                    )
  707. //                }, NetworkErrorHandlingImpl {
  708. //                    handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
  709. //                }).addTo(compositeDisposable)
  710. //        else {
  711. //            getAvailablePaymentMethodsUseCase.execute()
  712. //                .flatMap { list ->
  713. //                    selectedPaymentMethod = list.first()
  714. //                    setPaymentMethodOnCartUseCase.execute(list.first())
  715. //                        .andThen(fetchCartPriceDetailsUseCase.execute()).flatMap {
  716. //                            cartPriceDetails = it
  717. //                            addNoteToCartUseCase.execute(note).andThen(
  718. //                                Single.just(list)
  719. //                            )
  720. //                        }
  721. //                }
  722. //                .compose(getIOTransformer())
  723. //                .doOnSubscribe {
  724. //                    handleEvent(CheckoutEvent.OnProceedToPayment)
  725. //                }
  726. //                .subscribe({
  727. //                    availablePaymentMethods = it
  728. //                    handleEvent(
  729. //                        CheckoutEvent.OnPaymentMethodsLoaded(
  730. //                            it,
  731. //                            selectedPaymentMethod!!,
  732. //                            selectedShippingMethod!!,
  733. //                            cartPriceDetails!!
  734. //                        )
  735. //                    )
  736. //                }, NetworkErrorHandlingImpl {
  737. //                    handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
  738. //                }).addTo(compositeDisposable)
  739. //        }
  740.     }
  741.  
  742.     fun onShippingMethodSelected(shippingType: ShippingType) {
  743.         if (shippingType == ShippingType.HOME) {
  744.             loadShippingDataForDelivery()
  745.         } else {
  746.             loadShippingDataForPickup()
  747.         }
  748.     }
  749.  
  750.     fun onStoreSelected(store: AppShippingLocation) {
  751.         selectedStore = store
  752.     }
  753.  
  754.     fun editShippingDetails() {
  755.         handleEvent(CheckoutEvent.GoBackToShipping)
  756.     }
  757.  
  758.     fun setSelectedDeliverySlot(time: AppDeliveryInterval?, day: AppDeliveryDay?) {
  759.         if (time != null && day != null) {
  760.             selectedDeliveryInterval = time
  761.             selectedDeliveryDay = day
  762.             selectedDeliveryTime = DeliveryTime.SCHEDULE
  763.             setDeliveryTimeUseCase.execute(
  764.                 selectedDeliveryDay!!.date, selectedDeliveryInterval!!.id
  765.             ).compose(getIOTransformer<Completable>()).doOnSubscribe {
  766.                 handleEvent(
  767.                     CheckoutEvent.OnDeliveryTimeSelected(
  768.                         DeliveryTime.SCHEDULE,
  769.                         selectedDeliveryInterval = selectedDeliveryInterval!!,
  770.                         selectedDeliveryDay = selectedDeliveryDay!!,
  771.                         blockPlaceOrder = true
  772.                     )
  773.                 )
  774.             }.subscribe({
  775.                 handleEvent(
  776.                     CheckoutEvent.OnDeliveryTimeSelected(
  777.                         DeliveryTime.SCHEDULE,
  778.                         selectedDeliveryInterval = selectedDeliveryInterval!!,
  779.                         selectedDeliveryDay = selectedDeliveryDay!!,
  780.                         blockPlaceOrder = false
  781.                     )
  782.                 )
  783.             }, NetworkErrorHandlingImpl {
  784.                 handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
  785.             }).addTo(compositeDisposable)
  786.         }
  787.     }
  788.  
  789.     fun toggleDeliverySection(expanded: Boolean) {
  790.         handleEvent(CheckoutEvent.OnToggleDeliveryExpanded(expanded))
  791.     }
  792.  
  793.     fun togglePaymentSection(expanded: Boolean) {
  794.         handleEvent(CheckoutEvent.OnTogglePaymentExpanded(expanded))
  795.     }
  796.  
  797.     private fun isAddressSupportedByStoreCode(
  798.         address: AppShippingAddress, storeCode: String, regions: List<AppRegion>
  799.     ) =
  800.         regions.find { it.id == address.region?.id }?.cities?.find { it.id == address.city?.id }?.districts?.find { it.id == address.district?.id }?.store?.storeCode == storeCode
  801.  
  802.     fun refreshScreen() {
  803.         if (_stateLiveData.value == CheckoutState.LoadingCheckoutData) return else loadShippingDataForDelivery()
  804.     }
  805.  
  806.     fun applyLoyaltyPoints(points: Int) {
  807.         handleEvent(CheckoutEvent.OnApplyLoyaltyPoints)
  808.         applyLoyaltyPointsUseCase.execute(points)
  809.             .flatMap({ userRepository.getUserFromNetwork() }, { cartPrices, user ->
  810.                 Pair(cartPrices, user)
  811.             }).compose(getIOTransformer()).subscribe({
  812.                 handleEvent(
  813.                     CheckoutEvent.OnApplyLoyaltyPointsSuccessfully(it.first, it.second)
  814.                 )
  815.             }, NetworkErrorHandlingImpl {
  816.                 handleEvent(CheckoutEvent.OnApplyLoyaltyPointsFailed(it))
  817.             }).addTo(compositeDisposable)
  818.     }
  819.  
  820.     private fun getMinimumOrderAmount(): Int = storeConfigRepo.getMinimumOrderAmount()
  821.  
  822.     override fun onCancel(
  823.         requestParamsMap: MutableMap<String, Any>?,
  824.         responseMap: MutableMap<String, Any>?
  825.     ) {
  826.         loggingService.d("Cancelled ", responseMap.toString());
  827.         loggingService.d("FortSDK failure", responseMap.toString())
  828.         handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(null)))
  829.     }
  830.  
  831.     override fun onSuccess(
  832.         requestParamsMap: MutableMap<String, Any>?,
  833.         fortResponseMap: MutableMap<String, Any>?
  834.     ) {
  835.         loggingService.i("Success ", fortResponseMap.toString())
  836.         //VERY IMPORTANT TO CLEAR CART FROM ANY CACHED COUNTS
  837.         clearCart()
  838.         handleEvent(
  839.             CheckoutEvent.OnOrderPlacedSuccessfully(
  840.                 (fortResponseMap?.get("merchant_reference") as String).split("_")
  841.             )
  842.         )
  843.     }
  844.  
  845.     override fun onFailure(
  846.         requestParamsMap: MutableMap<String, Any>?,
  847.         fortResponseMap: MutableMap<String, Any>?
  848.     ) {
  849.         loggingService.e("Failure ", fortResponseMap.toString())
  850.         loggingService.d("FortSDK failure", fortResponseMap.toString())
  851.         handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(null)))
  852.     }
  853.  
  854.     fun onFailedToInitializePayFortSdk(exception: Exception) {
  855.         loggingService.e("Checkout Fragment", "PayFort Exception during payment")
  856.         loggingService.stackTrack(exception)
  857.         handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(exception.message)))
  858.     }
  859.  
  860.     //PAYFORT SDK REQUEST
  861.     private fun collectRequestMap(
  862.         sdkToken: String,
  863.         merchantRef: String,
  864.         email: String, total: Float
  865.     ): Map<String, Any> {
  866.         val requestMap: MutableMap<String, Any> = HashMap()
  867.         requestMap["command"] = "AUTHORIZATION"
  868.         requestMap["customer_email"] = email
  869.         requestMap["currency"] = "EGP"
  870.         requestMap["amount"] = (total * 100).toInt().toString()
  871.         requestMap["language"] = LocaleManager.getLocale()
  872. //        requestMap["return_url"] = payfortSignatureMutation.return_url!!
  873.         requestMap["merchant_reference"] = merchantRef
  874. //        requestMap["merchant_identifier"] = payfortSignatureMutation.merchant_identifier!!
  875. //        requestMap["signature"] = payfortSignatureMutation.signature!!
  876.         requestMap["sdk_token"] = sdkToken
  877. //        requestMap["access_code"] = payfortSignatureMutation.access_code!!
  878.         return requestMap
  879.     }
  880.  
  881. }
  882.