Skip to content
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

Feature/iap payment flow #8053

Merged
merged 38 commits into from
Dec 30, 2022
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
34e62d2
Force native flow for testing purposes
JorgeMucientes Dec 15, 2022
f7211ee
Add plan purchase using IAP
JorgeMucientes Dec 16, 2022
981c3dd
Add plan purchase using IAP
JorgeMucientes Dec 16, 2022
14b826e
Launch only native flow when IAP enabled
JorgeMucientes Dec 16, 2022
3a9bba9
Add circular progress inside button
JorgeMucientes Dec 16, 2022
f270124
Provide siteId to PurchaseWPComPlanActions when needed
JorgeMucientes Dec 16, 2022
04b07b4
Scope IAP collaborators to view model scope
JorgeMucientes Dec 20, 2022
1356cad
Scope IAP collaborators to view model scope
JorgeMucientes Dec 20, 2022
5310fa4
Save siteId after site is created
JorgeMucientes Dec 20, 2022
7bacc17
Pass closable to parent ViewModel
JorgeMucientes Dec 20, 2022
2c02f89
Keep primary button visible always
JorgeMucientes Dec 20, 2022
548ad36
Merge branch 'feature/check-iap-eligibility' into feature/iap-payment…
JorgeMucientes Dec 21, 2022
5188002
Update plan with IAP product info when enabled
JorgeMucientes Dec 21, 2022
992b175
Add use case around feature flag to enable testing
JorgeMucientes Dec 21, 2022
04e6e3a
Return price value converted
JorgeMucientes Dec 21, 2022
d209ce9
Fix wrong type
JorgeMucientes Dec 21, 2022
5bdd0cd
Replace usages of feature flag to check IAP availability
JorgeMucientes Dec 21, 2022
d343067
Remove currency formatter as it crashes
JorgeMucientes Dec 21, 2022
ca18516
Format currency based on device settings
JorgeMucientes Dec 21, 2022
2be79bd
Fix and add new unit tests to PlansViewModel
JorgeMucientes Dec 21, 2022
7913c0e
Merge branch 'feature/check-iap-eligibility' into feature/iap-payment…
JorgeMucientes Dec 22, 2022
f66d3cd
Replace usage of feature flag with use case
JorgeMucientes Dec 22, 2022
c41de29
Move IsIAPEnabled use case to correct package
JorgeMucientes Dec 22, 2022
94e929a
Add unit test for IAP eligibility view model
JorgeMucientes Dec 22, 2022
f83cfde
Add tracking for purchase flow and rename tracking for iap eligibilit…
JorgeMucientes Dec 22, 2022
c1d8b21
Fix warnings with R class imports
JorgeMucientes Dec 22, 2022
35c9036
Merge branch 'feature/check-iap-eligibility' into feature/iap-payment…
JorgeMucientes Dec 23, 2022
1d9423c
Merge branch 'trunk' into feature/iap-payment-flow
kidinov Dec 26, 2022
9c68696
Fix IAP module functional tests
kidinov Dec 26, 2022
0c25e8b
Avoid leaking iapActivityWrapper
JorgeMucientes Dec 26, 2022
9b12b91
Avoid subscribing multiple times to iap purchase flow
JorgeMucientes Dec 26, 2022
1ce9e8d
Properly track error types for IAP purchase flow
JorgeMucientes Dec 26, 2022
c4adf10
Add currency formatter independent from site settings
JorgeMucientes Dec 26, 2022
48a452b
Remove unneeded param
JorgeMucientes Dec 26, 2022
5bdd124
Track IAP error reason if any
JorgeMucientes Dec 26, 2022
24cd3d4
Fix import ordering
JorgeMucientes Dec 26, 2022
e4fede2
Remove unneeded log
JorgeMucientes Dec 27, 2022
a5751a8
Pass activity wrapper to view model when purchase button is clicked
JorgeMucientes Dec 28, 2022
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 @@ -20,9 +20,6 @@ import com.woocommerce.android.iapshowcase.IAPDebugLogWrapper
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

private const val REMOTE_SITE_ID = 1L
private const val MILLION = 1_000_000.0

