VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL

Subscription lifecycle

How purchases, renewals, cancellations, refunds and revocations change a person's access and which webhooks fire.

A subscription moves from purchase through renewals to its end. Voidhash learns each change from the store or payment source, updates the person's entitlements, and sends a webhook when the change matters to your backend.

Gate access on perks, not on subscription status. A perk stays active in every state that still entitles the person, so one check covers trials, grace periods and pending cancellations.

How Voidhash learns about changes

ProviderSource of changes
App StoreThe SDK reports each transaction; App Store Server Notifications report later changes.
Google PlayThe SDK reports each purchase; Real-time developer notifications (RTDN) report later changes.
StripeStripe events such as invoice.paid, customer.subscription.updated, customer.subscription.deleted and charge.refunded, each read back from Stripe.
Payment sourceYour provider endpoint reports transactions and subscription states. These events carry verified: false.

Voidhash processes each store event once: a repeat sends no second webhook, and a late event can't move a subscription back to an older state. An event for an unmapped store product is applied once you map it, if you do so within a year. See Unmapped purchases.

States

PurchaseActiveAuto-renew offBilling issueEndedrenewrefund, revoke, cancel now

A refund, revocation or immediate cancellation ends a subscription at once (dashed).

Statuses

Voidhash stores two statuses, which webhooks report in status. A subscription is canceled once it expired, was revoked, or was refunded on the App Store. Until then it is active, including trials, grace periods and auto-renew off, which sets cancelAtPeriodEnd and canceledAt instead. The SDKs and Studio derive more detailed statuses from status and the dates:

SituationAPI and webhook statusSDK current subscriptionSDK historyPeople label
Free trialactive, isTrial: truetrialingtrialingActive
Paid and renewingactiveactiveactiveActive
Auto-renew off, period not overactive, cancelAtPeriodEnd: trueactive or trialingactive or trialingCancels at period end
Renewal failed, in a grace periodactiveLeft outexpiredGrace period
End date passed, store hasn't reported the endactiveLeft outexpiredExpired
Ended: expired, revoked, or refunded on the App StorecanceledcanceledcanceledExpired (Canceled while its end date is still ahead)

The SDK's current subscription prefers an active subscription, then trialing, then canceled. One that isn't canceled drops out once its end date passes, even while a grace period or the 24-hour buffer below keeps its perks. A canceled one stays a candidate after its end date. With no candidate, it is null. The SDK schema also lists none and past_due, which Voidhash never sends. Use these statuses for account screens, such as "Renews on"; gate features on perks.

When perks end

Perks stay active until the later of expiry and the end of any grace period, plus 24 hours for late renewal notifications. A revoked or ended subscription loses its perks at once. A one-time purchase keeps them until it is refunded or revoked; a partial refund doesn't remove them.

Grace periods and billing retry

When a renewal payment fails, the store retries it. If you turned on a grace period (Billing Grace Period in App Store Connect, or a grace period in Google Play Console), the person keeps access while the store retries: Voidhash keeps their perks until the grace period ends, plus the 24 hours above. The subscription stays active and no webhook fires. Without a grace period, perks end at the expiry date plus 24 hours.

If the payment recovers, the subscription renews (subscription.renewed) and the perks return. Google Play's account hold works the same way, with access until the expiry Google reports. Payment sources report past_due, which starts billing retry without a grace period.

Lifecycle events

ChangeAccessWebhook
PurchasePerks grantedsubscription.created
Free trial startsPerks granted; isTrial: truesubscription.created
Trial converts or the subscription renewsPerks extended to the new expirysubscription.renewed
Auto-renew turned offPerks kept until expirysubscription.cancelled
Auto-renew turned back onNo changeNone
Grace period or billing retryPerks kept, see Grace periodsNone
Payment recoveredPerks extendedsubscription.renewed
ExpirationPerks endsubscription.expired with expirationReason: "ended"
Revocation (Family Sharing removed, Google revoke)Perks end at oncesubscription.expired with expirationReason: "revoked"
RefundDepends on the store, see belowpurchase.refunded
Product changeNew product's perksDepends on the store, see below
One-time or consumable purchasePerks grantedpurchase.completed
One-time purchase refunded or revokedPerks endpurchase.refunded or purchase.revoked

subscription.cancelled means auto-renew is off, not that access ended; end access on subscription.expired. A subscription Stripe ends immediately sends only subscription.expired. See Webhook events for payloads.

Store differences

Trials

  • App Store: only a free-trial introductory offer counts as a trial. Pay-up-front and pay-as-you-go offers are paid periods.
  • Google Play: a free-trial phase is a trial. Without a reported phase, any offer at the start is one.
  • Stripe: a period that starts before trial_end is a trial.

Refunds

  • App Store: refunding the current period ends the subscription and its perks at the refund date and sends subscription.expired. Refunding a past period changes nothing.
  • Google Play and Stripe: a refund records the money; access ends when the store expires or revokes the subscription. A lost Stripe dispute is a refund with refundReason: "chargeback_lost".
  • Payment sources: a reported refund doesn't end the subscription.

A reversed App Store refund restores the period. No webhook fires for it.

Product changes

StoreUpgradeDowngrade
App StoreImmediate; same subscription; subscription.renewedWaits for the next renewal; subscription.renewed then
Google PlayA new subscription replaces the old: subscription.created for it, subscription.expired for the oldSame as upgrade when it takes effect
Stripesubscription.product_changed at the switch, then subscription.renewed when it is chargedApplied when the scheduled change takes effect

Other differences

  • Resubscribing after expiry continues the same subscription on the App Store (subscription.renewed). Google Play and Stripe start a new one (subscription.created).
  • Pausing on Google Play isn't modelled: access follows the recorded expiry.
  • Non-renewing subscriptions on the App Store mapped to a subscription product end one billing duration after purchase. Mapped to a non-consumable, they are a one-time purchase.
  • Payment sources report trialing, active, past_due, canceled or expired. past_due starts billing retry without a grace period.

Simulate the lifecycle

In the test environment, renew, expire, revoke, refund or start a 3-day grace period on a simulated purchase with the MCP tool simulate_sandbox_event, POST /api/v1/projects/{projectId}/sandbox/actions with a secret key, or the CLI's voidhash sandbox renew, expire, grace-period, refund or revoke. Access changes at once, and webhooks follow. See Simulate renewals, expiry and refunds.

Next steps

  • Webhook events: every payload field.
  • Check access from your backend with Node.js, PHP, Go, Rust or over HTTP.
  • Test purchases (Help center): store sandboxes and simulated purchases.
  • Products and perks: what each purchase grants.

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousOffersNextPeople and identity

On this page

How Voidhash learns about changesStatesStatusesWhen perks endGrace periods and billing retryLifecycle eventsStore differencesTrialsRefundsProduct changesOther differencesSimulate the lifecycleNext steps