diff --git a/android/lib/http-client/build.gradle.kts b/android/lib/http-client/build.gradle.kts
new file mode 100644
index 000000000000..3f1f9e66b275
--- /dev/null
+++ b/android/lib/http-client/build.gradle.kts
@@ -0,0 +1,34 @@
+plugins {
+ id(Dependencies.Plugin.androidLibraryId)
+ id(Dependencies.Plugin.kotlinAndroidId)
+}
+
+android {
+ namespace = "net.mullvad.mullvadvpn.lib.http"
+ compileSdk = Versions.Android.compileSdkVersion
+
+ defaultConfig {
+ minSdk = Versions.Android.minSdkVersion
+ }
+
+ 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
+ }
+}
+
+dependencies {
+ implementation(Dependencies.Kotlin.stdlib)
+ implementation(Dependencies.KotlinX.coroutinesAndroid)
+ implementation(Dependencies.androidVolley)
+}
diff --git a/android/lib/http-client/src/main/AndroidManifest.xml b/android/lib/http-client/src/main/AndroidManifest.xml
new file mode 100644
index 000000000000..8bdb7e14b389
--- /dev/null
+++ b/android/lib/http-client/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/android/lib/http-client/src/main/java/net/mullvad/mullvadvpn/lib/http/MullvadHttpClient.kt b/android/lib/http-client/src/main/java/net/mullvad/mullvadvpn/lib/http/MullvadHttpClient.kt
new file mode 100644
index 000000000000..4257ff2a6720
--- /dev/null
+++ b/android/lib/http-client/src/main/java/net/mullvad/mullvadvpn/lib/http/MullvadHttpClient.kt
@@ -0,0 +1,94 @@
+package net.mullvad.mullvadvpn.lib.http
+
+import android.content.Context
+import android.util.Log
+import com.android.volley.Request
+import com.android.volley.toolbox.JsonObjectRequest
+import com.android.volley.toolbox.RequestFuture
+import com.android.volley.toolbox.Volley
+import org.json.JSONObject
+
+/**
+ * This class should primarily be used to make calls to the api during the early stages of
+ * implementing a new endpoint. These calls should then be migrated to the daemon and this class
+ * should not be used outside of this narrow scope.
+ */
+class MullvadHttpClient(context: Context) {
+ private val queue = Volley.newRequestQueue(context)
+
+ fun newPurchase(accountToken: String): String? {
+ val authToken = login(accountToken)
+ val response =
+ sendSimpleSynchronousRequest(
+ method = Request.Method.POST,
+ url = NEW_PURCHASE_URL,
+ token = authToken
+ )
+ return response?.getString("obfuscated_external_account_id")
+ }
+
+ fun acknowledgePurchase(
+ accountToken: String,
+ productId: String,
+ purchaseToken: String
+ ): Boolean {
+ val authToken = login(accountToken)
+ val response =
+ sendSimpleSynchronousRequest(
+ method = Request.Method.POST,
+ url = ACKNOWLEDGE_PURCHASE_URL,
+ token = authToken,
+ body =
+ JSONObject().apply {
+ put("product_id", productId)
+ put("token", purchaseToken)
+ }
+ )
+ return response != null
+ }
+
+ private fun login(accountToken: String): String {
+ val json = JSONObject().apply { put("account_number", accountToken) }
+ return sendSimpleSynchronousRequest(Request.Method.POST, AUTH_URL, json)
+ ?.getString("access_token")
+ ?: ""
+ }
+
+ private fun sendSimpleSynchronousRequest(
+ method: Int,
+ url: String,
+ body: JSONObject? = null,
+ token: String? = null
+ ): JSONObject? {
+ val future = RequestFuture.newFuture()
+ val request =
+ object : JsonObjectRequest(method, url, body, future, future) {
+ override fun getHeaders(): MutableMap {
+ val headers = HashMap()
+ if (body != null) {
+ headers.put("Content-Type", "application/json")
+ }
+ if (token != null) {
+ headers.put("Authorization", "Bearer $token")
+ }
+ return headers
+ }
+ }
+ queue.add(request)
+ return try {
+ future.get()
+ } catch (e: Exception) {
+ Log.e("Error", "Could not login", e)
+ null
+ }
+ }
+
+ companion object {
+ private const val API_BASE_URL = "https://api.mullvad.net"
+ private const val API_VERSION = "v1"
+ private const val AUTH_URL = "$API_BASE_URL/auth/$API_VERSION/token"
+ private const val NEW_PURCHASE_URL = "$API_BASE_URL/payments/google-play/new"
+ private const val ACKNOWLEDGE_PURCHASE_URL =
+ "$API_BASE_URL/payments/google-play/acknowledge"
+ }
+}
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index 5df9c08f1e07..ce2e4890dfd6 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -8,7 +8,8 @@ include(
":lib:resource",
":lib:talpid",
":lib:theme",
- ":lib:common-test"
+ ":lib:common-test",
+ ":lib:http-client"
)
include(":test", ":test:common", ":test:e2e", ":test:mockapi")