Skip to content

Fix day↔week price conversion#409

Open
yusuftor wants to merge 2 commits into
developfrom
fix-day-week-price-conversion
Open

Fix day↔week price conversion#409
yusuftor wants to merge 2 commits into
developfrom
fix-day-week-price-conversion

Conversation

@yusuftor
Copy link
Copy Markdown
Contributor

@yusuftor yusuftor commented May 15, 2026

Summary

Android counterpart to the iOS SDK fix (superwall/Superwall-iOS#473) and the editor fix (superwall/paywall-next#3178).

SubscriptionPeriod converted between days and weeks via a 52/365 factor — but 52 weeks is only 364 days, so a 7-day period resolved to ~0.997 weeks instead of exactly 1. That inflates a 7-day product's weekly price and skews a week product's daily price (e.g. a $7.00/week product reports $0.99/day instead of the exact $1.00).

7 days is exactly 1 week, so the conversion should be exact.

Scope note

Android does not have the iOS variant of the bug where the price getters bypassed normalized() — Android's RawStoreProduct price getters already compute off the normalized SubscriptionPeriod, and SubscriptionPeriod.from() collapses whole-week day-counts to weeks. So the most visible symptom (weeklyPrice of a weekly product) does not reproduce on Android. What this fixes is the residual error: dailyPrice of weekly products, odd day-count products, and trial per-unit prices — all of which would otherwise disagree with the now-fixed iOS SDK and editor.

Changes

  • SubscriptionPeriod.ktpricePerDay .week: 365/527; pricePerWeek .day: 52/3651/7.
  • RawStoreProduct.ktperiodsPerUnit (trial-price path): the same two day↔week entries.
  • Month↔year was already exact (12); week↔month / week↔year stay as conventional approximations.

Tests

Added two regression tests to the active test region of SubscriptionPeriodUnitTest:

  • a 7-day period's pricePerWeek equals the price;
  • a weekly product's pricePerDay divides by exactly 7 ($7.00/week$1.00/day, which the old 365/52 got wrong as $0.99).

Note: the existing singleDaily/multiDaily/… period-price tests are still inside the pre-existing /* TODO: Re-enable these in CI */ block — left untouched (some of them also carry unrelated stale month↔week expectations). 3 tests run, 0 failures.

🤖 Generated with Claude Code

Greptile Summary

This PR fixes the day↔week conversion factor in SubscriptionPeriod.pricePerDay/pricePerWeek and in RawStoreProduct.periodsPerUnit, replacing the approximate 365/52 (≈7.019) and 52/365 (≈0.1425) factors with the exact 7 and 1/7.

  • SubscriptionPeriod.kt: pricePerDay for a weekly product now uses BigDecimal(7) (days per week); pricePerWeek for a daily product now uses 1/7 (weeks per day).
  • RawStoreProduct.kt: The same two day↔week entries in periodsPerUnit are updated to match.
  • SubscriptionPeriodUnitTest.kt: Two new regression tests use BigDecimal(String) and assertEquals correctly and are placed in the active (non-commented) test region.

Confidence Score: 5/5

Safe to merge — the change is a narrow, well-scoped arithmetic correction with no side effects on unrelated paths.

The two changed constants (365/52 → 7 and 52/365 → 1/7) are mathematically exact, applied consistently in both SubscriptionPeriod and RawStoreProduct, and directly verified by two new regression tests that use the correct BigDecimal(String) constructor and assertEquals. Month↔year and week↔month conversions are left untouched as documented conventional approximations.

No files require special attention.

Important Files Changed

Filename Overview
superwall/src/main/java/com/superwall/sdk/store/abstractions/product/SubscriptionPeriod.kt Replaces the approximate 365/52 day↔week factor with the exact value 7 in both pricePerDay (week→days) and pricePerWeek (day→weeks); all other conversions left untouched.
superwall/src/main/java/com/superwall/sdk/store/abstractions/product/RawStoreProduct.kt Mirrors the SubscriptionPeriod fix in periodsPerUnit: day←week now returns BigDecimal(7) and week←day now returns 1/7 instead of the old 52/365 approximation.
superwall/src/test/java/com/superwall/sdk/store/abstractions/product/SubscriptionPeriodUnitTest.kt Adds two regression tests (sevenDayPeriod_weeklyPriceEqualsPrice and weeklyPeriod_dailyPriceDividesBySeven) using the BigDecimal(String) constructor and assertEquals — correctly placed outside the disabled CI block.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[SubscriptionPeriod] --> B{pricePerDay}
    A --> C{pricePerWeek}

    B -->|unit = day| D["÷ 1 × value"]
    B -->|unit = week| E["÷ 7 × value ✅ was: 365÷52"]
    B -->|unit = month| F["÷ 365÷12 × value"]
    B -->|unit = year| G["÷ 365 × value"]

    C -->|unit = day| H["÷ 1÷7 × value ✅ was: 52÷365"]
    C -->|unit = week| I["÷ 1 × value"]
    C -->|unit = month| J["÷ 52÷12 × value"]
    C -->|unit = year| K["÷ 52 × value"]

    L[RawStoreProduct.periodsPerUnit] --> M{outer unit}
    M -->|day| N{trial unit}
    M -->|week| O{trial unit}
    N -->|week| P["7 ✅ was: 365÷52"]
    O -->|day| Q["1÷7 ✅ was: 52÷365"]
Loading

Reviews (2): Last reviewed commit: "Use exact BigDecimal and assertEquals in..." | Re-trigger Greptile

SubscriptionPeriod converted between days and weeks via a 52/365 factor.
But 52 weeks is only 364 days, so a 7-day period resolved to ~0.997
weeks instead of exactly 1 — inflating a 7-day product's weekly price
and skewing a week product's daily price (e.g. a $7.00/week product
reported $0.99/day instead of the exact $1.00).

7 days is exactly 1 week. Use the exact ratio in both directions:

- SubscriptionPeriod.pricePerDay .week: 365/52 -> 7
- SubscriptionPeriod.pricePerWeek .day: 52/365 -> 1/7
- RawStoreProduct.periodsPerUnit (trial path): the same two day↔week
  entries.

Month↔year was already exact (12); week/month and week/year stay as
conventional approximations — those aren't whole-number relationships.

Mirrors the same fix in the iOS SDK and the web editor so all three
agree. Adds two regression tests (a 7-day period's weekly price equals
the price; a weekly product's daily price divides by exactly 7) in the
active test region — the existing period-price tests are still inside
the disabled "TODO: Re-enable in CI" block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two issues in the new regression tests:

- BigDecimal(6.99) calls the double constructor, which stores the
  inexact IEEE-754 value rather than exactly 6.99 — the two sides of
  the comparison could resolve to different values. Use the String
  constructor for exact decimals.
- Kotlin's assert() is compiled to a no-op unless the JVM runs with
  -ea, so the tests could silently pass even on a regression. Switch
  to JUnit's assertEquals, which always evaluates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant