diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e1dc728..f0d84d1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ The changelog for `Superwall`. Also see the [releases](https://github.com/superwall-me/Superwall-Android/releases) on GitHub. +## 1.0.0-alpha.45 + +### Fixes + +- Fixes issue where the `paywallProductsLoad_fail` event wasn't correctly being logged. This is a +"soft fail", meaning that even though it gets logged, your paywall will still show. The error message +with the event has been updated to include all product subscription IDs that are failing to be retrieved. + ## 1.0.0-alpha.44 ### Fixes diff --git a/superwall/build.gradle.kts b/superwall/build.gradle.kts index 376a7dea..4fce324d 100644 --- a/superwall/build.gradle.kts +++ b/superwall/build.gradle.kts @@ -19,7 +19,7 @@ plugins { id("maven-publish") } -version = "1.0.0-alpha.44" +version = "1.0.0-alpha.45" android { compileSdk = 33 diff --git a/superwall/src/main/java/com/superwall/sdk/debug/DebugViewController.kt b/superwall/src/main/java/com/superwall/sdk/debug/DebugViewController.kt index 6f65989b..1aebae5b 100644 --- a/superwall/src/main/java/com/superwall/sdk/debug/DebugViewController.kt +++ b/superwall/src/main/java/com/superwall/sdk/debug/DebugViewController.kt @@ -64,6 +64,7 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import com.superwall.sdk.debug.localizations.SWLocalizationActivity +import com.superwall.sdk.dependencies.TriggerSessionManagerFactory import kotlinx.coroutines.withContext interface AppCompatActivityEncapsulatable { @@ -79,7 +80,7 @@ class DebugViewController( private val debugManager: DebugManager, private val factory: Factory ) : ConstraintLayout(context), AppCompatActivityEncapsulatable { - interface Factory: RequestFactory, ViewControllerFactory {} + interface Factory: RequestFactory, ViewControllerFactory, TriggerSessionManagerFactory {} data class AlertOption( val title: String? = "", val action: (suspend () -> Unit)? = null, @@ -418,7 +419,11 @@ class DebugViewController( ) var paywall = paywallRequestManager.getPaywall(request) - val productVariables = storeKitManager.getProductVariables(paywall) + val productVariables = storeKitManager.getProductVariables( + paywall, + request = request, + factory = factory + ) paywall.productVariables = productVariables this.paywall = paywall @@ -565,7 +570,8 @@ class DebugViewController( } val (productsById, _) = storeKitManager.getProducts( - responseProductIds = paywall.productIds + paywall = paywall, + factory = factory ) val products = paywall.productIds.mapNotNull { productsById[it] } SWConsoleActivity.products = products diff --git a/superwall/src/main/java/com/superwall/sdk/paywall/request/PaywallRequestManager.kt b/superwall/src/main/java/com/superwall/sdk/paywall/request/PaywallRequestManager.kt index 2bd27001..0f21f38a 100644 --- a/superwall/src/main/java/com/superwall/sdk/paywall/request/PaywallRequestManager.kt +++ b/superwall/src/main/java/com/superwall/sdk/paywall/request/PaywallRequestManager.kt @@ -187,31 +187,25 @@ class PaywallRequestManager( private suspend fun getProducts(paywall: Paywall, request: PaywallRequest): Paywall = withContext(singleThreadContext) { var paywall = paywall - try { - val result = storeKitManager.getProducts( - paywall.productIds, - paywall.products, - request.overrides.products - ) - - paywall.products = result.products + val result = storeKitManager.getProducts( + substituteProducts = request.overrides.products, + paywall = paywall, + request = request, + factory = factory + ) + paywall = result.paywall + paywall.products = result.products - val outcome = PaywallLogic.getVariablesAndFreeTrial( - result.products, - result.productsById, - request.overrides.isFreeTrial - ) - paywall.productVariables = outcome.productVariables + val outcome = PaywallLogic.getVariablesAndFreeTrial( + result.products, + result.productsById, + request.overrides.isFreeTrial + ) + paywall.productVariables = outcome.productVariables // paywall.swProductVariablesTemplate = outcome.swProductVariablesTemplate - paywall.isFreeTrialAvailable = outcome.isFreeTrialAvailable + paywall.isFreeTrialAvailable = outcome.isFreeTrialAvailable - return@withContext paywall - } catch (error: Throwable) { - paywall.productsLoadingInfo.failAt = Date() - val paywallInfo = paywall.getInfo(request.eventData, factory) - trackProductLoadFail(error.message, paywallInfo, request.eventData) - throw error - } + return@withContext paywall } // Analytics @@ -228,19 +222,6 @@ class PaywallRequestManager( return@withContext paywall } - private suspend fun trackProductLoadFail( - errorMessage: String?, - paywallInfo: PaywallInfo, - event: EventData? - ) = withContext(singleThreadContext) { - val productLoadEvent = InternalSuperwallEvent.PaywallProductsLoad( - state = InternalSuperwallEvent.PaywallProductsLoad.State.Fail(errorMessage), - paywallInfo = paywallInfo, - eventData = event - ) - Superwall.instance.track(productLoadEvent) - } - private suspend fun trackProductsLoadFinish(paywall: Paywall, event: EventData?): Paywall = withContext(singleThreadContext) { var paywall = paywall paywall.productsLoadingInfo.endAt = Date() diff --git a/superwall/src/main/java/com/superwall/sdk/store/StoreKitManager.kt b/superwall/src/main/java/com/superwall/sdk/store/StoreKitManager.kt index 447b10cf..37ccd77e 100644 --- a/superwall/src/main/java/com/superwall/sdk/store/StoreKitManager.kt +++ b/superwall/src/main/java/com/superwall/sdk/store/StoreKitManager.kt @@ -4,15 +4,21 @@ import LogLevel import LogScope import Logger import android.content.Context +import com.superwall.sdk.Superwall +import com.superwall.sdk.analytics.internal.track +import com.superwall.sdk.analytics.internal.trackable.InternalSuperwallEvent +import com.superwall.sdk.dependencies.TriggerSessionManagerFactory import com.superwall.sdk.models.paywall.Paywall import com.superwall.sdk.models.paywall.PaywallProducts import com.superwall.sdk.models.product.Product import com.superwall.sdk.models.product.ProductType import com.superwall.sdk.models.product.ProductVariable +import com.superwall.sdk.paywall.request.PaywallRequest import com.superwall.sdk.store.abstractions.product.StoreProduct import com.superwall.sdk.store.abstractions.product.receipt.ReceiptManager import com.superwall.sdk.store.coordinator.ProductsFetcher import com.superwall.sdk.store.products.GooglePlayProductsFetcher +import java.util.Date /* class StoreKitManager(private val context: Context) : StoreKitManagerInterface { @@ -104,8 +110,16 @@ class StoreKitManager( val products: List ) - suspend fun getProductVariables(paywall: Paywall): List { - val output = getProducts(paywall.productIds) + suspend fun getProductVariables( + paywall: Paywall, + request: PaywallRequest, + factory: TriggerSessionManagerFactory + ): List { + val output = getProducts( + paywall = paywall, + request = request, + factory = factory + ) val variables = paywall.products.mapNotNull { product -> output.productsById[product.id]?.let { storeProduct -> @@ -120,19 +134,30 @@ class StoreKitManager( } suspend fun getProducts( - responseProductIds: List, - responseProducts: List = emptyList(), - substituteProducts: PaywallProducts? = null + substituteProducts: PaywallProducts? = null, + paywall: Paywall, + request: PaywallRequest? = null, + factory: TriggerSessionManagerFactory ): GetProductsResponse { val processingResult = removeAndStore( substituteProducts = substituteProducts, - responseProductIds, - responseProducts = responseProducts + responseProductIds = paywall.productIds, + responseProducts = paywall.products ) - val products = products( - identifiers = processingResult.productIdsToLoad - ) + var products: Set = setOf() + try { + products = products(identifiers = processingResult.productIdsToLoad) + } catch (error: Throwable) { + paywall.productsLoadingInfo.failAt = Date() + val paywallInfo = paywall.getInfo(request?.eventData, factory) + val productLoadEvent = InternalSuperwallEvent.PaywallProductsLoad( + state = InternalSuperwallEvent.PaywallProductsLoad.State.Fail(error.message), + paywallInfo = paywallInfo, + eventData = request?.eventData + ) + Superwall.instance.track(productLoadEvent) + } val productsById = processingResult.substituteProductsById.toMutableMap() @@ -142,7 +167,11 @@ class StoreKitManager( this.productsById[productIdentifier] = product } - return GetProductsResponse(productsById, processingResult.products) + return GetProductsResponse( + productsById = productsById, + products = processingResult.products, + paywall = paywall + ) } private fun removeAndStore( diff --git a/superwall/src/main/java/com/superwall/sdk/store/StoreKitManagerInterface.kt b/superwall/src/main/java/com/superwall/sdk/store/StoreKitManagerInterface.kt index 3dfe3962..906dcc15 100644 --- a/superwall/src/main/java/com/superwall/sdk/store/StoreKitManagerInterface.kt +++ b/superwall/src/main/java/com/superwall/sdk/store/StoreKitManagerInterface.kt @@ -11,7 +11,8 @@ import com.superwall.sdk.store.abstractions.product.StoreProduct data class GetProductsResponse( val productsById: Map, - val products: List + val products: List, + val paywall: Paywall ) interface StoreKitManagerInterface { diff --git a/superwall/src/main/java/com/superwall/sdk/store/products/GooglePlayProductsFetcher.kt b/superwall/src/main/java/com/superwall/sdk/store/products/GooglePlayProductsFetcher.kt index 44cc8fa8..c3b089d5 100644 --- a/superwall/src/main/java/com/superwall/sdk/store/products/GooglePlayProductsFetcher.kt +++ b/superwall/src/main/java/com/superwall/sdk/store/products/GooglePlayProductsFetcher.kt @@ -242,18 +242,20 @@ open class GooglePlayProductsFetcher( .toMap() .toMutableMap() as MutableMap> + val missingProductsString = missingProducts.joinToString(separator = ", ") missingProducts.forEach { missingProductId -> productIdsBySubscriptionId[missingProductId]?.forEach { product -> - subscriptionIdToResult[product.fullId] = Result.Error(Exception("Failed to query product details")) + subscriptionIdToResult[product.fullId] = Result.Error(Exception("Failed to query product details for $missingProductsString")) } } return subscriptionIdToResult.toMap() } else { + val missingProductsString = subscriptionIds.joinToString(separator = ", ") val results: MutableMap> = mutableMapOf() subscriptionIds.forEach { subscriptionId -> productIdsBySubscriptionId[subscriptionId]?.forEach { product -> - results[product.fullId] = Result.Error(Exception("Failed to query product details. Billing response code: ${billingResult.responseCode}")) + results[product.fullId] = Result.Error(Exception("Failed to query product details for $missingProductsString. Billing response code: ${billingResult.responseCode}")) } } return results @@ -271,6 +273,7 @@ open class GooglePlayProductsFetcher( return productResults.values.mapNotNull { when (it) { is Result.Success -> StoreProduct(it.value) // Assuming RawStoreProduct can be converted to StoreProduct + is Result.Error -> throw it.error else -> null } }.toSet()