diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesFactory.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesFactory.kt index 338efa9d8f..395c68bfd9 100644 --- a/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesFactory.kt +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesFactory.kt @@ -78,7 +78,8 @@ internal class PurchasesFactory( val sharedPreferencesForETags = ETagManager.initializeSharedPreferences(context) val eTagManager = ETagManager(sharedPreferencesForETags) - val dispatcher = Dispatcher(service ?: createDefaultExecutor(), runningIntegrationTests) + val dispatcher = Dispatcher(createDefaultExecutor(), runningIntegrationTests) + val backendDispatcher = Dispatcher(service ?: createDefaultExecutor(), runningIntegrationTests) val diagnosticsDispatcher = Dispatcher(createDiagnosticsExecutor(), runningIntegrationTests) var diagnosticsFileHelper: DiagnosticsFileHelper? = null @@ -101,10 +102,10 @@ internal class PurchasesFactory( val signingManager = SigningManager(signatureVerificationMode, appConfig, apiKey) val httpClient = HTTPClient(appConfig, eTagManager, diagnosticsTracker, signingManager) - val backendHelper = BackendHelper(apiKey, dispatcher, appConfig, httpClient) + val backendHelper = BackendHelper(apiKey, backendDispatcher, appConfig, httpClient) val backend = Backend( appConfig, - dispatcher, + backendDispatcher, diagnosticsDispatcher, httpClient, backendHelper, @@ -123,7 +124,7 @@ internal class PurchasesFactory( val subscriberAttributesPoster = SubscriberAttributesPoster(backendHelper) - val attributionFetcher = AttributionFetcherFactory.createAttributionFetcher(store, dispatcher) + val attributionFetcher = AttributionFetcherFactory.createAttributionFetcher(store, backendDispatcher) val subscriberAttributesCache = SubscriberAttributesCache(cache) @@ -182,7 +183,7 @@ internal class PurchasesFactory( appConfig, cache, billing, - dispatcher, + backendDispatcher, identityManager, postTransactionWithProductDetailsHelper, ) @@ -217,7 +218,7 @@ internal class PurchasesFactory( val offeringsManager = OfferingsManager( offeringsCache, backend, - OfferingsFactory(billing, offeringParser), + OfferingsFactory(billing, offeringParser, dispatcher), ) log(LogIntent.DEBUG, ConfigureStrings.DEBUG_ENABLED) diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/common/offerings/OfferingsFactory.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/common/offerings/OfferingsFactory.kt index 59bf441796..d32261cd77 100644 --- a/purchases/src/main/kotlin/com/revenuecat/purchases/common/offerings/OfferingsFactory.kt +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/common/offerings/OfferingsFactory.kt @@ -5,19 +5,23 @@ import com.revenuecat.purchases.ProductType import com.revenuecat.purchases.PurchasesError import com.revenuecat.purchases.PurchasesErrorCode import com.revenuecat.purchases.common.BillingAbstract +import com.revenuecat.purchases.common.Dispatcher import com.revenuecat.purchases.common.LogIntent import com.revenuecat.purchases.common.OfferingParser import com.revenuecat.purchases.common.log import com.revenuecat.purchases.models.StoreProduct import com.revenuecat.purchases.strings.OfferingStrings +import kotlinx.serialization.SerializationException import org.json.JSONException import org.json.JSONObject internal class OfferingsFactory( private val billing: BillingAbstract, private val offeringParser: OfferingParser, + private val dispatcher: Dispatcher, ) { + @SuppressWarnings("TooGenericExceptionCaught") fun createOfferings( offeringsJSON: JSONObject, onError: (PurchasesError) -> Unit, @@ -34,18 +38,35 @@ internal class OfferingsFactory( ) } else { getStoreProductsById(allRequestedProductIdentifiers, { productsById -> - logMissingProducts(allRequestedProductIdentifiers, productsById) + try { + logMissingProducts(allRequestedProductIdentifiers, productsById) - val offerings = offeringParser.createOfferings(offeringsJSON, productsById) - if (offerings.all.isEmpty()) { - onError( - PurchasesError( - PurchasesErrorCode.ConfigurationError, - OfferingStrings.CONFIGURATION_ERROR_PRODUCTS_NOT_FOUND, - ), - ) - } else { - onSuccess(offerings) + val offerings = offeringParser.createOfferings(offeringsJSON, productsById) + if (offerings.all.isEmpty()) { + onError( + PurchasesError( + PurchasesErrorCode.ConfigurationError, + OfferingStrings.CONFIGURATION_ERROR_PRODUCTS_NOT_FOUND, + ), + ) + } else { + onSuccess(offerings) + } + } catch (error: Exception) { + when (error) { + is JSONException, is SerializationException -> { + log( + LogIntent.RC_ERROR, + OfferingStrings.JSON_EXCEPTION_ERROR.format(error.localizedMessage), + ) + onError( + PurchasesError( + PurchasesErrorCode.UnexpectedBackendResponseError, + error.localizedMessage, + ), + ) + } else -> throw error + } } }, { error -> onError(error) @@ -87,27 +108,31 @@ internal class OfferingsFactory( ProductType.SUBS, productIds, { subscriptionProducts -> - val productsById = subscriptionProducts - .groupBy { subProduct -> subProduct.purchasingData.productId } - .toMutableMap() - val subscriptionIds = productsById.keys + dispatcher.enqueue(command = { + val productsById = subscriptionProducts + .groupBy { subProduct -> subProduct.purchasingData.productId } + .toMutableMap() + val subscriptionIds = productsById.keys - val inAppProductIds = productIds - subscriptionIds - if (inAppProductIds.isNotEmpty()) { - billing.queryProductDetailsAsync( - ProductType.INAPP, - inAppProductIds, - { inAppProducts -> - productsById.putAll(inAppProducts.map { it.purchasingData.productId to listOf(it) }) - onCompleted(productsById) - }, - { - onError(it) - }, - ) - } else { - onCompleted(productsById) - } + val inAppProductIds = productIds - subscriptionIds + if (inAppProductIds.isNotEmpty()) { + billing.queryProductDetailsAsync( + ProductType.INAPP, + inAppProductIds, + { inAppProducts -> + dispatcher.enqueue(command = { + productsById.putAll(inAppProducts.map { it.purchasingData.productId to listOf(it) }) + onCompleted(productsById) + }) + }, + { + onError(it) + }, + ) + } else { + onCompleted(productsById) + } + }) }, { onError(it) diff --git a/purchases/src/test/java/com/revenuecat/purchases/common/offerings/OfferingsFactoryTest.kt b/purchases/src/test/java/com/revenuecat/purchases/common/offerings/OfferingsFactoryTest.kt index d119958597..df8077bf97 100644 --- a/purchases/src/test/java/com/revenuecat/purchases/common/offerings/OfferingsFactoryTest.kt +++ b/purchases/src/test/java/com/revenuecat/purchases/common/offerings/OfferingsFactoryTest.kt @@ -6,6 +6,7 @@ import com.revenuecat.purchases.ProductType import com.revenuecat.purchases.PurchasesError import com.revenuecat.purchases.PurchasesErrorCode import com.revenuecat.purchases.common.BillingAbstract +import com.revenuecat.purchases.common.Dispatcher import com.revenuecat.purchases.common.GoogleOfferingParser import com.revenuecat.purchases.common.OfferingParser import com.revenuecat.purchases.models.StoreProduct @@ -14,6 +15,7 @@ import com.revenuecat.purchases.utils.ONE_OFFERINGS_INAPP_PRODUCT_RESPONSE import com.revenuecat.purchases.utils.ONE_OFFERINGS_RESPONSE import com.revenuecat.purchases.utils.STUB_OFFERING_IDENTIFIER import com.revenuecat.purchases.utils.STUB_PRODUCT_IDENTIFIER +import com.revenuecat.purchases.utils.SyncDispatcher import com.revenuecat.purchases.utils.stubINAPPStoreProduct import com.revenuecat.purchases.utils.stubStoreProduct import com.revenuecat.purchases.utils.stubSubscriptionOption @@ -43,17 +45,18 @@ class OfferingsFactoryTest { private lateinit var billing: BillingAbstract private lateinit var offeringParser: OfferingParser - + private lateinit var dispatcher: Dispatcher private lateinit var offeringsFactory: OfferingsFactory @Before fun setUp() { billing = mockk() offeringParser = GoogleOfferingParser() - + dispatcher = SyncDispatcher() offeringsFactory = OfferingsFactory( - billing = billing, - offeringParser = offeringParser + billing, + offeringParser, + dispatcher ) }