Separate three decisions in your data model

A selected plan, a verified purchase and permission to use a feature are related, but they are not identical facts. Keeping them separate prevents a screen choice from becoming unintended paid access.

Consider an illustrative app with Free and Pro. A new user can choose Pro, begin checkout and cancel the purchase. The selected option says what they intended to buy; it does not prove that a purchase succeeded. Similarly, an account created during onboarding should not gain Pro just because a profile column defaults to that label.

Document the meaning of every field: onboarding completed, preferred plan, verified entitlement, effective access and renewal status. Give each field a clear authority and update rule.

Use entitlements to describe access

An entitlement represents access to a set of features, while a store product describes something a customer can purchase. Several products, such as monthly and annual subscriptions, can grant the same entitlement.

RevenueCat's entitlement guide explains that mapping. Define feature checks around the intended entitlement rather than scattering product-name comparisons across screens.

Maintain a small feature policy: what Free can do, what an active paid entitlement adds and what happens when paid access ends. Preserve user records according to the product's stated behavior; an expired subscription does not inherently require deleting a customer's work.

Account for multiple entitlements if your products grant different capabilities. A generic paid flag can conceal distinctions the backend needs to enforce.

Keep display state and server authorization consistent

RevenueCat's CustomerInfo documentation describes retrieving and checking subscription information through the SDK. Use that information to update the interface after purchases, restores and relevant refreshes.

For a privileged backend operation, authorize against server-trusted subscription data for the authenticated account. A client-supplied plan string is not sufficient evidence. Define how the app account maps to the purchase provider's customer identity, including logout, account switching and restores.

Make offline and temporarily unknown state explicit. A cached interface may differ from the backend while connectivity recovers. Choose a documented product policy for that situation rather than silently treating a lookup failure as a successful paid purchase.

Process webhook notifications as durable work

A provider notification should trigger a reliable update process, not an unprotected write to a plan column. Validate the request using the provider's documented authentication mechanism, distinguish sandbox from production and retain the event identity.

RevenueCat's webhook guidance covers authorization, retries and duplicate events. Design the handler so processing the same event twice does not apply the business change twice.

A useful architecture persists accepted work, processes it through a controlled worker and records success or retry state. Acknowledge only after the notification is durably accepted. Keep provider secrets on the server and log operational identifiers without recording raw credentials.

Where appropriate, refresh the provider's current customer state rather than letting an old notification overwrite a newer effective state.

Model cancellation, expiration and billing issues deliberately

Canceling renewal is not generally the same event as losing access immediately. A normally canceled subscription can remain entitled until its paid period ends. A billing issue may interact with a configured grace period, and a later successful renewal can change the outcome.

The RevenueCat lifecycle examples explain these transitions and note that some deliveries can arrive in a different order. Use provider-verified current state and effective dates rather than assuming arrival order always matches business order.

A Free option inside your app also needs precise wording. Changing an app preference does not necessarily cancel a store subscription. Explain when billing must be managed through the store and avoid presenting an internal label change as proof that renewal stopped.

Test the lifecycle, including recovery

Use an explicit test matrix: new signup, Free selection, abandoned checkout, successful purchase, cancellation before expiry, expiry, restore, account switch and a temporary provider failure. Add duplicate notifications and delayed processing to the backend checks.

For each case, record the expected interface, stored entitlement and result of a direct backend request. In the illustrative Free-to-Pro flow, canceled checkout must leave paid operations unavailable; a verified purchase must enable the same operations on both sides.

Provide a reconciliation path for events that were missed or processing that stalled. Support should be able to inspect the account mapping and verified state without manually granting a permanent tier to hide a synchronization bug.

Keep the surrounding schema compatible too.

Changes to subscription fields need the same migration discipline as the rest of a live app.