From 757aaa599f1718249b6dc3435b31499fc034e245 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Mon, 30 Oct 2023 08:01:48 -0700 Subject: [PATCH] `Paywalls`: `PaywallData` errors shouldn't make `Offering`s fail to decode (#1402) This also adds coverage for `PaywallData` deserialization inside of `Offering` --- .../purchases/common/OfferingParser.kt | 9 +- .../common/offerings/OfferingsFactoryTest.kt | 93 +++++++++++++++++++ 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/common/OfferingParser.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/common/OfferingParser.kt index 85fa94ae13..5b299e1012 100644 --- a/purchases/src/main/kotlin/com/revenuecat/purchases/common/OfferingParser.kt +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/common/OfferingParser.kt @@ -65,8 +65,13 @@ internal abstract class OfferingParser { val paywallDataJson = offeringJson.optJSONObject("paywall") - val paywallData = paywallDataJson?.let { - json.decodeFromString(it.toString()) + val paywallData: PaywallData? = paywallDataJson?.let { + try { + json.decodeFromString(it.toString()) + } catch (e: IllegalArgumentException) { + errorLog("Error deserializing paywall data", e) + null + } } return if (availablePackages.isNotEmpty()) { 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 df8077bf97..52f5b7ad6b 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 @@ -38,6 +38,63 @@ class OfferingsFactoryTest { "'description': 'This is the base offering', " + "'packages': []}]," + "'current_offering_id': '$STUB_OFFERING_IDENTIFIER'}") + private val oneOfferingWithInvalidPaywallResponse = JSONObject( + "" + + "{" + + "'offerings': [" + + "{" + + "'identifier': '$STUB_OFFERING_IDENTIFIER', " + + "'description': 'This is the base offering', " + + "'packages': [" + + "{'identifier': '\$rc_monthly','platform_product_identifier': '$STUB_PRODUCT_IDENTIFIER'}" + + "]," + + "'paywall': 'not a paywall'" + + "}" + + "]," + + "'current_offering_id': '$STUB_OFFERING_IDENTIFIER'" + + "}" + ) + private val oneOfferingWithPaywall = JSONObject( + "" + + "{" + + "'offerings': [" + + "{" + + "'identifier': '$STUB_OFFERING_IDENTIFIER', " + + "'description': 'This is the base offering', " + + "'packages': [" + + "{'identifier': '\$rc_monthly','platform_product_identifier': '$STUB_PRODUCT_IDENTIFIER'}" + + "]," + + "'paywall': {\n" + + " \"template_name\": \"1\",\n" + + " \"localized_strings\": {\n" + + " \"en_US\": {\n" + + " \"title\": \"Paywall\",\n" + + " \"call_to_action\": \"Purchase\",\n" + + " \"subtitle\": \"Description\"\n" + + " }\n" + + " },\n" + + " \"config\": {\n" + + " \"packages\": [\"\$rc_monthly\"],\n" + + " \"default_package\": \"\$rc_monthly\",\n" + + " \"images\": {},\n" + + " \"colors\": {\n" + + " \"light\": {\n" + + " \"background\": \"#FF00AA\",\n" + + " \"text_1\": \"#FF00AA22\",\n" + + " \"call_to_action_background\": \"#FF00AACC\",\n" + + " \"call_to_action_foreground\": \"#FF00AA\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"asset_base_url\": \"https://rc-paywalls.s3.amazonaws.com\",\n" + + " \"revision\": 7\n" + + "}" + + "}" + + "]," + + "'current_offering_id': '$STUB_OFFERING_IDENTIFIER'" + + "}" + ) + private val oneOfferingResponse = JSONObject(ONE_OFFERINGS_RESPONSE) private val oneOfferingInAppProductResponse = JSONObject(ONE_OFFERINGS_INAPP_PRODUCT_RESPONSE) @@ -142,6 +199,42 @@ class OfferingsFactoryTest { assertThat(offerings!![STUB_OFFERING_IDENTIFIER]!!.monthly!!.product).isNotNull } + @Test + fun `createOfferings with paywall`() { + val productIds = listOf(productId) + mockStoreProduct(productIds, emptyList(), ProductType.SUBS) + mockStoreProduct(productIds, productIds, ProductType.INAPP) + + var offerings: Offerings? = null + offeringsFactory.createOfferings( + oneOfferingWithPaywall, + { fail("Error: $it") }, + { offerings = it } + ) + + assertThat(offerings).isNotNull + assertThat(offerings!!.current).isNotNull + assertThat(offerings!!.current?.paywall).isNotNull + } + + @Test + fun `createOfferings does not fail if paywall is invalid`() { + val productIds = listOf(productId) + mockStoreProduct(productIds, emptyList(), ProductType.SUBS) + mockStoreProduct(productIds, productIds, ProductType.INAPP) + + var offerings: Offerings? = null + offeringsFactory.createOfferings( + oneOfferingWithInvalidPaywallResponse, + { fail("Error: $it") }, + { offerings = it } + ) + + assertThat(offerings).isNotNull + assertThat(offerings!!.current).isNotNull + assertThat(offerings!!.current?.paywall).isNull() + } + // region helpers private fun mockStoreProduct(