@AndroidEntryPoint
class IAPShowcasePurchaseFragment : Fragment(R.layout.fragment_iap_showcase_purchase) {
@Inject
Expand All @@ -38,7 +35,6 @@ class IAPShowcasePurchaseFragment : Fragment(R.layout.fragment_iap_showcase_purc
return IAPShowcasePurchaseViewModel(
IAPSitePurchasePlanFactory.createIAPSitePurchasePlan(
[email protected]().application,
REMOTE_SITE_ID,
debugLogWrapper,
mobilePayAPIProvider::buildMobilePayAPI,
)
Expand Down Expand Up @@ -74,7 +70,7 @@ class IAPShowcasePurchaseFragment : Fragment(R.layout.fragment_iap_showcase_purc
viewModel.productInfo.observe(viewLifecycleOwner) {
view.findViewById<TextView>(R.id.tvProductInfoTitle).text = it.localizedTitle
view.findViewById<TextView>(R.id.tvProductInfoDescription).text = it.localizedDescription
view.findViewById<TextView>(R.id.tvProductInfoPrice).text = "${it.price / MILLION} ${it.currency}"
view.findViewById<TextView>(R.id.tvProductInfoPrice).text = "${it.price} ${it.currency}"
}
viewModel.iapEvent.observe(viewLifecycleOwner) {
Log.w("IAP_SHOWCASE", it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

class IAPShowcasePurchaseViewModel(private val iapManager: PurchaseWPComPlanActions) : ViewModel(iapManager) {
private companion object {
const val REMOTE_SITE_ID = 1L
}

private val _productInfo = MutableLiveData<WPComPlanProduct>()
val productInfo: LiveData<WPComPlanProduct> = _productInfo

Expand All @@ -29,7 +33,7 @@ class IAPShowcasePurchaseViewModel(private val iapManager: PurchaseWPComPlanActi

init {
viewModelScope.launch {
iapManager.purchaseWpComPlanResult.collectLatest { result ->
iapManager.getPurchaseWpComPlanResult(REMOTE_SITE_ID).collectLatest { result ->
_iapLoading.value = false
when (result) {
is WPComPurchaseResult.Success -> _iapEvent.value = "Plan has been successfully purchased"
Expand All @@ -42,7 +46,7 @@ class IAPShowcasePurchaseViewModel(private val iapManager: PurchaseWPComPlanActi
fun purchasePlan(activityWrapper: IAPActivityWrapper) {
viewModelScope.launch {
_iapLoading.value = true
iapManager.purchaseWPComPlan(activityWrapper)
iapManager.purchaseWPComPlan(activityWrapper, REMOTE_SITE_ID)
}
}

Expand All @@ -60,6 +64,7 @@ class IAPShowcasePurchaseViewModel(private val iapManager: PurchaseWPComPlanActi
PurchaseStatus.NOT_PURCHASED -> "Plan hasn't been purchased yet"
}
}

is WPComIsPurchasedResult.Error -> handleError(response.errorType)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,9 @@ enum class AnalyticsEvent(val siteless: Boolean = false) {
SITE_CREATION_STORE_MANAGEMENT_OPENED,
SITE_CREATION_STEP(siteless = true),
SITE_CREATION_IAP_ELIGIBILITY(siteless = true),
SITE_CREATION_IAP_ERROR(siteless = true),
SITE_CREATION_IAP_ELIGIBILITY_ERROR(siteless = true),
SITE_CREATION_IAP_PURCHASE_SUCCESS(siteless = true),
SITE_CREATION_IAP_PURCHASE_ERROR(siteless = true),

APPLICATION_PASSWORDS_NEW_PASSWORD_CREATED,
APPLICATION_PASSWORDS_TEST_INITIATED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,25 @@ import com.woocommerce.android.ui.login.storecreation.iap.WooIapLogWrapper
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped

@InstallIn(SingletonComponent::class)
@InstallIn(ViewModelComponent::class)
@Module
class InAppPurchasesModule {
@ViewModelScoped
@Provides
fun providePurchaseWPComPlanActions(
context: Application,
mobilePayAPIProvider: IAPShowcaseMobilePayAPIProvider
): PurchaseWPComPlanActions =
IAPSitePurchasePlanFactory.createIAPSitePurchasePlan(
context,
1L,
WooIapLogWrapper(),
mobilePayAPIProvider::buildMobilePayAPI
)

@ViewModelScoped
@Provides
fun providePurchaseWpComPlanSupportChecker(application: Application): PurchaseWpComPlanSupportChecker =
IAPSitePurchasePlanFactory.createIAPPurchaseWpComPlanSupportChecker(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import com.woocommerce.android.analytics.AnalyticsTrackerWrapper
import com.woocommerce.android.iap.pub.PurchaseWpComPlanSupportChecker
import com.woocommerce.android.iap.pub.model.IAPSupportedResult
import com.woocommerce.android.ui.login.storecreation.iap.IapEligibilityViewModel.IapEligibilityEvent.NavigateToNextStep
import com.woocommerce.android.util.FeatureFlag
import com.woocommerce.android.viewmodel.MultiLiveEvent
import com.woocommerce.android.viewmodel.ScopedViewModel
import com.woocommerce.android.viewmodel.getStateFlow
Expand All @@ -22,13 +21,14 @@ import javax.inject.Inject
class IapEligibilityViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val planSupportChecker: PurchaseWpComPlanSupportChecker,
private val analyticsTrackerWrapper: AnalyticsTrackerWrapper
) : ScopedViewModel(savedStateHandle) {
private val analyticsTrackerWrapper: AnalyticsTrackerWrapper,
private val isIAPEnabled: IsIAPEnabled
) : ScopedViewModel(savedStateHandle, planSupportChecker) {
private val _isCheckingIapEligibility = savedState.getStateFlow(scope = this, initialValue = true)
val isCheckingIapEligibility: LiveData<Boolean> = _isCheckingIapEligibility.asLiveData()

fun checkIapEligibility() {
if (FeatureFlag.IAP_FOR_STORE_CREATION.isEnabled()) {
if (isIAPEnabled()) {
launch {
when (val result = planSupportChecker.isIAPSupported()) {
is IAPSupportedResult.Success -> onSuccess(result)
Expand All @@ -42,7 +42,7 @@ class IapEligibilityViewModel @Inject constructor(

private fun onError(result: IAPSupportedResult.Error) {
analyticsTrackerWrapper.track(
AnalyticsEvent.SITE_CREATION_IAP_ERROR,
AnalyticsEvent.SITE_CREATION_IAP_ELIGIBILITY_ERROR,
mapOf(AnalyticsTracker.KEY_ERROR_TYPE to result.errorType.toString())
)
onUserNotEligibleForIAP()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.woocommerce.android.ui.login.storecreation.iap

import com.woocommerce.android.util.FeatureFlag
import javax.inject.Inject

class IsIAPEnabled @Inject constructor() {
operator fun invoke(): Boolean = FeatureFlag.IAP_FOR_STORE_CREATION.isEnabled()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.ui.platform.ComposeView
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.iap.pub.IAPActivityWrapper
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.common.wpcomwebview.WPComWebViewAuthenticator
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
Expand Down Expand Up @@ -42,6 +44,7 @@ class PlansFragment : BaseFragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.setIAPActivityWrapper(IAPActivityWrapper(requireActivity() as AppCompatActivity))
setupObservers()
}

Expand Down
Loading