diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2023-11-16 00:56:10 +0100 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2023-11-16 09:34:26 +0100 |
| commit | ae7471c50a653133aae6472199d7b0d16ad2a145 (patch) | |
| tree | 20488d174aae09caee907ac20960e26146b1aa9f /android/lib | |
| parent | 5c5c2a95d676648ffbd953b5f9e8587a8a80bf66 (diff) | |
| download | mullvadvpn-ae7471c50a653133aae6472199d7b0d16ad2a145.tar.xz mullvadvpn-ae7471c50a653133aae6472199d7b0d16ad2a145.zip | |
Add payment module and billing payment repository
Diffstat (limited to 'android/lib')
19 files changed, 443 insertions, 0 deletions
diff --git a/android/lib/billing/build.gradle.kts b/android/lib/billing/build.gradle.kts index a010534085..255459f453 100644 --- a/android/lib/billing/build.gradle.kts +++ b/android/lib/billing/build.gradle.kts @@ -54,6 +54,9 @@ dependencies { //IPC implementation(project(Dependencies.Mullvad.ipcLib)) + //Payment library + implementation(project(Dependencies.Mullvad.paymentLib)) + // Test dependencies testImplementation(project(Dependencies.Mullvad.commonTestLib)) testImplementation(Dependencies.Kotlin.test) diff --git a/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingPaymentRepository.kt b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingPaymentRepository.kt new file mode 100644 index 0000000000..76df623ada --- /dev/null +++ b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingPaymentRepository.kt @@ -0,0 +1,167 @@ +package net.mullvad.mullvadvpn.lib.billing + +import android.app.Activity +import com.android.billingclient.api.BillingClient.BillingResponseCode +import com.android.billingclient.api.Purchase +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flow +import net.mullvad.mullvadvpn.lib.billing.extension.getProductDetails +import net.mullvad.mullvadvpn.lib.billing.extension.nonPendingPurchases +import net.mullvad.mullvadvpn.lib.billing.extension.responseCode +import net.mullvad.mullvadvpn.lib.billing.extension.toBillingException +import net.mullvad.mullvadvpn.lib.billing.extension.toPaymentAvailability +import net.mullvad.mullvadvpn.lib.billing.extension.toPaymentStatus +import net.mullvad.mullvadvpn.lib.billing.extension.toPurchaseResult +import net.mullvad.mullvadvpn.lib.billing.model.BillingException +import net.mullvad.mullvadvpn.lib.billing.model.PurchaseEvent +import net.mullvad.mullvadvpn.lib.payment.PaymentRepository +import net.mullvad.mullvadvpn.lib.payment.ProductIds +import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability +import net.mullvad.mullvadvpn.lib.payment.model.ProductId +import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult +import net.mullvad.mullvadvpn.lib.payment.model.VerificationResult +import net.mullvad.mullvadvpn.model.PlayPurchase +import net.mullvad.mullvadvpn.model.PlayPurchaseInitResult +import net.mullvad.mullvadvpn.model.PlayPurchaseVerifyResult + +class BillingPaymentRepository( + private val billingRepository: BillingRepository, + private val playPurchaseRepository: PlayPurchaseRepository +) : PaymentRepository { + + override fun queryPaymentAvailability(): Flow<PaymentAvailability> = flow { + emit(PaymentAvailability.Loading) + val purchases = billingRepository.queryPurchases() + val productIdToPaymentStatus = + purchases.purchasesList + .filter { it.products.isNotEmpty() } + .associate { it.products.first() to it.purchaseState.toPaymentStatus() } + emit( + billingRepository + .queryProducts(listOf(ProductIds.OneMonth)) + .toPaymentAvailability(productIdToPaymentStatus) + ) + } + + override fun purchaseProduct( + productId: ProductId, + activityProvider: () -> Activity + ): Flow<PurchaseResult> = flow { + emit(PurchaseResult.FetchingProducts) + + val productDetailsResult = billingRepository.queryProducts(listOf(productId.value)) + + val productDetails = + when (productDetailsResult.responseCode()) { + BillingResponseCode.OK -> { + productDetailsResult.getProductDetails(productId.value) + ?: run { + emit(PurchaseResult.Error.NoProductFound(productId)) + return@flow + } + } + else -> { + emit( + PurchaseResult.Error.FetchProductsError( + productId, + productDetailsResult.toBillingException() + ) + ) + return@flow + } + } + + // Get transaction id + emit(PurchaseResult.FetchingObfuscationId) + val obfuscatedId: String = + when (val result = initialisePurchase()) { + is PlayPurchaseInitResult.Ok -> result.obfuscatedId + else -> { + emit(PurchaseResult.Error.TransactionIdError(productId, null)) + return@flow + } + } + + val result = + billingRepository.startPurchaseFlow( + productDetails = productDetails, + obfuscatedId = obfuscatedId, + activityProvider = activityProvider + ) + + if (result.responseCode == BillingResponseCode.OK) { + emit(PurchaseResult.BillingFlowStarted) + } else { + emit( + PurchaseResult.Error.BillingError( + BillingException(result.responseCode, result.debugMessage) + ) + ) + return@flow + } + + // Wait for a callback from the billing library + when (val event = billingRepository.purchaseEvents.firstOrNull()) { + is PurchaseEvent.Error -> emit(event.toPurchaseResult()) + is PurchaseEvent.Completed -> { + val purchase = + event.purchases.firstOrNull() + ?: run { + emit(PurchaseResult.Error.BillingError(null)) + return@flow + } + if (purchase.purchaseState == Purchase.PurchaseState.PENDING) { + emit(PurchaseResult.Completed.Pending) + } else { + emit(PurchaseResult.VerificationStarted) + if (verifyPurchase(event.purchases.first()) == PlayPurchaseVerifyResult.Ok) { + emit(PurchaseResult.Completed.Success) + } else { + emit(PurchaseResult.Error.VerificationError(null)) + } + } + } + PurchaseEvent.UserCanceled -> emit(event.toPurchaseResult()) + else -> emit(PurchaseResult.Error.BillingError(null)) + } + } + + override fun verifyPurchases(): Flow<VerificationResult> = flow { + emit(VerificationResult.FetchingUnfinishedPurchases) + val purchasesResult = billingRepository.queryPurchases() + when (purchasesResult.responseCode()) { + BillingResponseCode.OK -> { + val purchases = purchasesResult.nonPendingPurchases() + if (purchases.isNotEmpty()) { + emit(VerificationResult.VerificationStarted) + val verificationResult = verifyPurchase(purchases.first()) + emit( + when (verificationResult) { + is PlayPurchaseVerifyResult.Error -> + VerificationResult.Error.VerificationError(null) + PlayPurchaseVerifyResult.Ok -> VerificationResult.Success + } + ) + } else { + emit(VerificationResult.NothingToVerify) + } + } + else -> + emit(VerificationResult.Error.BillingError(purchasesResult.toBillingException())) + } + } + + private suspend fun initialisePurchase(): PlayPurchaseInitResult { + return playPurchaseRepository.initializePlayPurchase() + } + + private suspend fun verifyPurchase(purchase: Purchase): PlayPurchaseVerifyResult { + return playPurchaseRepository.verifyPlayPurchase( + PlayPurchase( + productId = purchase.products.first(), + purchaseToken = purchase.purchaseToken, + ) + ) + } +} diff --git a/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/ProductDetailsResultExtensions.kt b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/ProductDetailsResultExtensions.kt new file mode 100644 index 0000000000..3e4aee180a --- /dev/null +++ b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/ProductDetailsResultExtensions.kt @@ -0,0 +1,13 @@ +package net.mullvad.mullvadvpn.lib.billing.extension + +import com.android.billingclient.api.ProductDetails +import com.android.billingclient.api.ProductDetailsResult +import net.mullvad.mullvadvpn.lib.billing.model.BillingException + +fun ProductDetailsResult.getProductDetails(productId: String): ProductDetails? = + this.productDetailsList?.firstOrNull { it.productId == productId } + +fun ProductDetailsResult.responseCode(): Int = this.billingResult.responseCode + +fun ProductDetailsResult.toBillingException(): BillingException = + BillingException(responseCode = this.responseCode(), message = this.billingResult.debugMessage) diff --git a/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/ProductDetailsResultToPaymentAvailability.kt b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/ProductDetailsResultToPaymentAvailability.kt new file mode 100644 index 0000000000..37cc701724 --- /dev/null +++ b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/ProductDetailsResultToPaymentAvailability.kt @@ -0,0 +1,37 @@ +package net.mullvad.mullvadvpn.lib.billing.extension + +import com.android.billingclient.api.BillingClient +import com.android.billingclient.api.ProductDetailsResult +import net.mullvad.mullvadvpn.lib.billing.model.BillingException +import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability +import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus + +fun ProductDetailsResult.toPaymentAvailability( + productIdToPaymentStatus: Map<String, PaymentStatus?> +) = + when (this.billingResult.responseCode) { + BillingClient.BillingResponseCode.OK -> { + val productDetailsList = this.productDetailsList + if (productDetailsList?.isNotEmpty() == true) { + PaymentAvailability.ProductsAvailable( + productDetailsList.toPaymentProducts(productIdToPaymentStatus) + ) + } else { + PaymentAvailability.NoProductsFounds + } + } + BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> + PaymentAvailability.Error.BillingUnavailable + BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> + PaymentAvailability.Error.ServiceUnavailable + BillingClient.BillingResponseCode.DEVELOPER_ERROR -> + PaymentAvailability.Error.DeveloperError + BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED -> + PaymentAvailability.Error.FeatureNotSupported + BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> + PaymentAvailability.Error.ItemUnavailable + else -> + PaymentAvailability.Error.Other( + BillingException(this.billingResult.responseCode, this.billingResult.debugMessage) + ) + } diff --git a/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/ProductDetailsToPaymentProduct.kt b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/ProductDetailsToPaymentProduct.kt new file mode 100644 index 0000000000..fa9a20613f --- /dev/null +++ b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/ProductDetailsToPaymentProduct.kt @@ -0,0 +1,17 @@ +package net.mullvad.mullvadvpn.lib.billing.extension + +import com.android.billingclient.api.ProductDetails +import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct +import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus +import net.mullvad.mullvadvpn.lib.payment.model.ProductId +import net.mullvad.mullvadvpn.lib.payment.model.ProductPrice + +fun ProductDetails.toPaymentProduct(productIdToStatus: Map<String, PaymentStatus?>) = + PaymentProduct( + productId = ProductId(this.productId), + price = ProductPrice(this.oneTimePurchaseOfferDetails?.formattedPrice ?: ""), + productIdToStatus[this.productId] + ) + +fun List<ProductDetails>.toPaymentProducts(productIdToStatus: Map<String, PaymentStatus?>) = + this.map { it.toPaymentProduct(productIdToStatus) } diff --git a/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/PurchaseEventToPurchaseResult.kt b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/PurchaseEventToPurchaseResult.kt new file mode 100644 index 0000000000..e0e4bf0a77 --- /dev/null +++ b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/PurchaseEventToPurchaseResult.kt @@ -0,0 +1,11 @@ +package net.mullvad.mullvadvpn.lib.billing.extension + +import net.mullvad.mullvadvpn.lib.billing.model.PurchaseEvent +import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult + +fun PurchaseEvent.toPurchaseResult() = + when (this) { + is PurchaseEvent.Error -> PurchaseResult.Error.BillingError(this.exception) + is PurchaseEvent.Completed -> PurchaseResult.VerificationStarted + PurchaseEvent.UserCanceled -> PurchaseResult.Completed.Cancelled + } diff --git a/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/PurchaseStateToPaymentStatus.kt b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/PurchaseStateToPaymentStatus.kt new file mode 100644 index 0000000000..701e5fde3d --- /dev/null +++ b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/PurchaseStateToPaymentStatus.kt @@ -0,0 +1,11 @@ +package net.mullvad.mullvadvpn.lib.billing.extension + +import com.android.billingclient.api.Purchase +import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus + +internal fun Int.toPaymentStatus(): PaymentStatus? = + when (this) { + Purchase.PurchaseState.PURCHASED -> PaymentStatus.VERIFICATION_IN_PROGRESS + Purchase.PurchaseState.PENDING -> PaymentStatus.PENDING + else -> null + } diff --git a/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/PurchasesResultExtensions.kt b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/PurchasesResultExtensions.kt new file mode 100644 index 0000000000..d76d1a8b7e --- /dev/null +++ b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/extension/PurchasesResultExtensions.kt @@ -0,0 +1,13 @@ +package net.mullvad.mullvadvpn.lib.billing.extension + +import com.android.billingclient.api.Purchase +import com.android.billingclient.api.PurchasesResult +import net.mullvad.mullvadvpn.lib.billing.model.BillingException + +fun PurchasesResult.nonPendingPurchases(): List<Purchase> = + this.purchasesList.filter { it.purchaseState != Purchase.PurchaseState.PENDING } + +fun PurchasesResult.responseCode(): Int = this.billingResult.responseCode + +fun PurchasesResult.toBillingException(): BillingException = + BillingException(responseCode = this.responseCode(), message = this.billingResult.debugMessage) diff --git a/android/lib/payment/build.gradle.kts b/android/lib/payment/build.gradle.kts new file mode 100644 index 0000000000..23f945b4f9 --- /dev/null +++ b/android/lib/payment/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id(Dependencies.Plugin.androidLibraryId) + id(Dependencies.Plugin.kotlinAndroidId) +} + +android { + namespace = "net.mullvad.mullvadvpn.lib.payment" + compileSdk = Versions.Android.compileSdkVersion + + defaultConfig { + minSdk = Versions.Android.minSdkVersion + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = Versions.jvmTarget + } + + lint { + lintConfig = file("${rootProject.projectDir}/config/lint.xml") + abortOnError = true + warningsAsErrors = true + } + + packaging { + resources { + pickFirsts += setOf( + // Fixes packaging error caused by: jetified-junit-* + "META-INF/LICENSE.md", + "META-INF/LICENSE-notice.md" + ) + } + } +} + +dependencies { + implementation(Dependencies.Kotlin.stdlib) + implementation(Dependencies.KotlinX.coroutinesAndroid) +} diff --git a/android/lib/payment/src/main/AndroidManifest.xml b/android/lib/payment/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..b2d3ea1235 --- /dev/null +++ b/android/lib/payment/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" /> diff --git a/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/PaymentRepository.kt b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/PaymentRepository.kt new file mode 100644 index 0000000000..73fd0c061d --- /dev/null +++ b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/PaymentRepository.kt @@ -0,0 +1,20 @@ +package net.mullvad.mullvadvpn.lib.payment + +import android.app.Activity +import kotlinx.coroutines.flow.Flow +import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability +import net.mullvad.mullvadvpn.lib.payment.model.ProductId +import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult +import net.mullvad.mullvadvpn.lib.payment.model.VerificationResult + +interface PaymentRepository { + + fun purchaseProduct( + productId: ProductId, + activityProvider: () -> Activity + ): Flow<PurchaseResult> + + fun verifyPurchases(): Flow<VerificationResult> + + fun queryPaymentAvailability(): Flow<PaymentAvailability> +} diff --git a/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/ProductIds.kt b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/ProductIds.kt new file mode 100644 index 0000000000..8754968891 --- /dev/null +++ b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/ProductIds.kt @@ -0,0 +1,5 @@ +package net.mullvad.mullvadvpn.lib.payment + +object ProductIds { + const val OneMonth = "one_month" +} diff --git a/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PaymentAvailability.kt b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PaymentAvailability.kt new file mode 100644 index 0000000000..cee2800677 --- /dev/null +++ b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PaymentAvailability.kt @@ -0,0 +1,26 @@ +package net.mullvad.mullvadvpn.lib.payment.model + +sealed interface PaymentAvailability { + data object Loading : PaymentAvailability + + data class ProductsAvailable(val products: List<PaymentProduct>) : PaymentAvailability + + data object ProductsUnavailable : PaymentAvailability + + data object NoProductsFounds : PaymentAvailability + + sealed interface Error: PaymentAvailability { + data object BillingUnavailable : Error + + data object ServiceUnavailable : Error + + data object FeatureNotSupported : Error + + data object DeveloperError : Error + + data object ItemUnavailable : Error + + data class Other(val exception: Throwable) : + Error + } +} diff --git a/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PaymentProduct.kt b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PaymentProduct.kt new file mode 100644 index 0000000000..8945453d37 --- /dev/null +++ b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PaymentProduct.kt @@ -0,0 +1,7 @@ +package net.mullvad.mullvadvpn.lib.payment.model + +data class PaymentProduct( + val productId: ProductId, + val price: ProductPrice, + val status: PaymentStatus? +) diff --git a/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PaymentStatus.kt b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PaymentStatus.kt new file mode 100644 index 0000000000..37574249a6 --- /dev/null +++ b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PaymentStatus.kt @@ -0,0 +1,6 @@ +package net.mullvad.mullvadvpn.lib.payment.model + +enum class PaymentStatus { + PENDING, + VERIFICATION_IN_PROGRESS +} diff --git a/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/ProductId.kt b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/ProductId.kt new file mode 100644 index 0000000000..49a367f7c2 --- /dev/null +++ b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/ProductId.kt @@ -0,0 +1,4 @@ +package net.mullvad.mullvadvpn.lib.payment.model + +@JvmInline +value class ProductId(val value: String) diff --git a/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/ProductPrice.kt b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/ProductPrice.kt new file mode 100644 index 0000000000..4939eac3a5 --- /dev/null +++ b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/ProductPrice.kt @@ -0,0 +1,4 @@ +package net.mullvad.mullvadvpn.lib.payment.model + +@JvmInline +value class ProductPrice(val value: String) diff --git a/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PurchaseResult.kt b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PurchaseResult.kt new file mode 100644 index 0000000000..f5b89bffe6 --- /dev/null +++ b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/PurchaseResult.kt @@ -0,0 +1,34 @@ +package net.mullvad.mullvadvpn.lib.payment.model + +sealed interface PurchaseResult { + data object FetchingProducts : PurchaseResult + + data object FetchingObfuscationId : PurchaseResult + + data object BillingFlowStarted : PurchaseResult + + data object VerificationStarted : PurchaseResult + + sealed interface Completed : PurchaseResult { + data object Success : Completed + + data object Cancelled : Completed + + // This ends our part of the purchase flow. The rest is handled by Google and the api. + data object Pending : Completed + } + + sealed interface Error : PurchaseResult { + data class NoProductFound(val productId: ProductId) : Error + + data class FetchProductsError(val productId: ProductId, val exception: Throwable?) : Error + + data class TransactionIdError(val productId: ProductId, val exception: Throwable?) : Error + + data class BillingError(val exception: Throwable?) : Error + + data class VerificationError(val exception: Throwable?) : Error + } + + fun isTerminatingState(): Boolean = this is Completed || this is Error +} diff --git a/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/VerificationResult.kt b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/VerificationResult.kt new file mode 100644 index 0000000000..725ea0af68 --- /dev/null +++ b/android/lib/payment/src/main/kotlin/net/mullvad/mullvadvpn/lib/payment/model/VerificationResult.kt @@ -0,0 +1,19 @@ +package net.mullvad.mullvadvpn.lib.payment.model + +sealed interface VerificationResult { + data object FetchingUnfinishedPurchases : VerificationResult + + data object VerificationStarted : VerificationResult + + // No verification was needed as there is no purchases to verify + data object NothingToVerify : VerificationResult + + data object Success : VerificationResult + + // Generic error, add more cases as needed + sealed interface Error : VerificationResult { + data class BillingError(val exception: Throwable?) : Error + + data class VerificationError(val exception: Throwable?) : Error + } +} |
