Skip to content

Support to round+truncate prices displayed on paywalls for backend-specified countries#1806

Merged
jamesrb1 merged 43 commits into
mainfrom
james/drop-paywalls-.00-from-prices
Aug 15, 2024
Merged

Support to round+truncate prices displayed on paywalls for backend-specified countries#1806
jamesrb1 merged 43 commits into
mainfrom
james/drop-paywalls-.00-from-prices

Conversation

@jamesrb1

@jamesrb1 jamesrb1 commented Aug 8, 2024

Copy link
Copy Markdown
Contributor

Round and truncate prices displayed on paywalls for storefronts specified by the backend

Some countries have cents as part of their currency, but it is rare to see them written in prices. This PR makes it possible to round/hide the cents component.

Prices are only truncated if the price ends in .00.

The steps to do this are:

1. Get list of storefronts for which rounding should occur.

  • Get list of prices of storefront countries for which rounding applies. There are different storefront codes for Google (Android) and Apple, and we only save the Google ones, in PaywallData as zeroDecimalPlaceCountries.

2. Determine if this user is in a "rounding" country.

  • If the country of the storefront that the device is connected to is in the list returned by the backend, we set a new property, VariableProcessor.PackageContext.showZeroDecimalPlacePrices to true. VariableProcessor.PackageContext is used by VariableProcessor to translate {{ price_variables }} into the correct strings.

3. Round prices if needed while processing variables

  • When the VariableProcessor is asked for a localized price string (localizedPrice(...), localizedPricePerWeek(...) etc., it returns a rounded price if rounding conditions are met.

The server is currently returning "PH", "KZ", "TW", "MX", "TH" as zero decimal countries. If your google account is not in one of these countries, you can turn temporarily turn rounding "on" by going to PackageConfigurationFactory.kt and replacing

                paywallData.zeroDecimalPlaceCountries.contains(
                    Purchases.sharedInstance.storefrontCountryCode,
                )

with

                paywallData.zeroDecimalPlaceCountries.contains(
                    "TW",
                )

Note that prices are formatted according to the locale of the RC paywall. For example, if the device's language setting is Canadian English (en_CA), and attached the Canadian Play store (CA), but there is no RC paywall localized to English (Canada), then the paywall that we show falls back to the en_US paywall, and all prices will be formatted according to en_US rules, meaning a price will show as e.g. "CA$7.99". If there is an en_CA paywall, then prices are formatted according to en_CA rules, e.g. "$7.99".

This is the Android version of RevenueCat/purchases-ios#4132

@jamesrb1 jamesrb1 added the pr:feat A new feature label Aug 8, 2024
@codecov

codecov Bot commented Aug 10, 2024

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 40.00000% with 6 lines in your changes missing coverage. Please review.

Project coverage is 82.84%. Comparing base (f8ee985) to head (44fb0e5).
Report is 2 commits behind head on main.

Files Patch % Lines
...urchases/utils/serializers/GoogleListSerializer.kt 28.57% 2 Missing and 3 partials ⚠️
.../com/revenuecat/purchases/PurchasesOrchestrator.kt 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1806      +/-   ##
==========================================
- Coverage   82.89%   82.84%   -0.06%     
==========================================
  Files         221      222       +1     
  Lines        7660     7670      +10     
  Branches     1074     1077       +3     
==========================================
+ Hits         6350     6354       +4     
- Misses        891      894       +3     
- Partials      419      422       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jamesrb1 jamesrb1 marked this pull request as ready for review August 12, 2024 03:22
@jamesrb1 jamesrb1 requested a review from a team August 12, 2024 03:23

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly some API tests missing and some questions but looking good!

Comment thread purchases/src/defaults/kotlin/com/revenuecat/purchases/Purchases.kt
Comment thread purchases/src/main/kotlin/com/revenuecat/purchases/paywalls/PaywallData.kt Outdated

@SerialName("zero_decimal_place_countries")
@Serializable(with = GoogleListSerializer::class)
val zeroDecimalPlaceCountries: List<String> = emptyList(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also need to add this to API tests

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any explicit API testers for PaywallData, but the sample paywalls in SamplePaywalls.kt serves this purpose. Is there somewhere else this should be added?

@jamesrb1

jamesrb1 commented Aug 13, 2024

Copy link
Copy Markdown
Contributor Author

StoreProduct and PricingPhase have .pricePerPeriod() functions too. Should they exhibit the same rounding behavior, or is this purely Paywall presentation logic?

AFAIK this rounding is only needed for paywall presentation. I see you added the code for PricingPhase - do you know if these values are ever presented to the user?

@JayShortway

Copy link
Copy Markdown
Member

StoreProduct and PricingPhase have .pricePerPeriod() functions too. Should they exhibit the same rounding behavior, or is this purely Paywall presentation logic?

AFAIK this rounding is only needed for paywall presentation. I see you added the code for PricingPhase - do you know if these values are ever presented to the user?

(Really missing GitLab's comment threads ☝️)

Alright! They're not presented to the user, unless the app developer does it in their own UI. They're purely convenience functions (but I do imagine their main purpose is to show in UI).

cc @tonidero for verification 🙏

@tonidero

Copy link
Copy Markdown
Contributor

They're not presented to the user, unless the app developer does it in their own UI. They're purely convenience functions (but I do imagine their main purpose is to show in UI).

Yeah, that's true. It's part of the public API, in case a developer wants to use them. They can also write this logic themselves for sure, so this would be a mostly convenience change. However, note that this is configured from the paywalls, so maybe it makes more sense to restrain this logic to paywalls and just use what comes from the store otherwise? Happy to discuss this more over Slack if needed.

@JayShortway

Copy link
Copy Markdown
Member

so maybe it makes more sense to restrain this logic to paywalls and just use what comes from the store otherwise?

Yea I think that makes a lot of sense!

@jamesrb1

Copy link
Copy Markdown
Contributor Author

I believe I've addressed all concerns raised.

@JayShortway JayShortway left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

To fix PurchasesAPI.java:

- final String storefrontCountryCode = purchases.storefrontCountryCode();
+ final String storefrontCountryCode = purchases.getStorefrontCountryCode();

Java loves its getters and setters. So a val in Kotlin becomes a getter. A var becomes a pair of getter and setter.

@jamesrb1

Copy link
Copy Markdown
Contributor Author

To fix PurchasesAPI.java:

THANK YOU!!!

@jamesrb1 jamesrb1 merged commit d26a594 into main Aug 15, 2024
@jamesrb1 jamesrb1 deleted the james/drop-paywalls-.00-from-prices branch August 15, 2024 20:53
joshdholtz added a commit that referenced this pull request Aug 22, 2024
**This is an automatic release.**

### New Features
* [Paywalls] Add default locale (#1809) via Josh Holtz (@joshdholtz)
* [Paywalls] Support to round+truncate prices displayed on paywalls for
backend-specified countries (#1806) via James Borthwick (@jamesrb1)
### Other Changes
* Publish paywall tester on each release (#1803) via Toni Rico
(@tonidero)

---------

Co-authored-by: revenuecat-ops <ops@revenuecat.com>
Co-authored-by: Josh Holtz <me@joshholtz.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:feat A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants