Best practice for subscription state handling with managed pricing, trial periods, and recurring charges (uninstall/reinstall flow)

I’m building a Shopify app with managed pricing (trial + recurring subscription) and I want users to be able to uninstall and reinstall the app without losing access if they’re still within their billing period.

The flow I’m working with:

  • Merchant is on a trial or paid subscription (mid billing cycle)

  • They uninstall the app

  • Before currentPeriodEnd, they reinstall it again

  • OAuth runs again and I can query activeSubscriptions

Right now I’m storing subscription state in my DB (status + currentPeriodEnd) to handle this.

What I want is:

If they reinstall within the same billing period, they should regain access immediately without being charged again or creating a new subscription.

Where I’m unsure:

  • Should activeSubscriptions always be checked first on reinstall before creating anything new?

  • Is it expected that a subscription remains valid across uninstall/reinstall until currentPeriodEnd?

  • In practice, is the DB just a cache/safety layer, while Shopify is still the source of truth for billing?

  • What’s the standard way to avoid accidentally creating duplicate subscriptions when a merchant reinstalls?

Just trying to make sure I’m handling reinstall flows correctly without breaking access or billing.

Hey @abdulrahim - thanks for reaching out.

I did some digging here and I’ll go over each of your questions:

When a merchant uninstalls your app, Shopify automatically cancels the subscription. What this means is that the AppSubscriptionStatus moves to CANCELLED, which is a terminal state - it can’t flip back to ACTIVE.

But the merchant isn’t charged again for the rest of that billing period, and they can reinstall and use the app until the remainder of the billing period. When they reinstall, activeSubscriptions on the currentAppInstallation query will return empty because cancelled subscriptions are filtered out entirely. A new subscription is only created after the merchant goes through plan selection on managed pricing’s hosted page - reinstalling alone doesn’t create one.

From the subscription billing docs:

“When an app is uninstalled, Shopify automatically cancels the subscription. A credit isn’t applied to cover the cost of the rest of the billing period. Merchants can reinstall and use the app for the remainder of the billing period.”

Regarding whether the DB is just a cache/safety layer, while Shopify is still the source of truth for billing: Yes, exactly. Shopify is the source of truth for billing state. On reinstall, always re-sync from Shopify’s state rather than trusting stale DB records. Register for the APP_SUBSCRIPTIONS_UPDATE webhook to keep your local state in sync.

Regarding how to avoid duplicate subscriptions, Shopify handles this for you when it comes to Managed App Pricing since it controls the plan selection page. If you’re using the Billing API instead, always check activeSubscriptions before calling appSubscriptionCreate.

The App Store requirement 1.2.2 spells this out - your app must be able to “accept, decline and request approval for charges again on reinstall.”

TL;DR for your reinstall flow:

  1. Merchant reinstalls. Their old subscription is CANCELLED and won’t show in activeSubscriptions
  2. Managed pricing redirects the merchant to plan selection and creates the new subscription
  3. You get an APP_SUBSCRIPTIONS_UPDATE webhook and can sync your DB from that
  4. Gate paid features based on subscription state, not install state

I hope that helps and let me know if you have any further questions.