diff options
| author | David Göransson <david.goransson@mullvad.net> | 2026-04-02 11:21:28 +0200 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2026-04-02 11:21:28 +0200 |
| commit | a124229440cd7c83450a00886f1bddbf99871f8b (patch) | |
| tree | 2791fc72cb193723bd8a83eba9321ace11ee0af2 | |
| parent | cc6fbb2731689317ae3a48625904dde15a5c1daf (diff) | |
| parent | d471cbeea9cc7858e194d25ee27874d91feff035 (diff) | |
| download | mullvadvpn-a124229440cd7c83450a00886f1bddbf99871f8b.tar.xz mullvadvpn-a124229440cd7c83450a00886f1bddbf99871f8b.zip | |
Merge branch 'clean-up-obfuscation-id'
18 files changed, 271 insertions, 65 deletions
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 2c9d72b188..9964ee71d3 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 @@ -22,6 +22,7 @@ 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.model.PlayExternalObfuscatedAccountId import net.mullvad.mullvadvpn.lib.model.PlayPurchase import net.mullvad.mullvadvpn.lib.model.PlayPurchaseInitError import net.mullvad.mullvadvpn.lib.model.PlayPurchasePaymentToken @@ -83,7 +84,7 @@ class BillingPaymentRepository( // Get transaction id emit(PurchaseResult.FetchingObfuscationId) - val obfuscatedId: PlayPurchasePaymentToken = + val obfuscatedId: PlayExternalObfuscatedAccountId = initializePurchase() .fold( { @@ -96,7 +97,7 @@ class BillingPaymentRepository( val result = billingRepository.startPurchaseFlow( productDetails = productDetails, - obfuscatedId = obfuscatedId.value, + obfuscatedId = obfuscatedId, activityProvider = activityProvider, ) @@ -126,7 +127,7 @@ class BillingPaymentRepository( } else { emit(PurchaseResult.VerificationStarted) emit( - verifyPurchase(event.purchases.first()) + verifyPurchase(purchase) .fold( { PurchaseResult.Error.VerificationError(null) }, { productId -> PurchaseResult.Completed.Success(productId) }, @@ -160,7 +161,7 @@ class BillingPaymentRepository( if (it.value.isNotEmpty()) { it.right() } else { - Logger.e("PlayPurchasePaymentToken is empty") + Logger.e("Obfuscated account id is empty") PlayPurchaseInitError.OtherError.left() } } @@ -173,6 +174,10 @@ class BillingPaymentRepository( Logger.e("Purchase has no products") PlayPurchaseVerifyError.OtherError } + ensure(purchase.accountIdentifiers?.obfuscatedAccountId != null) { + Logger.e("Purchase is missing obfuscatedAccountId") + PlayPurchaseVerifyError.OtherError + } ensure(purchase.purchaseToken.isNotEmpty()) { Logger.e("Purchase has no purchase token") PlayPurchaseVerifyError.OtherError 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 78ab7a6f35..463188837c 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 @@ -28,6 +28,7 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import net.mullvad.mullvadvpn.lib.billing.model.BillingException import net.mullvad.mullvadvpn.lib.billing.model.PurchaseEvent +import net.mullvad.mullvadvpn.lib.model.PlayExternalObfuscatedAccountId class BillingRepository(context: Context) { @@ -118,13 +119,13 @@ class BillingRepository(context: Context) { suspend fun startPurchaseFlow( productDetails: ProductDetails, - obfuscatedId: String, + obfuscatedId: PlayExternalObfuscatedAccountId, activityProvider: () -> Activity, ): BillingResult { return try { ensureConnected() - if (obfuscatedId.isEmpty()) { + if (obfuscatedId.value.isEmpty()) { Logger.e("Obfuscated id is empty") return BillingResult.newBuilder().setResponseCode(BillingResponseCode.ERROR).build() } @@ -139,7 +140,7 @@ class BillingRepository(context: Context) { val billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) - .setObfuscatedAccountId(obfuscatedId) + .setObfuscatedAccountId(obfuscatedId.value) .build() val activity = activityProvider() 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 97815d41ee..9c0ae33398 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 @@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.lib.billing import app.cash.turbine.test import arrow.core.left import arrow.core.right +import com.android.billingclient.api.AccountIdentifiers import com.android.billingclient.api.BillingClient.BillingResponseCode import com.android.billingclient.api.BillingResult import com.android.billingclient.api.ProductDetails @@ -19,8 +20,8 @@ import kotlinx.coroutines.test.runTest import net.mullvad.mullvadvpn.lib.billing.extension.toPaymentProduct import net.mullvad.mullvadvpn.lib.billing.model.PurchaseEvent import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule +import net.mullvad.mullvadvpn.lib.model.PlayExternalObfuscatedAccountId import net.mullvad.mullvadvpn.lib.model.PlayPurchaseInitError -import net.mullvad.mullvadvpn.lib.model.PlayPurchasePaymentToken import net.mullvad.mullvadvpn.lib.model.PlayPurchaseVerifyError import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct @@ -197,7 +198,7 @@ class BillingPaymentRepositoryTest { coEvery { mockBillingRepository.queryProducts(listOf(mockProductId.value)) } returns mockProductDetailsResult coEvery { mockPlayPurchaseRepository.initializePlayPurchase() } returns - PlayPurchasePaymentToken("").right() + PlayExternalObfuscatedAccountId("").right() // Act, Assert paymentRepository.purchaseProduct(mockProductId, mockk()).test { @@ -233,7 +234,7 @@ class BillingPaymentRepositoryTest { ) } returns mockBillingResult coEvery { mockPlayPurchaseRepository.initializePlayPurchase() } returns - PlayPurchasePaymentToken("MOCK").right() + PlayExternalObfuscatedAccountId("MOCK").right() // Act, Assert paymentRepository.purchaseProduct(mockProductId, mockk()).test { @@ -257,7 +258,7 @@ class BillingPaymentRepositoryTest { every { mockProductDetailsResult.productDetailsList } returns listOf(mockProductDetails) coEvery { mockBillingRepository.queryProducts(listOf(mockProductId.value)) } returns mockProductDetailsResult - val mockObfuscatedId = "MOCK-ID" + val mockObfuscatedId = PlayExternalObfuscatedAccountId("MOCK-ID") val mockBillingResult: BillingResult = mockk() every { mockBillingResult.responseCode } returns BillingResponseCode.OK coEvery { @@ -268,7 +269,7 @@ class BillingPaymentRepositoryTest { ) } returns mockBillingResult coEvery { mockPlayPurchaseRepository.initializePlayPurchase() } returns - PlayPurchasePaymentToken(mockObfuscatedId).right() + mockObfuscatedId.right() // Act, Assert paymentRepository.purchaseProduct(mockProductId, mockk()).test { @@ -298,10 +299,13 @@ class BillingPaymentRepositoryTest { val mockPurchaseToken = "TOKEN" val mockBillingPurchase: Purchase = mockk() val mockBillingResult: BillingResult = mockk() + val mockAccountIdentifiers: AccountIdentifiers = mockk() every { mockBillingPurchase.purchaseState } returns Purchase.PurchaseState.PURCHASED every { mockBillingResult.responseCode } returns BillingResponseCode.OK every { mockBillingPurchase.products } returns listOf(mockProductId.value) every { mockBillingPurchase.purchaseToken } returns mockPurchaseToken + every { mockBillingPurchase.accountIdentifiers } returns mockAccountIdentifiers + every { mockAccountIdentifiers.obfuscatedAccountId } returns "Something" coEvery { mockBillingRepository.startPurchaseFlow( productDetails = any(), @@ -310,7 +314,7 @@ class BillingPaymentRepositoryTest { ) } returns mockBillingResult coEvery { mockPlayPurchaseRepository.initializePlayPurchase() } returns - PlayPurchasePaymentToken("MOCK-ID").right() + PlayExternalObfuscatedAccountId("MOCK-ID").right() coEvery { mockPlayPurchaseRepository.verifyPlayPurchase(any()) } returns PlayPurchaseVerifyError.OtherError.left() @@ -341,10 +345,13 @@ class BillingPaymentRepositoryTest { val mockPurchaseToken = "TOKEN" val mockBillingPurchase: Purchase = mockk() val mockBillingResult: BillingResult = mockk() + val mockAccountIdentifiers: AccountIdentifiers = mockk() every { mockBillingPurchase.purchaseState } returns Purchase.PurchaseState.PURCHASED every { mockBillingResult.responseCode } returns BillingResponseCode.OK every { mockBillingPurchase.products } returns listOf(mockProductId.value) every { mockBillingPurchase.purchaseToken } returns mockPurchaseToken + every { mockBillingPurchase.accountIdentifiers } returns mockAccountIdentifiers + every { mockAccountIdentifiers.obfuscatedAccountId } returns "Something" coEvery { mockBillingRepository.startPurchaseFlow( productDetails = any(), @@ -353,7 +360,7 @@ class BillingPaymentRepositoryTest { ) } returns mockBillingResult coEvery { mockPlayPurchaseRepository.initializePlayPurchase() } returns - PlayPurchasePaymentToken("MOCK").right() + PlayExternalObfuscatedAccountId("MOCK").right() coEvery { mockPlayPurchaseRepository.verifyPlayPurchase(any()) } returns Unit.right() // Act, Assert @@ -395,7 +402,7 @@ class BillingPaymentRepositoryTest { ) } returns mockBillingResult coEvery { mockPlayPurchaseRepository.initializePlayPurchase() } returns - PlayPurchasePaymentToken("MOCK").right() + PlayExternalObfuscatedAccountId("MOCK").right() // Act, Assert paymentRepository.purchaseProduct(mockProductId, mockk()).test { 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 bce573a6b6..cdb59430b0 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 @@ -34,6 +34,7 @@ import net.mullvad.mullvadvpn.lib.billing.model.BillingException import net.mullvad.mullvadvpn.lib.billing.model.PurchaseEvent import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.lib.common.test.assertLists +import net.mullvad.mullvadvpn.lib.model.PlayExternalObfuscatedAccountId import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -153,7 +154,7 @@ class BillingRepositoryTest { // Arrange val mockProductBillingResult: BillingResult = mockk() val mockBillingResult: BillingResult = mockk() - val transactionId = "MOCK22" + val obfuscatedAccountId = PlayExternalObfuscatedAccountId("MOCK22") val mockProductDetails: ProductDetails = mockk(relaxed = true) val mockActivityProvider: () -> Activity = mockk() every { mockBillingResult.responseCode } returns BillingResponseCode.OK @@ -169,7 +170,7 @@ class BillingRepositoryTest { val result = billingRepository.startPurchaseFlow( mockProductDetails, - transactionId, + obfuscatedAccountId, mockActivityProvider, ) @@ -182,7 +183,7 @@ class BillingRepositoryTest { runTest { // Arrange val mockBillingResult: BillingResult = mockk() - val transactionId = "MOCK22" + val transactionId = PlayExternalObfuscatedAccountId("MOCK22") val mockProductDetails: ProductDetails = mockk(relaxed = true) val mockActivityProvider: () -> Activity = mockk() every { mockBillingResult.responseCode } returns BillingResponseCode.BILLING_UNAVAILABLE @@ -208,7 +209,7 @@ class BillingRepositoryTest { @Test fun `starting purchase flow with empty transaction id should return error`() = runTest { // Arrange - val transactionId = "" + val transactionId = PlayExternalObfuscatedAccountId("") val mockProductDetails: ProductDetails = mockk(relaxed = true) val mockActivityProvider: () -> Activity = mockk() every { mockBillingClient.isReady } returns true diff --git a/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/ManagementService.kt b/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/ManagementService.kt index e0522df9dd..05266556f7 100644 --- a/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/ManagementService.kt +++ b/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/ManagementService.kt @@ -93,9 +93,9 @@ import net.mullvad.mullvadvpn.lib.model.NewAccessMethodSetting import net.mullvad.mullvadvpn.lib.model.ObfuscationMode import net.mullvad.mullvadvpn.lib.model.ObfuscationSettings import net.mullvad.mullvadvpn.lib.model.Ownership as ModelOwnership +import net.mullvad.mullvadvpn.lib.model.PlayExternalObfuscatedAccountId 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.model.PlayPurchaseVerifyError import net.mullvad.mullvadvpn.lib.model.Port import net.mullvad.mullvadvpn.lib.model.Providers @@ -796,7 +796,8 @@ class ManagementService( } } - suspend fun initializePlayPurchase(): Either<PlayPurchaseInitError, PlayPurchasePaymentToken> = + suspend fun initializePlayPurchase(): + Either<PlayPurchaseInitError, PlayExternalObfuscatedAccountId> = Either.catch { grpc.initPlayPurchase(Empty.getDefaultInstance()).toDomain() } .onLeft { Logger.e("Initialize play purchase error") } .mapLeft { PlayPurchaseInitError.OtherError } diff --git a/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/mapper/ToDomain.kt b/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/mapper/ToDomain.kt index 91924b4d35..96b9cab684 100644 --- a/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/mapper/ToDomain.kt +++ b/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/mapper/ToDomain.kt @@ -55,7 +55,7 @@ import net.mullvad.mullvadvpn.lib.model.ObfuscationSettings import net.mullvad.mullvadvpn.lib.model.ObfuscationType import net.mullvad.mullvadvpn.lib.model.Ownership import net.mullvad.mullvadvpn.lib.model.ParameterGenerationError -import net.mullvad.mullvadvpn.lib.model.PlayPurchasePaymentToken +import net.mullvad.mullvadvpn.lib.model.PlayExternalObfuscatedAccountId import net.mullvad.mullvadvpn.lib.model.Port import net.mullvad.mullvadvpn.lib.model.PortRange import net.mullvad.mullvadvpn.lib.model.ProviderId @@ -670,8 +670,8 @@ internal fun ManagementInterface.SplitTunnelSettings.toDomain(): SplitTunnelSett excludedApps = appsList.map { AppId(it) }.toSet(), ) -internal fun ManagementInterface.PlayPurchasePaymentToken.toDomain(): PlayPurchasePaymentToken = - PlayPurchasePaymentToken(value = token) +internal fun ManagementInterface.PlayExternalObfuscatedAccountId.toDomain(): + PlayExternalObfuscatedAccountId = PlayExternalObfuscatedAccountId(value = id) internal fun ManagementInterface.ApiAccessMethodSettings.toDomain(): List<ApiAccessMethodSetting> = buildList { diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PlayExternalObfuscatedAccountId.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PlayExternalObfuscatedAccountId.kt new file mode 100644 index 0000000000..9404d2b6d0 --- /dev/null +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PlayExternalObfuscatedAccountId.kt @@ -0,0 +1,3 @@ +package net.mullvad.mullvadvpn.lib.model + +@JvmInline value class PlayExternalObfuscatedAccountId(val value: String) diff --git a/desktop/packages/management-interface/dist/management_interface_grpc_pb.d.ts b/desktop/packages/management-interface/dist/management_interface_grpc_pb.d.ts index 51d3363bb6..6bbf42de4d 100644 --- a/desktop/packages/management-interface/dist/management_interface_grpc_pb.d.ts +++ b/desktop/packages/management-interface/dist/management_interface_grpc_pb.d.ts @@ -762,14 +762,14 @@ interface IManagementServiceService_IGetExcludedProcesses extends grpc.MethodDef responseSerialize: grpc.serialize<management_interface_pb.ExcludedProcessList>; responseDeserialize: grpc.deserialize<management_interface_pb.ExcludedProcessList>; } -interface IManagementServiceService_IInitPlayPurchase extends grpc.MethodDefinition<google_protobuf_empty_pb.Empty, management_interface_pb.PlayPurchasePaymentToken> { +interface IManagementServiceService_IInitPlayPurchase extends grpc.MethodDefinition<google_protobuf_empty_pb.Empty, management_interface_pb.PlayExternalObfuscatedAccountId> { path: "/mullvad_daemon.management_interface.ManagementService/InitPlayPurchase"; requestStream: false; responseStream: false; requestSerialize: grpc.serialize<google_protobuf_empty_pb.Empty>; requestDeserialize: grpc.deserialize<google_protobuf_empty_pb.Empty>; - responseSerialize: grpc.serialize<management_interface_pb.PlayPurchasePaymentToken>; - responseDeserialize: grpc.deserialize<management_interface_pb.PlayPurchasePaymentToken>; + responseSerialize: grpc.serialize<management_interface_pb.PlayExternalObfuscatedAccountId>; + responseDeserialize: grpc.deserialize<management_interface_pb.PlayExternalObfuscatedAccountId>; } interface IManagementServiceService_IVerifyPlayPurchase extends grpc.MethodDefinition<management_interface_pb.PlayPurchase, google_protobuf_empty_pb.Empty> { path: "/mullvad_daemon.management_interface.ManagementService/VerifyPlayPurchase"; @@ -1001,7 +1001,7 @@ export interface IManagementServiceServer extends grpc.UntypedServiceImplementat setSplitTunnelState: grpc.handleUnaryCall<google_protobuf_wrappers_pb.BoolValue, google_protobuf_empty_pb.Empty>; clearSplitTunnelApps: grpc.handleUnaryCall<google_protobuf_empty_pb.Empty, google_protobuf_empty_pb.Empty>; getExcludedProcesses: grpc.handleUnaryCall<google_protobuf_empty_pb.Empty, management_interface_pb.ExcludedProcessList>; - initPlayPurchase: grpc.handleUnaryCall<google_protobuf_empty_pb.Empty, management_interface_pb.PlayPurchasePaymentToken>; + initPlayPurchase: grpc.handleUnaryCall<google_protobuf_empty_pb.Empty, management_interface_pb.PlayExternalObfuscatedAccountId>; verifyPlayPurchase: grpc.handleUnaryCall<management_interface_pb.PlayPurchase, google_protobuf_empty_pb.Empty>; needFullDiskPermissions: grpc.handleUnaryCall<google_protobuf_empty_pb.Empty, google_protobuf_wrappers_pb.BoolValue>; checkVolumes: grpc.handleUnaryCall<google_protobuf_empty_pb.Empty, google_protobuf_empty_pb.Empty>; @@ -1239,9 +1239,9 @@ export interface IManagementServiceClient { getExcludedProcesses(request: google_protobuf_empty_pb.Empty, callback: (error: grpc.ServiceError | null, response: management_interface_pb.ExcludedProcessList) => void): grpc.ClientUnaryCall; getExcludedProcesses(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: management_interface_pb.ExcludedProcessList) => void): grpc.ClientUnaryCall; getExcludedProcesses(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: management_interface_pb.ExcludedProcessList) => void): grpc.ClientUnaryCall; - initPlayPurchase(request: google_protobuf_empty_pb.Empty, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayPurchasePaymentToken) => void): grpc.ClientUnaryCall; - initPlayPurchase(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayPurchasePaymentToken) => void): grpc.ClientUnaryCall; - initPlayPurchase(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayPurchasePaymentToken) => void): grpc.ClientUnaryCall; + initPlayPurchase(request: google_protobuf_empty_pb.Empty, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayExternalObfuscatedAccountId) => void): grpc.ClientUnaryCall; + initPlayPurchase(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayExternalObfuscatedAccountId) => void): grpc.ClientUnaryCall; + initPlayPurchase(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayExternalObfuscatedAccountId) => void): grpc.ClientUnaryCall; verifyPlayPurchase(request: management_interface_pb.PlayPurchase, callback: (error: grpc.ServiceError | null, response: google_protobuf_empty_pb.Empty) => void): grpc.ClientUnaryCall; verifyPlayPurchase(request: management_interface_pb.PlayPurchase, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: google_protobuf_empty_pb.Empty) => void): grpc.ClientUnaryCall; verifyPlayPurchase(request: management_interface_pb.PlayPurchase, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: google_protobuf_empty_pb.Empty) => void): grpc.ClientUnaryCall; @@ -1512,9 +1512,9 @@ export class ManagementServiceClient extends grpc.Client implements IManagementS public getExcludedProcesses(request: google_protobuf_empty_pb.Empty, callback: (error: grpc.ServiceError | null, response: management_interface_pb.ExcludedProcessList) => void): grpc.ClientUnaryCall; public getExcludedProcesses(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: management_interface_pb.ExcludedProcessList) => void): grpc.ClientUnaryCall; public getExcludedProcesses(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: management_interface_pb.ExcludedProcessList) => void): grpc.ClientUnaryCall; - public initPlayPurchase(request: google_protobuf_empty_pb.Empty, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayPurchasePaymentToken) => void): grpc.ClientUnaryCall; - public initPlayPurchase(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayPurchasePaymentToken) => void): grpc.ClientUnaryCall; - public initPlayPurchase(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayPurchasePaymentToken) => void): grpc.ClientUnaryCall; + public initPlayPurchase(request: google_protobuf_empty_pb.Empty, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayExternalObfuscatedAccountId) => void): grpc.ClientUnaryCall; + public initPlayPurchase(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayExternalObfuscatedAccountId) => void): grpc.ClientUnaryCall; + public initPlayPurchase(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: management_interface_pb.PlayExternalObfuscatedAccountId) => void): grpc.ClientUnaryCall; public verifyPlayPurchase(request: management_interface_pb.PlayPurchase, callback: (error: grpc.ServiceError | null, response: google_protobuf_empty_pb.Empty) => void): grpc.ClientUnaryCall; public verifyPlayPurchase(request: management_interface_pb.PlayPurchase, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: google_protobuf_empty_pb.Empty) => void): grpc.ClientUnaryCall; public verifyPlayPurchase(request: management_interface_pb.PlayPurchase, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: google_protobuf_empty_pb.Empty) => void): grpc.ClientUnaryCall; diff --git a/desktop/packages/management-interface/dist/management_interface_grpc_pb.js b/desktop/packages/management-interface/dist/management_interface_grpc_pb.js index f07ed529ce..a6664a655a 100644 --- a/desktop/packages/management-interface/dist/management_interface_grpc_pb.js +++ b/desktop/packages/management-interface/dist/management_interface_grpc_pb.js @@ -316,26 +316,26 @@ function deserialize_mullvad_daemon_management_interface_ObfuscationSettings(buf return management_interface_pb.ObfuscationSettings.deserializeBinary(new Uint8Array(buffer_arg)); } -function serialize_mullvad_daemon_management_interface_PlayPurchase(arg) { - if (!(arg instanceof management_interface_pb.PlayPurchase)) { - throw new Error('Expected argument of type mullvad_daemon.management_interface.PlayPurchase'); +function serialize_mullvad_daemon_management_interface_PlayExternalObfuscatedAccountId(arg) { + if (!(arg instanceof management_interface_pb.PlayExternalObfuscatedAccountId)) { + throw new Error('Expected argument of type mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId'); } return Buffer.from(arg.serializeBinary()); } -function deserialize_mullvad_daemon_management_interface_PlayPurchase(buffer_arg) { - return management_interface_pb.PlayPurchase.deserializeBinary(new Uint8Array(buffer_arg)); +function deserialize_mullvad_daemon_management_interface_PlayExternalObfuscatedAccountId(buffer_arg) { + return management_interface_pb.PlayExternalObfuscatedAccountId.deserializeBinary(new Uint8Array(buffer_arg)); } -function serialize_mullvad_daemon_management_interface_PlayPurchasePaymentToken(arg) { - if (!(arg instanceof management_interface_pb.PlayPurchasePaymentToken)) { - throw new Error('Expected argument of type mullvad_daemon.management_interface.PlayPurchasePaymentToken'); +function serialize_mullvad_daemon_management_interface_PlayPurchase(arg) { + if (!(arg instanceof management_interface_pb.PlayPurchase)) { + throw new Error('Expected argument of type mullvad_daemon.management_interface.PlayPurchase'); } return Buffer.from(arg.serializeBinary()); } -function deserialize_mullvad_daemon_management_interface_PlayPurchasePaymentToken(buffer_arg) { - return management_interface_pb.PlayPurchasePaymentToken.deserializeBinary(new Uint8Array(buffer_arg)); +function deserialize_mullvad_daemon_management_interface_PlayPurchase(buffer_arg) { + return management_interface_pb.PlayPurchase.deserializeBinary(new Uint8Array(buffer_arg)); } function serialize_mullvad_daemon_management_interface_PublicKey(arg) { @@ -1290,11 +1290,11 @@ initPlayPurchase: { requestStream: false, responseStream: false, requestType: google_protobuf_empty_pb.Empty, - responseType: management_interface_pb.PlayPurchasePaymentToken, + responseType: management_interface_pb.PlayExternalObfuscatedAccountId, requestSerialize: serialize_google_protobuf_Empty, requestDeserialize: deserialize_google_protobuf_Empty, - responseSerialize: serialize_mullvad_daemon_management_interface_PlayPurchasePaymentToken, - responseDeserialize: deserialize_mullvad_daemon_management_interface_PlayPurchasePaymentToken, + responseSerialize: serialize_mullvad_daemon_management_interface_PlayExternalObfuscatedAccountId, + responseDeserialize: deserialize_mullvad_daemon_management_interface_PlayExternalObfuscatedAccountId, }, verifyPlayPurchase: { path: '/mullvad_daemon.management_interface.ManagementService/VerifyPlayPurchase', diff --git a/desktop/packages/management-interface/dist/management_interface_pb.d.ts b/desktop/packages/management-interface/dist/management_interface_pb.d.ts index 7bf62a78f9..0abc998706 100644 --- a/desktop/packages/management-interface/dist/management_interface_pb.d.ts +++ b/desktop/packages/management-interface/dist/management_interface_pb.d.ts @@ -3228,6 +3228,26 @@ export namespace LeakInfo { } } +export class PlayExternalObfuscatedAccountId extends jspb.Message { + getId(): string; + setId(value: string): PlayExternalObfuscatedAccountId; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): PlayExternalObfuscatedAccountId.AsObject; + static toObject(includeInstance: boolean, msg: PlayExternalObfuscatedAccountId): PlayExternalObfuscatedAccountId.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>}; + static serializeBinaryToWriter(message: PlayExternalObfuscatedAccountId, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): PlayExternalObfuscatedAccountId; + static deserializeBinaryFromReader(message: PlayExternalObfuscatedAccountId, reader: jspb.BinaryReader): PlayExternalObfuscatedAccountId; +} + +export namespace PlayExternalObfuscatedAccountId { + export type AsObject = { + id: string, + } +} + export class PlayPurchase extends jspb.Message { getProductId(): string; setProductId(value: string): PlayPurchase; diff --git a/desktop/packages/management-interface/dist/management_interface_pb.js b/desktop/packages/management-interface/dist/management_interface_pb.js index a8375479f3..cc5e4f8bc8 100644 --- a/desktop/packages/management-interface/dist/management_interface_pb.js +++ b/desktop/packages/management-interface/dist/management_interface_pb.js @@ -111,6 +111,7 @@ goog.exportSymbol('proto.mullvad_daemon.management_interface.ObfuscationSettings goog.exportSymbol('proto.mullvad_daemon.management_interface.ObfuscationSettings.Udp2TcpObfuscation', null, global); goog.exportSymbol('proto.mullvad_daemon.management_interface.ObfuscationSettings.WireguardPort', null, global); goog.exportSymbol('proto.mullvad_daemon.management_interface.Ownership', null, global); +goog.exportSymbol('proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId', null, global); goog.exportSymbol('proto.mullvad_daemon.management_interface.PlayPurchase', null, global); goog.exportSymbol('proto.mullvad_daemon.management_interface.PlayPurchasePaymentToken', null, global); goog.exportSymbol('proto.mullvad_daemon.management_interface.PortRange', null, global); @@ -2291,6 +2292,27 @@ if (goog.DEBUG && !COMPILED) { * @extends {jspb.Message} * @constructor */ +proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.displayName = 'proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ proto.mullvad_daemon.management_interface.PlayPurchase = function(opt_data) { jspb.Message.initialize(this, opt_data, 0, -1, null, null); }; @@ -24916,6 +24938,136 @@ if (jspb.Message.GENERATE_TO_OBJECT) { * http://goto/soy-param-migration * @return {!Object} */ +proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.prototype.toObject = function(opt_includeInstance) { + return proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.toObject = function(includeInstance, msg) { + var f, obj = { + id: jspb.Message.getFieldWithDefault(msg, 1, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId} + */ +proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId; + return proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId} + */ +proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setId(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string id = 1; + * @return {string} + */ +proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.prototype.getId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId} returns this + */ +proto.mullvad_daemon.management_interface.PlayExternalObfuscatedAccountId.prototype.setId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ proto.mullvad_daemon.management_interface.PlayPurchase.prototype.toObject = function(opt_includeInstance) { return proto.mullvad_daemon.management_interface.PlayPurchase.toObject(opt_includeInstance, this); }; diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs index 5761d79ac5..c3feee218f 100644 --- a/mullvad-api/src/lib.rs +++ b/mullvad-api/src/lib.rs @@ -5,7 +5,7 @@ use futures::channel::mpsc; use hyper::body::Incoming; use mullvad_types::account::{AccountData, AccountNumber, VoucherSubmission}; #[cfg(target_os = "android")] -use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; +use mullvad_types::account::{PlayExternalObfuscatedAccountId, PlayPurchase}; use proxy::{ApiConnectionMode, ConnectionModeProvider}; use std::{collections::BTreeMap, future::Future, io, net::SocketAddr, path::Path, sync::Arc}; use talpid_types::ErrorExt; @@ -642,7 +642,7 @@ impl AccountsProxy { pub fn init_play_purchase( &mut self, account: AccountNumber, - ) -> impl Future<Output = Result<PlayPurchasePaymentToken, rest::Error>> + use<> { + ) -> impl Future<Output = Result<PlayExternalObfuscatedAccountId, rest::Error>> + use<> { #[derive(serde::Deserialize)] struct PlayPurchaseInitResponse { obfuscated_id: String, diff --git a/mullvad-daemon/src/device/mod.rs b/mullvad-daemon/src/device/mod.rs index 535c11c83d..6e14efbc3d 100644 --- a/mullvad-daemon/src/device/mod.rs +++ b/mullvad-daemon/src/device/mod.rs @@ -6,7 +6,9 @@ use futures::{ use mullvad_api::rest; #[cfg(target_os = "android")] -use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; +use mullvad_types::account::{ + PlayExternalObfuscatedAccountId, PlayPurchase, PlayPurchasePaymentToken, +}; use mullvad_types::{ account::{AccountNumber, VoucherSubmission}, device::{ @@ -304,7 +306,7 @@ enum AccountManagerCommand { ValidateDevice(ResponseTx<()>), SubmitVoucher(String, ResponseTx<VoucherSubmission>), #[cfg(target_os = "android")] - InitPlayPurchase(ResponseTx<PlayPurchasePaymentToken>), + InitPlayPurchase(ResponseTx<PlayExternalObfuscatedAccountId>), #[cfg(target_os = "android")] VerifyPlayPurchase(ResponseTx<()>, PlayPurchase), CheckExpiry(ResponseTx<DateTime<Utc>>), @@ -368,7 +370,7 @@ impl AccountManagerHandle { } #[cfg(target_os = "android")] - pub async fn init_play_purchase(&self) -> Result<PlayPurchasePaymentToken, Error> { + pub async fn init_play_purchase(&self) -> Result<PlayExternalObfuscatedAccountId, Error> { self.send_command(AccountManagerCommand::InitPlayPurchase) .await } diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index ff4a6906d4..becdd3039c 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -49,7 +49,7 @@ use mullvad_api::{ use mullvad_encrypted_dns_proxy::state::EncryptedDnsProxyState; use mullvad_relay_selector::{Config, RelaySelector}; #[cfg(target_os = "android")] -use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; +use mullvad_types::account::{PlayExternalObfuscatedAccountId, PlayPurchase}; #[cfg(any(target_os = "windows", target_os = "android", target_os = "macos"))] use mullvad_types::settings::SplitApp; #[cfg(daita)] @@ -418,7 +418,7 @@ pub enum DaemonCommand { BypassSocket(RawFd, oneshot::Sender<()>), /// Initialize a google play purchase through the API. #[cfg(target_os = "android")] - InitPlayPurchase(ResponseTx<PlayPurchasePaymentToken, Error>), + InitPlayPurchase(ResponseTx<PlayExternalObfuscatedAccountId, Error>), /// Verify that a google play payment was successful through the API. #[cfg(target_os = "android")] VerifyPlayPurchase(ResponseTx<(), Error>, PlayPurchase), @@ -3392,7 +3392,7 @@ impl Daemon { } #[cfg(target_os = "android")] - fn on_init_play_purchase(&mut self, tx: ResponseTx<PlayPurchasePaymentToken, Error>) { + fn on_init_play_purchase(&mut self, tx: ResponseTx<PlayExternalObfuscatedAccountId, Error>) { let manager = self.account_manager.clone(); tokio::spawn(async move { Self::oneshot_send( diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index fcb24ded90..d26adb2812 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -1088,19 +1088,19 @@ impl ManagementService for ManagementServiceImpl { async fn init_play_purchase( &self, _request: Request<()>, - ) -> ServiceResult<types::PlayPurchasePaymentToken> { + ) -> ServiceResult<types::PlayExternalObfuscatedAccountId> { log::debug!("init_play_purchase"); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::InitPlayPurchase(tx))?; - let payment_token = self + let external_obufscated_account_id = self .wait_for_result(rx) .await? - .map(types::PlayPurchasePaymentToken::from) + .map(types::PlayExternalObfuscatedAccountId::from) .map_err(map_daemon_error)?; - Ok(Response::new(payment_token)) + Ok(Response::new(external_obufscated_account_id)) } /// On non-Android platforms, the return value will be useless. @@ -1108,10 +1108,10 @@ impl ManagementService for ManagementServiceImpl { async fn init_play_purchase( &self, _: Request<()>, - ) -> ServiceResult<types::PlayPurchasePaymentToken> { + ) -> ServiceResult<types::PlayExternalObfuscatedAccountId> { log::error!("Called `init_play_purchase` on non-Android platform"); - Ok(Response::new(types::PlayPurchasePaymentToken { - token: String::default(), + Ok(Response::new(types::PlayExternalObfuscatedAccountId { + id: String::default(), })) } diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index 79bd7474ab..14fc1800b1 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -118,7 +118,7 @@ service ManagementService { rpc GetExcludedProcesses(google.protobuf.Empty) returns (ExcludedProcessList) {} // Play payment (Android) - rpc InitPlayPurchase(google.protobuf.Empty) returns (PlayPurchasePaymentToken) {} + rpc InitPlayPurchase(google.protobuf.Empty) returns (PlayExternalObfuscatedAccountId) {} rpc VerifyPlayPurchase(PlayPurchase) returns (google.protobuf.Empty) {} // Check whether the app needs TCC approval for split tunneling (macOS) @@ -850,6 +850,8 @@ message LeakInfo { string interface = 2; } +message PlayExternalObfuscatedAccountId { string id = 1; } + message PlayPurchase { string product_id = 1; PlayPurchasePaymentToken purchase_token = 2; diff --git a/mullvad-management-interface/src/types/conversions/account.rs b/mullvad-management-interface/src/types/conversions/account.rs index 8354658e6e..262811bdaf 100644 --- a/mullvad-management-interface/src/types/conversions/account.rs +++ b/mullvad-management-interface/src/types/conversions/account.rs @@ -2,7 +2,9 @@ use crate::types; use chrono::DateTime; use mullvad_types::account::{AccountData, VoucherSubmission}; #[cfg(target_os = "android")] -use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; +use mullvad_types::account::{ + PlayExternalObfuscatedAccountId, PlayPurchase, PlayPurchasePaymentToken, +}; use super::FromProtobufTypeError; @@ -92,3 +94,10 @@ impl From<PlayPurchasePaymentToken> for types::PlayPurchasePaymentToken { Self { token } } } + +#[cfg(target_os = "android")] +impl From<PlayExternalObfuscatedAccountId> for types::PlayExternalObfuscatedAccountId { + fn from(id: PlayExternalObfuscatedAccountId) -> Self { + Self { id } + } +} diff --git a/mullvad-types/src/account.rs b/mullvad-types/src/account.rs index 9940bbb657..5460c5fa46 100644 --- a/mullvad-types/src/account.rs +++ b/mullvad-types/src/account.rs @@ -10,9 +10,12 @@ pub type AccessToken = String; /// Account identifier (not used for authentication). pub type AccountId = String; -/// The payment token returned by initiating a google play purchase. +/// The external obfuscated id returned by initiating a google play purchase. /// In the API this is called the `obfuscated_id`. #[cfg(target_os = "android")] +pub type PlayExternalObfuscatedAccountId = String; + +#[cfg(target_os = "android")] pub type PlayPurchasePaymentToken = String; /// Account expiration info returned by the API via `/v1/me`. |
