Skip to content

getCurrentOfferingForPlacement returns null on Android but returns fallback offering on iOS #1700

@ismailcaakir

Description

@ismailcaakir

Environment

  • purchases_flutter: 9.15.1
  • purchases_ui_flutter: 9.15.1
  • Flutter: 3.32.x
  • iOS: 26.2 (Simulator)
  • Android: API 36 (Physical device — Samsung SM-A065F)

Description

Purchases.getCurrentOfferingForPlacement(placementId) behaves differently on iOS and Android when the placement does not exist in the RevenueCat dashboard.

  • iOS: Returns the current (default) offering as a fallback when the placement is not found.
  • Android: Returns null when the placement is not found.

This inconsistency causes a critical business logic bug in apps that use placement-based paywall resolution with a fallback chain.

Steps to Reproduce

  1. Configure a RevenueCat project with a current (default) offering set.
  2. Do NOT create any placements in the RevenueCat dashboard.
  3. Call Purchases.getCurrentOfferingForPlacement('any_nonexistent_id').
  4. Observe the return value on both platforms.

Expected Behavior

Both platforms should return null when the placement does not exist (or both should return the fallback — but behavior should be consistent).

Actual Behavior

Platform Placement exists? Return value
iOS No default_offering (current offering fallback)
Android No null

Debug Logs

iOS (Simulator)

[Paywall] placementId: whatsapp
[Paywall] offerings.current: default_offering
[Paywall] placementOffering: default_offering    ← fallback returned
[Paywall] Step 1: presenting placement (default_offering)

Android (Physical device)

[Paywall] placementId: whatsapp
[Paywall] offerings.current: default_offering
[Paywall] placementOffering: null                ← null returned
[Paywall] Step 1: placement not found, skipping

Impact

In our paywall resolution chain:

1. Try placement offering → presentPaywallIfNeeded (subscription check)
2. If no placementId → try current offering → presentPaywallIfNeeded (subscription check)
3. Try credit offering → presentPaywall (NO subscription check)
4. Fallback → presentPaywall

On Android, when the placement returns null:

  • Step 1 is skipped (no offering found)
  • Step 2 is skipped (placementId is not null, so the if (placementId == null) guard prevents execution)
  • Step 3 executes — showing credit-only packages to non-subscribers

This means non-subscribed users on Android can see and purchase credit packages without a subscription, bypassing the intended paywall flow. This does not happen on iOS because the fallback offering triggers the subscription check in Step 1.

Workaround

Remove the if (placementId == null) guard on Step 2, so the subscription offering check runs regardless of whether a placement was attempted. Additionally, add a subscription entitlement check before Step 3.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions