mirror of
https://github.com/ProtonMail/protoncore_android.git
synced 2026-06-14 09:54:49 +00:00
Clear consumed Payment Token (HumanVerification token).
This commit is contained in:
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixes
|
||||
|
||||
- Clear consumed Payment Token (HumanVerification token).
|
||||
|
||||
## [7.1.11]
|
||||
|
||||
### Changes
|
||||
|
||||
@@ -2152,11 +2152,11 @@ public final class me/proton/core/auth/presentation/viewmodel/signup/RecoverySMS
|
||||
}
|
||||
|
||||
public final class me/proton/core/auth/presentation/viewmodel/signup/SignupViewModel_Factory : dagger/internal/Factory {
|
||||
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
|
||||
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lme/proton/core/auth/presentation/viewmodel/signup/SignupViewModel_Factory;
|
||||
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
|
||||
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lme/proton/core/auth/presentation/viewmodel/signup/SignupViewModel_Factory;
|
||||
public synthetic fun get ()Ljava/lang/Object;
|
||||
public fun get ()Lme/proton/core/auth/presentation/viewmodel/signup/SignupViewModel;
|
||||
public static fun newInstance (Lme/proton/core/auth/domain/usecase/signup/PerformCreateUser;Lme/proton/core/auth/domain/usecase/signup/PerformCreateExternalEmailUser;Lme/proton/core/crypto/common/keystore/KeyStoreCrypto;Lme/proton/core/plan/presentation/PlansOrchestrator;Lme/proton/core/payment/presentation/PaymentsOrchestrator;Lme/proton/core/network/domain/client/ClientIdProvider;Lme/proton/core/humanverification/domain/HumanVerificationManager;Lme/proton/core/auth/domain/usecase/PerformLogin;Lme/proton/core/challenge/domain/ChallengeManager;Lme/proton/core/auth/domain/usecase/signup/SignupChallengeConfig;Lme/proton/core/humanverification/presentation/HumanVerificationOrchestrator;Landroidx/lifecycle/SavedStateHandle;)Lme/proton/core/auth/presentation/viewmodel/signup/SignupViewModel;
|
||||
public static fun newInstance (Lme/proton/core/auth/domain/usecase/signup/PerformCreateUser;Lme/proton/core/auth/domain/usecase/signup/PerformCreateExternalEmailUser;Lme/proton/core/crypto/common/keystore/KeyStoreCrypto;Lme/proton/core/plan/presentation/PlansOrchestrator;Lme/proton/core/payment/presentation/PaymentsOrchestrator;Lme/proton/core/humanverification/domain/HumanVerificationManager;Lme/proton/core/auth/domain/usecase/PerformLogin;Lme/proton/core/challenge/domain/ChallengeManager;Lme/proton/core/auth/domain/usecase/signup/SignupChallengeConfig;Lme/proton/core/humanverification/presentation/HumanVerificationOrchestrator;Landroidx/lifecycle/SavedStateHandle;)Lme/proton/core/auth/presentation/viewmodel/signup/SignupViewModel;
|
||||
}
|
||||
|
||||
public final class me/proton/core/auth/presentation/viewmodel/signup/SignupViewModel_HiltModules {
|
||||
|
||||
+9
-12
@@ -37,6 +37,7 @@ import me.proton.core.auth.presentation.R
|
||||
import me.proton.core.auth.presentation.databinding.ActivitySignupBinding
|
||||
import me.proton.core.auth.presentation.entity.signup.SignUpInput
|
||||
import me.proton.core.auth.presentation.entity.signup.SignUpResult
|
||||
import me.proton.core.auth.presentation.entity.signup.SubscriptionDetails
|
||||
import me.proton.core.auth.presentation.ui.AuthActivity
|
||||
import me.proton.core.auth.presentation.ui.removeCreatingUser
|
||||
import me.proton.core.auth.presentation.ui.showCongrats
|
||||
@@ -122,10 +123,16 @@ class SignupActivity : AuthActivity<ActivitySignupBinding>(ActivitySignupBinding
|
||||
KEY_PLAN_SELECTED, this
|
||||
) { _, bundle ->
|
||||
val plan = bundle.getParcelable<SelectedPlan>(BUNDLE_KEY_PLAN)
|
||||
val billing = bundle.getParcelable<BillingResult>(BUNDLE_KEY_BILLING_DETAILS)
|
||||
val billingResult = bundle.getParcelable<BillingResult>(BUNDLE_KEY_BILLING_DETAILS)
|
||||
if (plan != null) {
|
||||
signUpViewModel.subscriptionDetails = SubscriptionDetails(
|
||||
billingResult = billingResult,
|
||||
planName = plan.planName,
|
||||
planDisplayName = plan.planDisplayName,
|
||||
cycle = plan.cycle.toSubscriptionCycle()
|
||||
)
|
||||
signUpViewModel.startCreateUserWorkflow()
|
||||
supportFragmentManager.showCreatingUser()
|
||||
onPlanSelected(plan, billing)
|
||||
} else {
|
||||
supportFragmentManager.removePlansSignup()
|
||||
signUpViewModel.onPlanChooserCancel()
|
||||
@@ -138,7 +145,6 @@ class SignupActivity : AuthActivity<ActivitySignupBinding>(ActivitySignupBinding
|
||||
is PostLoginAccountSetup.Result.Error.UnlockPrimaryKeyError -> onUnlockUserError(result.error)
|
||||
is PostLoginAccountSetup.Result.Error.UserCheckError -> onLoginError(result.error.localizedMessage)
|
||||
is PostLoginAccountSetup.Result.UserUnlocked -> onLoginSuccess(result.userId)
|
||||
|
||||
is PostLoginAccountSetup.Result.Need.ChangePassword,
|
||||
is PostLoginAccountSetup.Result.Need.ChooseUsername,
|
||||
is PostLoginAccountSetup.Result.Need.SecondFactor,
|
||||
@@ -146,15 +152,6 @@ class SignupActivity : AuthActivity<ActivitySignupBinding>(ActivitySignupBinding
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun onPlanSelected(plan: SelectedPlan, billingResult: BillingResult?) {
|
||||
if (billingResult == null) {
|
||||
signUpViewModel.startCreateUserWorkflow()
|
||||
} else {
|
||||
val cycle = plan.cycle.toSubscriptionCycle()
|
||||
signUpViewModel.startCreatePaidUserWorkflow(plan.planName, plan.planDisplayName, cycle, billingResult)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSignUpSuccess(loginUsername: String, encryptedPassword: EncryptedString) {
|
||||
with(supportFragmentManager) {
|
||||
for (i in 0..backStackEntryCount) {
|
||||
|
||||
-31
@@ -49,10 +49,7 @@ import me.proton.core.crypto.common.keystore.encrypt
|
||||
import me.proton.core.humanverification.domain.HumanVerificationManager
|
||||
import me.proton.core.humanverification.presentation.HumanVerificationOrchestrator
|
||||
import me.proton.core.humanverification.presentation.onHumanVerificationFailed
|
||||
import me.proton.core.network.domain.client.ClientIdProvider
|
||||
import me.proton.core.payment.domain.entity.SubscriptionCycle
|
||||
import me.proton.core.payment.presentation.PaymentsOrchestrator
|
||||
import me.proton.core.payment.presentation.entity.BillingResult
|
||||
import me.proton.core.plan.presentation.PlansOrchestrator
|
||||
import me.proton.core.presentation.savedstate.flowState
|
||||
import me.proton.core.presentation.savedstate.state
|
||||
@@ -68,7 +65,6 @@ internal class SignupViewModel @Inject constructor(
|
||||
private val keyStoreCrypto: KeyStoreCrypto,
|
||||
private val plansOrchestrator: PlansOrchestrator,
|
||||
private val paymentsOrchestrator: PaymentsOrchestrator,
|
||||
private val clientIdProvider: ClientIdProvider,
|
||||
private val humanVerificationManager: HumanVerificationManager,
|
||||
private val performLogin: PerformLogin,
|
||||
private val challengeManager: ChallengeManager,
|
||||
@@ -200,33 +196,6 @@ internal class SignupViewModel @Inject constructor(
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
fun startCreatePaidUserWorkflow(
|
||||
planName: String,
|
||||
planDisplayName: String,
|
||||
cycle: SubscriptionCycle,
|
||||
billingResult: BillingResult
|
||||
) = viewModelScope.launch {
|
||||
val clientId = requireNotNull(clientIdProvider.getClientId(sessionId = null))
|
||||
subscriptionDetails = SubscriptionDetails(
|
||||
billingResult = billingResult,
|
||||
planName = planName,
|
||||
planDisplayName = planDisplayName,
|
||||
cycle = cycle
|
||||
)
|
||||
if (billingResult.paySuccess) {
|
||||
viewModelScope.launch {
|
||||
// update subscription details
|
||||
humanVerificationManager.addDetails(
|
||||
details = BillingResult.paymentDetails(
|
||||
clientId = clientId,
|
||||
token = billingResult.token!!
|
||||
)
|
||||
)
|
||||
}
|
||||
startCreateUserWorkflow()
|
||||
}
|
||||
}
|
||||
|
||||
override fun register(context: FragmentActivity) {
|
||||
super.register(context)
|
||||
plansOrchestrator.register(context)
|
||||
|
||||
-1
@@ -119,7 +119,6 @@ class SignupViewModelTest : ArchTest, CoroutinesTest {
|
||||
keyStoreCrypto,
|
||||
plansOrchestrator,
|
||||
paymentsOrchestrator,
|
||||
clientIdProvider,
|
||||
humanVerificationManager,
|
||||
performLogin,
|
||||
challengeManager,
|
||||
|
||||
@@ -17,6 +17,7 @@ public final class me/proton/core/humanverification/data/HumanVerificationManage
|
||||
public fun <init> (Lme/proton/core/network/domain/humanverification/HumanVerificationProvider;Lme/proton/core/network/domain/humanverification/HumanVerificationListener;Lme/proton/core/humanverification/domain/repository/HumanVerificationRepository;)V
|
||||
public fun addDetails (Lme/proton/core/network/domain/humanverification/HumanVerificationDetails;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun awaitHumanVerificationProcessFinished (Lme/proton/core/network/domain/client/ClientId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun clearDetails (Lme/proton/core/network/domain/client/ClientId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun getHumanVerificationDetails (Lme/proton/core/network/domain/client/ClientId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun handleHumanVerificationFailed (Lme/proton/core/network/domain/client/ClientId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun handleHumanVerificationSuccess (Lme/proton/core/network/domain/client/ClientId;Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
@@ -187,6 +188,7 @@ public final class me/proton/core/humanverification/data/entity/HumanVerificatio
|
||||
|
||||
public final class me/proton/core/humanverification/data/repository/HumanVerificationRepositoryImpl : me/proton/core/humanverification/domain/repository/HumanVerificationRepository {
|
||||
public fun <init> (Lme/proton/core/humanverification/data/db/HumanVerificationDatabase;Lme/proton/core/crypto/common/keystore/KeyStoreCrypto;)V
|
||||
public fun deleteHumanVerificationDetails (Lme/proton/core/network/domain/client/ClientId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun getAllHumanVerificationDetails (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun getHumanVerificationDetails (Lme/proton/core/network/domain/client/ClientId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun insertHumanVerificationDetails (Lme/proton/core/network/domain/humanverification/HumanVerificationDetails;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
|
||||
+6
-2
@@ -22,11 +22,11 @@ import kotlinx.coroutines.flow.Flow
|
||||
import me.proton.core.humanverification.domain.HumanVerificationManager
|
||||
import me.proton.core.humanverification.domain.HumanVerificationWorkflowHandler
|
||||
import me.proton.core.humanverification.domain.repository.HumanVerificationRepository
|
||||
import me.proton.core.network.domain.humanverification.HumanVerificationDetails
|
||||
import me.proton.core.network.domain.humanverification.HumanVerificationState
|
||||
import me.proton.core.network.domain.client.ClientId
|
||||
import me.proton.core.network.domain.humanverification.HumanVerificationDetails
|
||||
import me.proton.core.network.domain.humanverification.HumanVerificationListener
|
||||
import me.proton.core.network.domain.humanverification.HumanVerificationProvider
|
||||
import me.proton.core.network.domain.humanverification.HumanVerificationState
|
||||
|
||||
class HumanVerificationManagerImpl(
|
||||
private val humanVerificationProvider: HumanVerificationProvider,
|
||||
@@ -43,6 +43,10 @@ class HumanVerificationManagerImpl(
|
||||
humanVerificationRepository.insertHumanVerificationDetails(details = details)
|
||||
}
|
||||
|
||||
override suspend fun clearDetails(clientId: ClientId) {
|
||||
humanVerificationRepository.deleteHumanVerificationDetails(clientId)
|
||||
}
|
||||
|
||||
override suspend fun handleHumanVerificationSuccess(clientId: ClientId, tokenType: String, tokenCode: String) {
|
||||
humanVerificationRepository.updateHumanVerificationState(
|
||||
clientId = clientId,
|
||||
|
||||
+3
@@ -75,6 +75,9 @@ class HumanVerificationRepositoryImpl(
|
||||
getHumanVerificationDetails(clientId)?.let { tryEmitStateChanged(it) }
|
||||
}
|
||||
|
||||
override suspend fun deleteHumanVerificationDetails(clientId: ClientId) =
|
||||
humanVerificationDetailsDao.deleteByClientId(clientId.id)
|
||||
|
||||
override suspend fun updateHumanVerificationState(
|
||||
clientId: ClientId,
|
||||
state: HumanVerificationState,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
public abstract interface class me/proton/core/humanverification/domain/HumanVerificationManager : me/proton/core/network/domain/humanverification/HumanVerificationListener, me/proton/core/network/domain/humanverification/HumanVerificationProvider {
|
||||
public abstract fun addDetails (Lme/proton/core/network/domain/humanverification/HumanVerificationDetails;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public abstract fun clearDetails (Lme/proton/core/network/domain/client/ClientId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public abstract fun onHumanVerificationStateChanged (Z)Lkotlinx/coroutines/flow/Flow;
|
||||
}
|
||||
|
||||
@@ -34,6 +35,7 @@ public final class me/proton/core/humanverification/domain/entity/TokenType$Comp
|
||||
}
|
||||
|
||||
public abstract interface class me/proton/core/humanverification/domain/repository/HumanVerificationRepository {
|
||||
public abstract fun deleteHumanVerificationDetails (Lme/proton/core/network/domain/client/ClientId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public abstract fun getAllHumanVerificationDetails (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public abstract fun getHumanVerificationDetails (Lme/proton/core/network/domain/client/ClientId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public abstract fun insertHumanVerificationDetails (Lme/proton/core/network/domain/humanverification/HumanVerificationDetails;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
|
||||
+9
@@ -20,6 +20,7 @@ package me.proton.core.humanverification.domain
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import me.proton.core.network.domain.client.ClientId
|
||||
import me.proton.core.network.domain.humanverification.HumanVerificationDetails
|
||||
import me.proton.core.network.domain.humanverification.HumanVerificationListener
|
||||
import me.proton.core.network.domain.humanverification.HumanVerificationProvider
|
||||
@@ -34,7 +35,15 @@ interface HumanVerificationManager : HumanVerificationProvider, HumanVerificatio
|
||||
*/
|
||||
fun onHumanVerificationStateChanged(initialState: Boolean = false): Flow<HumanVerificationDetails>
|
||||
|
||||
/**
|
||||
* Add (insert or update) a [HumanVerificationDetails].
|
||||
*/
|
||||
suspend fun addDetails(details: HumanVerificationDetails)
|
||||
|
||||
/**
|
||||
* Clear all [HumanVerificationDetails], by [clientId].
|
||||
*/
|
||||
suspend fun clearDetails(clientId: ClientId)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-2
@@ -26,7 +26,7 @@ import me.proton.core.network.domain.humanverification.HumanVerificationState
|
||||
interface HumanVerificationRepository {
|
||||
|
||||
/**
|
||||
* Get [HumanVerificationDetails], if exist, by sessionId.
|
||||
* Get [HumanVerificationDetails], if exist, by [clientId].
|
||||
*/
|
||||
suspend fun getHumanVerificationDetails(clientId: ClientId): HumanVerificationDetails?
|
||||
|
||||
@@ -36,10 +36,15 @@ interface HumanVerificationRepository {
|
||||
suspend fun getAllHumanVerificationDetails(): Flow<List<HumanVerificationDetails>>
|
||||
|
||||
/**
|
||||
* Set [HumanVerificationDetails], by clientId. Basically it inserts new record in the DB.
|
||||
* Insert [HumanVerificationDetails], by [HumanVerificationDetails.clientId].
|
||||
*/
|
||||
suspend fun insertHumanVerificationDetails(details: HumanVerificationDetails)
|
||||
|
||||
/**
|
||||
* Delete [HumanVerificationDetails], by [clientId].
|
||||
*/
|
||||
suspend fun deleteHumanVerificationDetails(clientId: ClientId)
|
||||
|
||||
/**
|
||||
* Sets new state for a human verification flow, along with the token type and token code if needed.
|
||||
*/
|
||||
|
||||
@@ -385,7 +385,7 @@ public final class me/proton/core/payment/domain/usecase/GetPaymentTokenStatus {
|
||||
}
|
||||
|
||||
public final class me/proton/core/payment/domain/usecase/PerformSubscribe {
|
||||
public fun <init> (Lme/proton/core/payment/domain/repository/PaymentsRepository;)V
|
||||
public fun <init> (Lme/proton/core/payment/domain/repository/PaymentsRepository;Lme/proton/core/humanverification/domain/HumanVerificationManager;Lme/proton/core/network/domain/client/ClientIdProvider;)V
|
||||
public final fun invoke (Lme/proton/core/domain/entity/UserId;JLme/proton/core/payment/domain/entity/Currency;Lme/proton/core/payment/domain/entity/SubscriptionCycle;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static synthetic fun invoke$default (Lme/proton/core/payment/domain/usecase/PerformSubscribe;Lme/proton/core/domain/entity/UserId;JLme/proton/core/payment/domain/entity/Currency;Lme/proton/core/payment/domain/entity/SubscriptionCycle;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ dependencies {
|
||||
project(Module.networkDomain),
|
||||
|
||||
// Features
|
||||
project(Module.humanVerificationDomain),
|
||||
project(Module.accountDomain),
|
||||
project(Module.featureFlagDomain),
|
||||
|
||||
|
||||
+13
-2
@@ -19,6 +19,8 @@
|
||||
package me.proton.core.payment.domain.usecase
|
||||
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.humanverification.domain.HumanVerificationManager
|
||||
import me.proton.core.network.domain.client.ClientIdProvider
|
||||
import me.proton.core.payment.domain.MAX_PLAN_QUANTITY
|
||||
import me.proton.core.payment.domain.entity.Currency
|
||||
import me.proton.core.payment.domain.entity.PaymentBody
|
||||
@@ -33,7 +35,9 @@ import javax.inject.Inject
|
||||
* Headers with token and token type "payment".
|
||||
*/
|
||||
class PerformSubscribe @Inject constructor(
|
||||
private val paymentsRepository: PaymentsRepository
|
||||
private val paymentsRepository: PaymentsRepository,
|
||||
private val humanVerificationManager: HumanVerificationManager,
|
||||
private val clientIdProvider: ClientIdProvider,
|
||||
) {
|
||||
/**
|
||||
* @param codes optional an array of [String] coupon or gift codes used for discounts.
|
||||
@@ -61,6 +65,13 @@ class PerformSubscribe @Inject constructor(
|
||||
codes = codes,
|
||||
plans = planNames.map { it to MAX_PLAN_QUANTITY }.toMap(),
|
||||
cycle = cycle
|
||||
)
|
||||
).also {
|
||||
if (paymentToken != null) {
|
||||
// Clear any previous payment token (unauthenticated session cookie HV details).
|
||||
// HV payment token is previously added by BillingCommonViewModel.
|
||||
val clientId = requireNotNull(clientIdProvider.getClientId(sessionId = null))
|
||||
humanVerificationManager.clearDetails(clientId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
-1
@@ -23,8 +23,10 @@ import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.humanverification.domain.HumanVerificationManager
|
||||
import me.proton.core.network.domain.ApiException
|
||||
import me.proton.core.network.domain.ApiResult
|
||||
import me.proton.core.network.domain.client.ClientIdProvider
|
||||
import me.proton.core.payment.domain.entity.Currency
|
||||
import me.proton.core.payment.domain.entity.PaymentBody
|
||||
import me.proton.core.payment.domain.entity.Subscription
|
||||
@@ -39,6 +41,8 @@ import kotlin.test.assertNotNull
|
||||
class PerformSubscribeTest {
|
||||
// region mocks
|
||||
private val repository = mockk<PaymentsRepository>(relaxed = true)
|
||||
private val humanVerificationManager = mockk<HumanVerificationManager>(relaxed = true)
|
||||
private val clientIdProvider = mockk<ClientIdProvider>(relaxed = true)
|
||||
// endregion
|
||||
|
||||
// region test data
|
||||
@@ -63,7 +67,7 @@ class PerformSubscribeTest {
|
||||
|
||||
@Before
|
||||
fun beforeEveryTest() {
|
||||
useCase = PerformSubscribe(repository)
|
||||
useCase = PerformSubscribe(repository, humanVerificationManager, clientIdProvider)
|
||||
coEvery {
|
||||
repository.createOrUpdateSubscription(
|
||||
testUserId,
|
||||
@@ -205,4 +209,34 @@ class PerformSubscribeTest {
|
||||
assertNotNull(throwable)
|
||||
assertEquals("Test error", throwable.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `payment token is cleared on successful subscription`() = runBlockingTest {
|
||||
useCase.invoke(
|
||||
userId = testUserId,
|
||||
amount = 48,
|
||||
currency = Currency.CHF,
|
||||
cycle = SubscriptionCycle.YEARLY,
|
||||
planNames = listOf(testPlanName),
|
||||
codes = null,
|
||||
paymentToken = "token"
|
||||
)
|
||||
|
||||
coVerify(atLeast = 1) { humanVerificationManager.clearDetails(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `null payment token is not cleared on successful subscription`() = runBlockingTest {
|
||||
useCase.invoke(
|
||||
userId = testUserId,
|
||||
amount = 0,
|
||||
currency = Currency.CHF,
|
||||
cycle = SubscriptionCycle.YEARLY,
|
||||
planNames = listOf(testPlanName),
|
||||
codes = null,
|
||||
paymentToken = null
|
||||
)
|
||||
|
||||
coVerify(exactly = 0) { humanVerificationManager.clearDetails(any()) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ public final class me/proton/core/payment/presentation/ui/UtilsKt {
|
||||
|
||||
public final class me/proton/core/payment/presentation/viewmodel/BillingCommonViewModel : me/proton/core/presentation/viewmodel/ProtonViewModel {
|
||||
public static final field Companion Lme/proton/core/payment/presentation/viewmodel/BillingCommonViewModel$Companion;
|
||||
public fun <init> (Lme/proton/core/payment/domain/usecase/ValidateSubscriptionPlan;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithNewCreditCard;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithNewPayPal;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithExistingPaymentMethod;Lme/proton/core/payment/domain/usecase/PerformSubscribe;Lme/proton/core/country/domain/usecase/GetCountry;)V
|
||||
public fun <init> (Lme/proton/core/payment/domain/usecase/ValidateSubscriptionPlan;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithNewCreditCard;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithNewPayPal;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithExistingPaymentMethod;Lme/proton/core/payment/domain/usecase/PerformSubscribe;Lme/proton/core/country/domain/usecase/GetCountry;Lme/proton/core/humanverification/domain/HumanVerificationManager;Lme/proton/core/network/domain/client/ClientIdProvider;)V
|
||||
public final fun getPlansValidationState ()Lkotlinx/coroutines/flow/StateFlow;
|
||||
public final fun getSubscriptionResult ()Lkotlinx/coroutines/flow/StateFlow;
|
||||
public final fun onThreeDSTokenApproved (Lme/proton/core/domain/entity/UserId;Ljava/util/List;Ljava/util/List;JLme/proton/core/payment/domain/entity/Currency;Lme/proton/core/payment/domain/entity/SubscriptionCycle;Ljava/lang/String;)Lkotlinx/coroutines/Job;
|
||||
@@ -649,11 +649,11 @@ public final class me/proton/core/payment/presentation/viewmodel/BillingCommonVi
|
||||
}
|
||||
|
||||
public final class me/proton/core/payment/presentation/viewmodel/BillingCommonViewModel_Factory : dagger/internal/Factory {
|
||||
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
|
||||
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lme/proton/core/payment/presentation/viewmodel/BillingCommonViewModel_Factory;
|
||||
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
|
||||
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lme/proton/core/payment/presentation/viewmodel/BillingCommonViewModel_Factory;
|
||||
public synthetic fun get ()Ljava/lang/Object;
|
||||
public fun get ()Lme/proton/core/payment/presentation/viewmodel/BillingCommonViewModel;
|
||||
public static fun newInstance (Lme/proton/core/payment/domain/usecase/ValidateSubscriptionPlan;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithNewCreditCard;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithNewPayPal;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithExistingPaymentMethod;Lme/proton/core/payment/domain/usecase/PerformSubscribe;Lme/proton/core/country/domain/usecase/GetCountry;)Lme/proton/core/payment/presentation/viewmodel/BillingCommonViewModel;
|
||||
public static fun newInstance (Lme/proton/core/payment/domain/usecase/ValidateSubscriptionPlan;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithNewCreditCard;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithNewPayPal;Lme/proton/core/payment/domain/usecase/CreatePaymentTokenWithExistingPaymentMethod;Lme/proton/core/payment/domain/usecase/PerformSubscribe;Lme/proton/core/country/domain/usecase/GetCountry;Lme/proton/core/humanverification/domain/HumanVerificationManager;Lme/proton/core/network/domain/client/ClientIdProvider;)Lme/proton/core/payment/presentation/viewmodel/BillingCommonViewModel;
|
||||
}
|
||||
|
||||
public final class me/proton/core/payment/presentation/viewmodel/BillingViewModel : me/proton/core/presentation/viewmodel/ProtonViewModel {
|
||||
|
||||
+12
-9
@@ -29,6 +29,7 @@ import me.proton.core.payment.presentation.ui.StartBilling
|
||||
import me.proton.core.payment.presentation.ui.StartPaymentOptions
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("UseIfInsteadOfWhen")
|
||||
class PaymentsOrchestrator @Inject constructor() {
|
||||
|
||||
// region result launchers
|
||||
@@ -38,6 +39,9 @@ class PaymentsOrchestrator @Inject constructor() {
|
||||
|
||||
private var onPaymentResultListener: (result: BillingResult?) -> Unit = {}
|
||||
|
||||
private fun <T> checkRegistered(launcher: ActivityResultLauncher<T>?) =
|
||||
checkNotNull(launcher) { "You must call PaymentsOrchestrator.register(context) before starting workflow!" }
|
||||
|
||||
// region public api
|
||||
fun register(caller: ActivityResultCaller) {
|
||||
billingLauncher = registerBillingResult(caller)
|
||||
@@ -60,16 +64,15 @@ class PaymentsOrchestrator @Inject constructor() {
|
||||
selectedPlan: PlanShortDetails,
|
||||
codes: List<String>? = null
|
||||
) {
|
||||
userId?.let {
|
||||
// start the payment options chooser screen
|
||||
paymentOptionsLauncher?.launch(
|
||||
PaymentOptionsInput(it.id, selectedPlan, codes)
|
||||
) ?: throw IllegalStateException("You must call register(context) before any start workflow function!")
|
||||
} ?: run {
|
||||
// directly start the billing screen
|
||||
billingLauncher?.launch(
|
||||
when (userId) {
|
||||
// Directly start the billing screen.
|
||||
null -> checkRegistered(billingLauncher).launch(
|
||||
BillingInput(null, emptyList(), selectedPlan, codes, null)
|
||||
) ?: throw IllegalStateException("You must call register before any start workflow function!")
|
||||
)
|
||||
// Start the payment options chooser screen.
|
||||
else -> checkRegistered(paymentOptionsLauncher).launch(
|
||||
PaymentOptionsInput(userId.id, selectedPlan, codes)
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
+11
-2
@@ -27,6 +27,8 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import me.proton.core.country.domain.usecase.GetCountry
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.humanverification.domain.HumanVerificationManager
|
||||
import me.proton.core.network.domain.client.ClientIdProvider
|
||||
import me.proton.core.payment.domain.entity.Card
|
||||
import me.proton.core.payment.domain.entity.Currency
|
||||
import me.proton.core.payment.domain.entity.PaymentToken
|
||||
@@ -40,6 +42,7 @@ import me.proton.core.payment.domain.usecase.CreatePaymentTokenWithNewCreditCard
|
||||
import me.proton.core.payment.domain.usecase.CreatePaymentTokenWithNewPayPal
|
||||
import me.proton.core.payment.domain.usecase.PerformSubscribe
|
||||
import me.proton.core.payment.domain.usecase.ValidateSubscriptionPlan
|
||||
import me.proton.core.payment.presentation.entity.BillingResult
|
||||
import me.proton.core.payment.presentation.entity.CurrentSubscribedPlanDetails
|
||||
import me.proton.core.plan.domain.entity.MASK_MAIL
|
||||
import me.proton.core.plan.domain.entity.MASK_VPN
|
||||
@@ -61,7 +64,9 @@ class BillingCommonViewModel @Inject constructor(
|
||||
private val createPaymentTokenWithNewPayPal: CreatePaymentTokenWithNewPayPal,
|
||||
private val createPaymentTokenWithExistingPaymentMethod: CreatePaymentTokenWithExistingPaymentMethod,
|
||||
private val performSubscribe: PerformSubscribe,
|
||||
private val getCountry: GetCountry
|
||||
private val getCountry: GetCountry,
|
||||
private val humanVerificationManager: HumanVerificationManager,
|
||||
private val clientIdProvider: ClientIdProvider,
|
||||
) : ProtonViewModel() {
|
||||
|
||||
private val _subscriptionState = MutableStateFlow<State>(State.Idle)
|
||||
@@ -247,7 +252,11 @@ class BillingCommonViewModel @Inject constructor(
|
||||
token: String
|
||||
): State =
|
||||
if (userId == null) {
|
||||
// subscription should be created by the sign up module. return payment info (needed for Human Ver headers).
|
||||
// Token will be used during sign up (create user), as part of HumanVerification headers.
|
||||
// Subscription will be performed during login, just after create user.
|
||||
// Token will be cleared by PerformSubscribe.
|
||||
val clientId = requireNotNull(clientIdProvider.getClientId(sessionId = null))
|
||||
humanVerificationManager.addDetails(BillingResult.paymentDetails(clientId = clientId, token = token))
|
||||
State.Success.SignUpTokenReady(amount, currency, cycle, token)
|
||||
} else {
|
||||
State.Success.SubscriptionCreated(
|
||||
|
||||
+8
-1
@@ -25,8 +25,10 @@ import io.mockk.mockk
|
||||
import me.proton.core.country.domain.entity.Country
|
||||
import me.proton.core.country.domain.usecase.GetCountry
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.humanverification.domain.HumanVerificationManager
|
||||
import me.proton.core.network.domain.ApiException
|
||||
import me.proton.core.network.domain.ApiResult
|
||||
import me.proton.core.network.domain.client.ClientIdProvider
|
||||
import me.proton.core.payment.domain.entity.Card
|
||||
import me.proton.core.payment.domain.entity.Currency
|
||||
import me.proton.core.payment.domain.entity.PaymentToken
|
||||
@@ -57,6 +59,8 @@ class BillingViewModelTest : ArchTest, CoroutinesTest {
|
||||
private val createPaymentTokenWithNewPayPal = mockk<CreatePaymentTokenWithNewPayPal>()
|
||||
private val performSubscribe = mockk<PerformSubscribe>()
|
||||
private val getCountryCode = mockk<GetCountry>()
|
||||
private val humanVerificationManager = mockk<HumanVerificationManager>(relaxed = true)
|
||||
private val clientIdProvider = mockk<ClientIdProvider>(relaxed = true)
|
||||
// endregion
|
||||
|
||||
// region test data
|
||||
@@ -88,7 +92,9 @@ class BillingViewModelTest : ArchTest, CoroutinesTest {
|
||||
createPaymentTokenWithNewPayPal,
|
||||
createPaymentTokenWithExistingPaymentMethod,
|
||||
performSubscribe,
|
||||
getCountryCode
|
||||
getCountryCode,
|
||||
humanVerificationManager,
|
||||
clientIdProvider
|
||||
)
|
||||
}
|
||||
|
||||
@@ -522,6 +528,7 @@ class BillingViewModelTest : ArchTest, CoroutinesTest {
|
||||
|
||||
// THEN
|
||||
coVerify(exactly = 1) { createPaymentToken.invoke(null, 2, testCurrency, paymentType) }
|
||||
coVerify(exactly = 1) { humanVerificationManager.addDetails(any()) }
|
||||
coVerify(exactly = 0) { performSubscribe.invoke(any(), any(), any(), any(), any(), any(), any()) }
|
||||
assertIs<BillingCommonViewModel.State.Idle>(awaitItem())
|
||||
assertIs<BillingCommonViewModel.State.Processing>(awaitItem())
|
||||
|
||||
Reference in New Issue
Block a user