Implementing Subscription Expiry Notifications in Mobile Application
User forgets about subscription — fact. Auto-debit processes, service continues working, but month later card blocks, payment fails, app stays silent. Person leaves thinking subscription just ended. Well-structured notification system retains these users before payment fails.
Server Events vs Local Notifications
First question: how to know when subscription expires? For In-App Purchase (StoreKit) Apple sends server events via App Store Server Notifications V2. For proprietary subscriptions — your server logic.
StoreKit 2 / App Store Server Notifications V2: Apple sends RENEWAL and EXPIRED events to server. On DID_FAIL_TO_RENEW — first signal to send push. On GRACE_PERIOD_EXPIRED — last chance. Important: Apple self-sends some system notifications, but can't rely only on them — build reminder business logic yourself.
// Server notification V2 (decoded payload)
{
"notificationType": "DID_FAIL_TO_RENEW",
"subtype": "GRACE_PERIOD",
"data": {
"bundleId": "com.example.app",
"transactionInfo": { ... }
}
}
Local Notifications: suitable only offline or without server. UNUserNotificationCenter with UNCalendarNotificationTrigger — schedule for expiresDate - 3 days. Problem: if user renewed via web or other device, local notification still fires. Without server sync this gives false positives.
Reminder Chain
One notification — bad strategy. Working B2C scheme:
- 7 days before expiry: "Subscription expires April 15 — renew to keep data"
- 1 day before: specific call with deeplink to subscription management
- Expiry day: notification with limited-time offer (if applicable)
- 3 days after: reactivation with discount (optional)
Deeplink in notification — mandatory. Push without action loses conversion. On iOS: UNNotificationAction with foreground — opens app and passes userInfo. On Android: PendingIntent with needed Intent.
// iOS: handle subscription expiry notification tap
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse) async {
let info = response.notification.request.content.userInfo
if info["type"] as? String == "subscription_expiry" {
NavigationRouter.shared.navigate(to: .subscriptionManagement)
}
}
On Android via FCM similarly: pass type: subscription_expiry in data payload, route in FirebaseMessagingService.onMessageReceived.
Segmentation and Timing
Monthly subscription user vs yearly — different situations. For monthly, 7-day window — 25% remaining time, aggressive. Yearly — 7 days of 365, normal.
User timezone critical: 3 AM push — annoyance and unsubscribe. Firebase FCM lets set delivery_time accounting for device local time. For APNs — server-side via task scheduler with user timezone.
Process
Audit current subscription system: StoreKit / server / hybrid.
Set up App Store Server Notifications V2 or server webhooks for expiry triggers.
Implement push notification chain with deeplink to relevant screen.
Test on StoreKit sandbox (Xcode → StoreKit Testing) and live FCM.
Timeline Estimates
Integration with ready notification server and StoreKit 2 — 2–3 days. Server logic with scheduler and user segmentation from scratch — up to 1 week.







