From ccbb14f00e4fb54d6936d5eae73da240d534b529 Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Fri, 17 Jul 2026 13:57:32 +0300 Subject: [PATCH 1/4] Migrate to Google Play Billing Library 9.1.0 - Bump billingclient 8.1.0 -> 9.1.0 - Bump compileSdk/targetSdk to 35 (required by PBL 9) - Handle nullable PackageInfo.versionName (nullability tightened in SDK 35) - Fix stale SkuDetails KDoc mention Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016gkK8UCtMzk324y5meDWLw --- nocodes/build.gradle | 4 ++-- sample/build.gradle | 4 ++-- sdk/build.gradle | 6 +++--- sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt | 2 +- .../qonversion/android/sdk/internal/EnvironmentProvider.kt | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/nocodes/build.gradle b/nocodes/build.gradle index 626d2985a..4c3f0c1d8 100644 --- a/nocodes/build.gradle +++ b/nocodes/build.gradle @@ -5,11 +5,11 @@ plugins { android { namespace 'io.qonversion.nocodes' - compileSdk 34 + compileSdk 35 defaultConfig { minSdk 23 - targetSdk 34 + targetSdk 35 group = 'io.qonversion.nocodes' version = nocodes.versionName diff --git a/sample/build.gradle b/sample/build.gradle index ad1279458..ea6aa2d14 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -5,12 +5,12 @@ plugins { android { namespace 'io.qonversion.sample' - compileSdk 34 + compileSdk 35 defaultConfig { applicationId "io.qonversion.sampleapp" minSdk 23 - targetSdk 34 + targetSdk 35 versionCode 1 versionName '1.0.0' multiDexEnabled true diff --git a/sdk/build.gradle b/sdk/build.gradle index 1a0594a67..ad04fe2eb 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -3,11 +3,11 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' android { - compileSdk 34 + compileSdk 35 defaultConfig { minSdkVersion 23 - targetSdkVersion 34 + targetSdkVersion 35 consumerProguardFiles 'consumer-rules.pro' group = 'io.qonversion.android.sdk' @@ -65,7 +65,7 @@ ext { moshiVersion = '1.15.2' retrofit_version = '2.9.0' okhttp_version = '3.12.13' - billing = '8.1.0' + billing = '9.1.0' lifecycle_version = '2.1.0' assertj_version = '3.16.1' junit_version = '5.6.2' diff --git a/sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt b/sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt index bede52ad7..fe35f166a 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt @@ -172,7 +172,7 @@ interface Qonversion { /** * Return Qonversion Products in association with Google Play Store Products - * If you get an empty SkuDetails be sure your products are correctly set up in Google Play Store. + * If you get empty ProductDetails be sure your products are correctly set up in Google Play Store. * @param callback - callback that will be called when response is received * @see [Product Center](https://qonversion.io/docs/product-center) */ diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/EnvironmentProvider.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/EnvironmentProvider.kt index 46d003b58..2bbff7aab 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/EnvironmentProvider.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/EnvironmentProvider.kt @@ -44,7 +44,7 @@ internal class EnvironmentProvider( context.packageManager.getPackageInfo(context.packageName, 0) } - packageInfo.versionName + packageInfo.versionName ?: UNKNOWN } catch (throwable: Throwable) { UNKNOWN } From a26d723a0ef6966db98607463d1c3f89a5e144bc Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Fri, 17 Jul 2026 13:59:14 +0300 Subject: [PATCH 2/4] Drop unused SkuDetails test helpers SkuDetails class was removed in Billing Library 9. The helpers had no usages left in tests. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016gkK8UCtMzk324y5meDWLw --- .../test/java/com/qonversion/android/sdk/utils.kt | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/sdk/src/test/java/com/qonversion/android/sdk/utils.kt b/sdk/src/test/java/com/qonversion/android/sdk/utils.kt index aeb8b22ca..8792a7682 100644 --- a/sdk/src/test/java/com/qonversion/android/sdk/utils.kt +++ b/sdk/src/test/java/com/qonversion/android/sdk/utils.kt @@ -26,16 +26,6 @@ fun Any.getPrivateField(name: String): T { return field.get(this) as T } -@Suppress("DEPRECATION") -fun mockSubsSkuDetails( - sku: String = DEFAULT_SUBS_SKU, - subsPeriod: String = DEFAULT_SUBS_PERIOD, - freeTrialPeriod: String? = null -): SkuDetails { - val skuDetailsJson = mockSubsSkuDetailsJson(sku, subsPeriod, freeTrialPeriod) - return SkuDetails(skuDetailsJson) -} - fun mockSubsSkuDetailsJson( sku: String = DEFAULT_SUBS_SKU, subsPeriod: String = DEFAULT_SUBS_PERIOD, @@ -58,11 +48,6 @@ fun mockIncorrectSubsPurchase(): Purchase { return Purchase("{$SUBS_PURCHASE_INCOMPLETE}", PURCHASE_SIGNATURE) } -@Suppress("DEPRECATION") -fun mockInAppSkuDetails(): SkuDetails { - return SkuDetails(mockInAppSkuDetailsJson()) -} - fun mockInAppSkuDetailsJson( ): String { return "{$INAPP_SKU_DETAILS}" From 9b42c736c385881d1f0eec35573c09137ea2311a Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Fri, 17 Jul 2026 15:02:06 +0300 Subject: [PATCH 3/4] Drop remaining dead SkuDetails test companions mockSubsSkuDetailsJson/mockInAppSkuDetailsJson, their constants and the billingclient wildcard import had no consumers left after removing the SkuDetails-based helpers. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016gkK8UCtMzk324y5meDWLw --- .../java/com/qonversion/android/sdk/utils.kt | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/sdk/src/test/java/com/qonversion/android/sdk/utils.kt b/sdk/src/test/java/com/qonversion/android/sdk/utils.kt index 8792a7682..a31040fe3 100644 --- a/sdk/src/test/java/com/qonversion/android/sdk/utils.kt +++ b/sdk/src/test/java/com/qonversion/android/sdk/utils.kt @@ -1,14 +1,10 @@ package com.qonversion.android.sdk import com.android.billingclient.api.Purchase -import com.android.billingclient.api.* -import com.qonversion.android.sdk.Constants.DEFAULT_SUBS_PERIOD import com.qonversion.android.sdk.Constants.DEFAULT_SUBS_SKU import com.qonversion.android.sdk.Constants.INAPP_PURCHASE -import com.qonversion.android.sdk.Constants.INAPP_SKU_DETAILS import com.qonversion.android.sdk.Constants.SUBS_PURCHASE_INCOMPLETE import com.qonversion.android.sdk.Constants.PURCHASE_SIGNATURE -import com.qonversion.android.sdk.Constants.SUBS_SKU_DETAILS_INCOMPLETE import java.lang.reflect.Modifier fun Any.mockPrivateField(fieldName: String, field: Any?) { @@ -26,20 +22,6 @@ fun Any.getPrivateField(name: String): T { return field.get(this) as T } -fun mockSubsSkuDetailsJson( - sku: String = DEFAULT_SUBS_SKU, - subsPeriod: String = DEFAULT_SUBS_PERIOD, - freeTrialPeriod: String? = null -): String { - val sb = StringBuilder() - sb.append("$SUBS_SKU_DETAILS_INCOMPLETE, \"productId\":\"$sku\", \"subscriptionPeriod\":$subsPeriod") - freeTrialPeriod?.let { - sb.append(",\"freeTrialPeriod\":\"$it\"") - } - - return "{${sb}}" -} - fun mockSubsPurchase(sku: String = DEFAULT_SUBS_SKU): Purchase { return Purchase("{$SUBS_PURCHASE_INCOMPLETE, \"productId\":\"$sku\"}", PURCHASE_SIGNATURE) } @@ -48,11 +30,6 @@ fun mockIncorrectSubsPurchase(): Purchase { return Purchase("{$SUBS_PURCHASE_INCOMPLETE}", PURCHASE_SIGNATURE) } -fun mockInAppSkuDetailsJson( -): String { - return "{$INAPP_SKU_DETAILS}" -} - fun mockInAppPurchase(): Purchase { return Purchase("{$INAPP_PURCHASE}", PURCHASE_SIGNATURE) } @@ -68,30 +45,6 @@ object Constants { "\"autoRenewing\":true," + "\"acknowledged\":true" - // Subscription SkuDetails without productId, subscriptionPeriod, freeTrialPeriod - const val SUBS_SKU_DETAILS_INCOMPLETE = "\"type\":\"subs\"," + - "\"title\":\"Qonversion Subs\"," + - "\"name\":\"Qonversion Subscription Weekly\"," + - "\"price\":\"RUB 439.00\"," + - "\"price_amount_micros\":439000000," + - "\"price_currency_code\":\"RUB\"," + - "\"description\":\"Weekly\"," + - "\"introductoryPriceAmountMicros\":85000000," + - "\"introductoryPricePeriod\":\"P3D\"," + - "\"introductoryPrice\":\"RUB 85.00\"," + - "\"introductoryPriceCycles\":1," + - "\"skuDetailsToken\":\"XXXXXXX\"" - - const val INAPP_SKU_DETAILS = "\"productId\":\"qonversion_inapp_consumable\"," + - "\"type\":\"inapp\"," + - "\"title\":\"Qonversion In-app\"," + - "\"name\":\"Qonversion In-app Consumable\"," + - "\"price\":\"RUB 75.00\"," + - "\"price_amount_micros\":75000000," + - "\"price_currency_code\":\"RUB\"," + - "\"description\":\"Consumable\"," + - "\"skuDetailsToken\":\"XXXXXXX\"}" - const val INAPP_PURCHASE = "\"orderId\":\"GPA.0000-0000-0000-0000\"," + "\"packageName\":\"io.qonversion.sample\"," + "\"productId\":\"qonversion_inapp_consumable\"," + @@ -102,6 +55,5 @@ object Constants { "\"acknowledged\":true" const val PURCHASE_SIGNATURE = "mockSignature" - const val DEFAULT_SUBS_PERIOD = "P1W" const val DEFAULT_SUBS_SKU = "subs_weekly" } \ No newline at end of file From dd0054f6bd1ed019863fdb6be009d53365e7e188 Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Fri, 17 Jul 2026 18:53:32 +0300 Subject: [PATCH 4/4] Drop dead Purchase test helpers mockSubsPurchase/mockInAppPurchase/mockIncorrectSubsPurchase and the whole Constants object had no usages; only the reflection helpers remain in test utils. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016gkK8UCtMzk324y5meDWLw --- .../java/com/qonversion/android/sdk/utils.kt | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/sdk/src/test/java/com/qonversion/android/sdk/utils.kt b/sdk/src/test/java/com/qonversion/android/sdk/utils.kt index a31040fe3..80ba0ff8b 100644 --- a/sdk/src/test/java/com/qonversion/android/sdk/utils.kt +++ b/sdk/src/test/java/com/qonversion/android/sdk/utils.kt @@ -1,10 +1,5 @@ package com.qonversion.android.sdk -import com.android.billingclient.api.Purchase -import com.qonversion.android.sdk.Constants.DEFAULT_SUBS_SKU -import com.qonversion.android.sdk.Constants.INAPP_PURCHASE -import com.qonversion.android.sdk.Constants.SUBS_PURCHASE_INCOMPLETE -import com.qonversion.android.sdk.Constants.PURCHASE_SIGNATURE import java.lang.reflect.Modifier fun Any.mockPrivateField(fieldName: String, field: Any?) { @@ -21,39 +16,3 @@ fun Any.getPrivateField(name: String): T { @Suppress("UNCHECKED_CAST") return field.get(this) as T } - -fun mockSubsPurchase(sku: String = DEFAULT_SUBS_SKU): Purchase { - return Purchase("{$SUBS_PURCHASE_INCOMPLETE, \"productId\":\"$sku\"}", PURCHASE_SIGNATURE) -} - -fun mockIncorrectSubsPurchase(): Purchase { - return Purchase("{$SUBS_PURCHASE_INCOMPLETE}", PURCHASE_SIGNATURE) -} - -fun mockInAppPurchase(): Purchase { - return Purchase("{$INAPP_PURCHASE}", PURCHASE_SIGNATURE) -} - -object Constants { - // Subscription purchase without productId - const val SUBS_PURCHASE_INCOMPLETE = "\"orderId\":\"GPA.0000-0000-0000-0000\"," + - "\"packageName\":\"io.qonversion.sample\"," + - "\"purchaseTime\":1631867965714," + - "\"purchaseState\":1," + - "\"purchaseToken\":\"XXXXXXX\"," + - "\"quantity\":1," + - "\"autoRenewing\":true," + - "\"acknowledged\":true" - - const val INAPP_PURCHASE = "\"orderId\":\"GPA.0000-0000-0000-0000\"," + - "\"packageName\":\"io.qonversion.sample\"," + - "\"productId\":\"qonversion_inapp_consumable\"," + - "\"purchaseTime\":1632238801527," + - "\"purchaseState\":1," + - "\"purchaseToken\":\"XXXXXXX\"," + - "\"quantity\":1," + - "\"acknowledged\":true" - - const val PURCHASE_SIGNATURE = "mockSignature" - const val DEFAULT_SUBS_SKU = "subs_weekly" -} \ No newline at end of file