summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2024-12-02 14:00:41 +0100
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2024-12-03 10:28:16 +0100
commit41e9feafddaabc9ab42793cd4242ec5059b4136b (patch)
tree697adf3083ff88f75f23614d7bd85a32aa74caa9 /android
parentce8c122ca8983ce72d8362c7054ec318ed80b7f4 (diff)
downloadmullvadvpn-41e9feafddaabc9ab42793cd4242ec5059b4136b.tar.xz
mullvadvpn-41e9feafddaabc9ab42793cd4242ec5059b4136b.zip
Add check for empty play purchase payment token
Diffstat (limited to 'android')
-rw-r--r--android/lib/billing/build.gradle.kts3
-rw-r--r--android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingPaymentRepository.kt17
-rw-r--r--android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepository.kt6
-rw-r--r--android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingPaymentRepositoryTest.kt26
-rw-r--r--android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepositoryTest.kt21
5 files changed, 71 insertions, 2 deletions
diff --git a/android/lib/billing/build.gradle.kts b/android/lib/billing/build.gradle.kts
index 46554cf956..d9b4c4dc68 100644
--- a/android/lib/billing/build.gradle.kts
+++ b/android/lib/billing/build.gradle.kts
@@ -62,6 +62,9 @@ dependencies {
// Management service
implementation(projects.lib.daemonGrpc)
+ // Logger
+ implementation(libs.kermit)
+
// Test dependencies
testRuntimeOnly(Dependencies.junitJupiterEngine)
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
index 540a8ad929..39cc584a57 100644
--- 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
@@ -2,8 +2,12 @@ package net.mullvad.mullvadvpn.lib.billing
import android.app.Activity
import arrow.core.Either
+import arrow.core.flatMap
+import arrow.core.left
import arrow.core.raise.either
import arrow.core.raise.ensure
+import arrow.core.right
+import co.touchlab.kermit.Logger
import com.android.billingclient.api.BillingClient.BillingResponseCode
import com.android.billingclient.api.Purchase
import kotlinx.coroutines.flow.Flow
@@ -19,6 +23,7 @@ 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.model.PlayPurchase
+import net.mullvad.mullvadvpn.lib.model.PlayPurchaseInitError
import net.mullvad.mullvadvpn.lib.model.PlayPurchasePaymentToken
import net.mullvad.mullvadvpn.lib.payment.PaymentRepository
import net.mullvad.mullvadvpn.lib.payment.ProductIds
@@ -78,7 +83,7 @@ class BillingPaymentRepository(
// Get transaction id
emit(PurchaseResult.FetchingObfuscationId)
val obfuscatedId: PlayPurchasePaymentToken =
- initialisePurchase()
+ initializePurchase()
.fold(
{
emit(PurchaseResult.Error.TransactionIdError(productId, null))
@@ -148,7 +153,15 @@ class BillingPaymentRepository(
.bind()
}
- private suspend fun initialisePurchase() = playPurchaseRepository.initializePlayPurchase()
+ private suspend fun initializePurchase() =
+ playPurchaseRepository.initializePlayPurchase().flatMap {
+ if (it.value.isNotEmpty()) {
+ it.right()
+ } else {
+ Logger.e("PlayPurchasePaymentToken is empty")
+ PlayPurchaseInitError.OtherError.left()
+ }
+ }
private suspend fun verifyPurchase(purchase: Purchase) =
playPurchaseRepository.verifyPlayPurchase(
diff --git a/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepository.kt b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepository.kt
index 77eaea03a6..2e9f956d07 100644
--- a/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepository.kt
+++ b/android/lib/billing/src/main/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepository.kt
@@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.lib.billing
import android.app.Activity
import android.content.Context
+import co.touchlab.kermit.Logger
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.BillingClient.BillingResponseCode
import com.android.billingclient.api.BillingClientStateListener
@@ -124,6 +125,11 @@ class BillingRepository(context: Context) {
return try {
ensureConnected()
+ if (obfuscatedId.isEmpty()) {
+ Logger.e("Obfuscated id is empty")
+ return BillingResult.newBuilder().setResponseCode(BillingResponseCode.ERROR).build()
+ }
+
val productDetailsParamsList =
listOf(
BillingFlowParams.ProductDetailsParams.newBuilder()
diff --git a/android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingPaymentRepositoryTest.kt b/android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingPaymentRepositoryTest.kt
index 48618feb2b..d04c40029e 100644
--- a/android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingPaymentRepositoryTest.kt
+++ b/android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingPaymentRepositoryTest.kt
@@ -184,6 +184,32 @@ class BillingPaymentRepositoryTest {
}
@Test
+ fun `purchaseProduct should return TransactionIdError when PlayPurchasePaymentToken is empty`() =
+ runTest {
+ // Arrange
+ val mockProductId = ProductId("MOCK")
+ val mockProductDetailsResult = mockk<ProductDetailsResult>()
+ val mockProductDetails: ProductDetails = mockk()
+ every { mockProductDetails.productId } returns mockProductId.value
+ every { mockProductDetailsResult.billingResult.responseCode } returns
+ BillingResponseCode.OK
+ every { mockProductDetailsResult.productDetailsList } returns listOf(mockProductDetails)
+ coEvery { mockBillingRepository.queryProducts(listOf(mockProductId.value)) } returns
+ mockProductDetailsResult
+ coEvery { mockPlayPurchaseRepository.initializePlayPurchase() } returns
+ PlayPurchasePaymentToken("").right()
+
+ // Act, Assert
+ paymentRepository.purchaseProduct(mockProductId, mockk()).test {
+ assertIs<PurchaseResult.FetchingProducts>(awaitItem())
+ assertIs<PurchaseResult.FetchingObfuscationId>(awaitItem())
+ val result = awaitItem()
+ assertIs<PurchaseResult.Error.TransactionIdError>(result)
+ awaitComplete()
+ }
+ }
+
+ @Test
fun `purchaseProduct should return BillingError on billing unavailable from startPurchaseFlow`() =
runTest {
// Arrange
diff --git a/android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepositoryTest.kt b/android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepositoryTest.kt
index 3313e2d91e..bce573a6b6 100644
--- a/android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepositoryTest.kt
+++ b/android/lib/billing/src/test/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepositoryTest.kt
@@ -206,6 +206,27 @@ class BillingRepositoryTest {
}
@Test
+ fun `starting purchase flow with empty transaction id should return error`() = runTest {
+ // Arrange
+ val transactionId = ""
+ val mockProductDetails: ProductDetails = mockk(relaxed = true)
+ val mockActivityProvider: () -> Activity = mockk()
+ every { mockBillingClient.isReady } returns true
+ every { mockBillingClient.connectionState } returns BillingClient.ConnectionState.CONNECTED
+
+ // Act
+ val result =
+ billingRepository.startPurchaseFlow(
+ mockProductDetails,
+ transactionId,
+ mockActivityProvider,
+ )
+
+ // Assert
+ assertEquals(BillingResponseCode.ERROR, result.responseCode)
+ }
+
+ @Test
fun `when billing client query purchases returns OK query purchases should return OK`() =
runTest {
// Arrange