Skip to content

Commit

Permalink
Merge pull request #8206 from woocommerce/feature/8182-iap-single-sub…
Browse files Browse the repository at this point in the history
…cription-requirement

Feature/8182 iap single subcription requirement
  • Loading branch information
JorgeMucientes authored Feb 1, 2023
2 parents 596a6e5 + 50e07db commit ab19239
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 71 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ import com.woocommerce.android.R
import com.woocommerce.android.iap.pub.IAPActivityWrapper
import com.woocommerce.android.iap.pub.IAPSitePurchasePlanFactory
import com.woocommerce.android.iapshowcase.IAPDebugLogWrapper
import com.woocommerce.android.ui.login.storecreation.iap.IapMobilePayApiProvider
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class IAPShowcasePurchaseFragment : Fragment(R.layout.fragment_iap_showcase_purchase) {
@Inject
lateinit var mobilePayAPIProvider: IAPShowcaseMobilePayAPIProvider
lateinit var mobilePayAPIProvider: IapMobilePayApiProvider

@Inject
lateinit var debugLogWrapper: IAPDebugLogWrapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.woocommerce.android.extensions.navigateSafely
import com.woocommerce.android.extensions.navigateToHelpScreen
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
import com.woocommerce.android.ui.login.storecreation.iap.IapEligibilityViewModel.IapEligibilityEvent.NavigateToNextStep
Expand Down Expand Up @@ -49,6 +50,10 @@ class CheckIapEligibilityFragment : BaseFragment() {
is NavigateToNextStep -> navigateToStoreCreationNative()
is Exit -> findNavController().popBackStack()
is MultiLiveEvent.Event.ShowDialog -> event.showDialog()
is MultiLiveEvent.Event.NavigateToHelpScreen -> {
findNavController().popBackStack()
navigateToHelpScreen(event.origin)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package com.woocommerce.android.ui.login.storecreation.iap

import android.content.DialogInterface
import androidx.annotation.StringRes
import androidx.lifecycle.LiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.asLiveData
import com.woocommerce.android.R
import com.woocommerce.android.analytics.AnalyticsEvent
import com.woocommerce.android.analytics.AnalyticsTracker
import com.woocommerce.android.analytics.AnalyticsTrackerWrapper
import com.woocommerce.android.iap.pub.PurchaseWPComPlanActions
import com.woocommerce.android.iap.pub.PurchaseWpComPlanSupportChecker
import com.woocommerce.android.iap.pub.model.IAPError
import com.woocommerce.android.iap.pub.model.IAPSupportedResult
import com.woocommerce.android.iap.pub.model.PurchaseStatus
import com.woocommerce.android.iap.pub.model.WPComIsPurchasedResult
import com.woocommerce.android.support.help.HelpOrigin.STORE_CREATION
import com.woocommerce.android.ui.login.storecreation.iap.IapEligibilityViewModel.IapEligibilityEvent.NavigateToNextStep
import com.woocommerce.android.viewmodel.MultiLiveEvent
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.NavigateToHelpScreen
import com.woocommerce.android.viewmodel.ScopedViewModel
import com.woocommerce.android.viewmodel.getStateFlow
import dagger.hilt.android.lifecycle.HiltViewModel
Expand All @@ -22,7 +30,8 @@ class IapEligibilityViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val planSupportChecker: PurchaseWpComPlanSupportChecker,
private val analyticsTrackerWrapper: AnalyticsTrackerWrapper,
private val isIAPEnabled: IsIAPEnabled
private val isIAPEnabled: IsIAPEnabled,
private val iapManager: PurchaseWPComPlanActions
) : ScopedViewModel(savedStateHandle, planSupportChecker) {
private val _isCheckingIapEligibility = savedState.getStateFlow(scope = this, initialValue = true)
val isCheckingIapEligibility: LiveData<Boolean> = _isCheckingIapEligibility.asLiveData()
Expand All @@ -32,33 +41,46 @@ class IapEligibilityViewModel @Inject constructor(
launch {
when (val result = planSupportChecker.isIAPSupported()) {
is IAPSupportedResult.Success -> onSuccess(result)
is IAPSupportedResult.Error -> onError(result)
is IAPSupportedResult.Error -> onIAPError(result.errorType)
}
}
} else {
triggerEvent(NavigateToNextStep)
}
}

private fun onError(result: IAPSupportedResult.Error) {
analyticsTrackerWrapper.track(
AnalyticsEvent.SITE_CREATION_IAP_ELIGIBILITY_ERROR,
mapOf(AnalyticsTracker.KEY_ERROR_TYPE to result.errorType.toString())
private fun onUserNotEligibleForIAP(
@StringRes title: Int = R.string.store_creation_iap_eligibility_check_error_title,
@StringRes message: Int,
@StringRes positiveButtonId: Int? = null,
positiveBtnAction: DialogInterface.OnClickListener? = null,
) {
_isCheckingIapEligibility.value = false
triggerDialogError(
title = title,
message = message,
positiveButtonId = positiveButtonId,
positiveBtnAction = positiveBtnAction
)
onUserNotEligibleForIAP()
}

private fun onUserNotEligibleForIAP() {
_isCheckingIapEligibility.value = false
private fun triggerDialogError(
@StringRes title: Int,
@StringRes message: Int,
@StringRes positiveButtonId: Int? = null,
positiveBtnAction: DialogInterface.OnClickListener? = null,
) {
triggerEvent(
MultiLiveEvent.Event.ShowDialog(
titleId = R.string.store_creation_iap_eligibility_check_error_title,
messageId = R.string.store_creation_iap_eligibility_check_error_description,
titleId = title,
messageId = message,
positiveButtonId = positiveButtonId,
positiveBtnAction = positiveBtnAction,
negativeBtnAction = { dialog, _ ->
triggerEvent(MultiLiveEvent.Event.Exit)
dialog.dismiss()
},
negativeButtonId = R.string.close
negativeButtonId = R.string.close,
)
)
}
Expand All @@ -69,9 +91,60 @@ class IapEligibilityViewModel @Inject constructor(
mapOf(AnalyticsTracker.KEY_IAP_ELIGIBLE to result.isSupported)
)
when {
result.isSupported -> triggerEvent(NavigateToNextStep)
else -> onUserNotEligibleForIAP()
result.isSupported -> checkIfWooPlanAlreadyPurchased()
else -> onUserNotEligibleForIAP(
message = R.string.store_creation_iap_eligibility_check_error_not_available_for_country
)
}
}

private fun checkIfWooPlanAlreadyPurchased() {
launch {
when (val result = iapManager.isWPComPlanPurchased()) {
is WPComIsPurchasedResult.Success -> {
when (result.purchaseStatus) {
PurchaseStatus.PURCHASED -> {
onUserNotEligibleForIAP(
message = R.string.store_creation_iap_eligibility_existing_purchase_not_acknowledged,
positiveButtonId = R.string.support_contact,
positiveBtnAction = { dialog, _ ->
triggerEvent(NavigateToHelpScreen(STORE_CREATION))
dialog.dismiss()
}
)
}
PurchaseStatus.PURCHASED_AND_ACKNOWLEDGED -> onUserNotEligibleForIAP(
message = R.string.store_creation_iap_eligibility_check_error_existing_subscription
)
PurchaseStatus.NOT_PURCHASED -> triggerEvent(NavigateToNextStep)
}
}
is WPComIsPurchasedResult.Error -> onIAPError(result.errorType)
}
}
}

private fun onIAPError(error: IAPError) {
analyticsTrackerWrapper.track(
AnalyticsEvent.SITE_CREATION_IAP_ELIGIBILITY_ERROR,
mapOf(AnalyticsTracker.KEY_ERROR_TYPE to error.toString())
)
val message = when (error) {
is IAPError.Billing.DeveloperError,
is IAPError.Billing.ServiceDisconnected,
is IAPError.Billing.FeatureNotSupported,
is IAPError.Billing.BillingUnavailable,
is IAPError.Billing.Unknown,
is IAPError.Billing.ItemUnavailable,
is IAPError.Billing.ServiceTimeout,
is IAPError.Billing.ServiceUnavailable,
is IAPError.Billing.ItemNotOwned,
is IAPError.Billing.UserCancelled -> R.string.store_creation_iap_eligibility_check_generic_error
is IAPError.Billing.ItemAlreadyOwned ->
R.string.store_creation_iap_eligibility_check_error_existing_subscription
is IAPError.RemoteCommunication -> R.string.store_creation_iap_eligibility_network_error
}
onUserNotEligibleForIAP(message = message)
}

sealed class IapEligibilityEvent : MultiLiveEvent.Event() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ import org.wordpress.android.fluxc.network.rest.wpcom.mobilepay.MobilePayRestCli
import org.wordpress.android.fluxc.store.MobilePayStore
import javax.inject.Inject

/**
* This class provides a default implementation of IAPMobilePayAPI for store creation flow. It duplicates
* code from IAPShowcaseMobilePayAPIProvider. We should double check if we need this duplication to remove
* one of the files and move the other to the correct package.
* Github issue: https://github.com/woocommerce/woocommerce-android/issues/8148
*/
class IapMobilePayApiProvider @Inject constructor(private val mobilePayStore: MobilePayStore) {
fun buildMobilePayAPI(customUrl: String?) = object : IAPMobilePayAPI {
override suspend fun createAndConfirmOrder(
Expand Down Expand Up @@ -40,10 +34,10 @@ class IapMobilePayApiProvider @Inject constructor(private val mobilePayStore: Mo
MobilePayRestClient.CreateOrderErrorType.API_ERROR,
MobilePayRestClient.CreateOrderErrorType.AUTH_ERROR,
MobilePayRestClient.CreateOrderErrorType.GENERIC_ERROR,
MobilePayRestClient.CreateOrderErrorType.NETWORK_ERROR,
MobilePayRestClient.CreateOrderErrorType.INVALID_RESPONSE ->
CreateAndConfirmOrderResponse.Server(response.message ?: "Reason is not provided")
MobilePayRestClient.CreateOrderErrorType.TIMEOUT -> CreateAndConfirmOrderResponse.Network
MobilePayRestClient.CreateOrderErrorType.TIMEOUT,
MobilePayRestClient.CreateOrderErrorType.NETWORK_ERROR -> CreateAndConfirmOrderResponse.Network
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion WooCommerce/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2817,7 +2817,11 @@
<string name="store_creation_iap_eligibility_loading_title">We are getting ready for your store creation</string>
<string name="store_creation_iap_eligibility_loading_subtitle">Please remain connected.</string>
<string name="store_creation_iap_eligibility_check_error_title">Store Creation Unavailable </string>
<string name="store_creation_iap_eligibility_check_error_description">Store creation from the mobile app is currently unavailable for your country.</string>
<string name="store_creation_iap_eligibility_check_error_not_available_for_country">Store creation from the mobile app is currently unavailable for your country.</string>
<string name="store_creation_iap_eligibility_check_error_existing_subscription">You can only purchase the eCommerce plan from the mobile app once. To create additional sites visit our website.</string>
<string name="store_creation_iap_eligibility_check_generic_error">Store creation is currently unavailable. Please, try again later.</string>
<string name="store_creation_iap_eligibility_network_error">A network error occurred. Please check your connection and try again.</string>
<string name="store_creation_iap_eligibility_existing_purchase_not_acknowledged">You already have an active eCommerce subscription, please contact support if you are experiencing issues with your current subscription not linked to your new site.</string>
<string name="store_creation_create_new_store_label">Create a new store</string>
<string name="store_creation_cannot_load_store">Couldn\'t load your store.\nPlease try again…</string>
<string name="store_creation_in_progress">Creating your store…</string>
Expand Down
Loading

0 comments on commit ab19239

Please sign in to comment.