Implementing Win-Back Campaigns for Cancelled Subscriptions in Mobile Applications
User cancelled subscription. Two weeks later they open the app again — either out of habit or after a push notification. This is the return window. Win-back campaign is a combination of technical tools: special offer via StoreKit, push notification via APNs, and logic to determine the "right moment" to show it.
Three Win-Back Channels
1. Push notification with deep link to offer — sent via FCM / APNs to devices of users who cancelled subscription N days ago.
2. In-app offer on next launch — for users who still open the app after cancellation.
3. Apple Win-Back Offers — native Apple mechanism (iOS 18+) that allows configuring automatic offer distribution via App Store without server infrastructure.
Apple Win-Back Offers (iOS 18+)
Latest tool — configured in App Store Connect → Subscriptions → Win-Back Offers. Apple herself determines eligible users (former subscribers of specific Subscription Group) and displays offer on app page in App Store.
On client you need to handle transaction that arrives when user activates win-back offer:
// Listen to Transaction.updates on startup
for await result in Transaction.updates {
if case .verified(let transaction) = result {
if transaction.offerType == .winBackOffer {
// User returned via Win-Back — unlock access
await restoreSubscriptionAccess(transaction)
await transaction.finish()
// Log for analytics
Analytics.logEvent("win_back_reactivated", parameters: [
"offer_id": transaction.offerID ?? "unknown",
"product_id": transaction.productID
])
}
}
}
Promotional Offers for Win-Back (iOS 14+)
For iOS below 18 or for in-app win-back use Promotional Offers with server signature (architecture details in separate service):
// Determine win-back candidates
func isWinBackCandidate() async -> Bool {
// Check: is there expired transaction and no active one
var hasExpiredSubscription = false
var hasActiveSubscription = false
for await result in Transaction.all {
if case .verified(let tx) = result,
tx.productType == .autoRenewableSubscription {
if tx.expirationDate ?? Date() < Date() {
hasExpiredSubscription = true
} else {
hasActiveSubscription = true
}
}
}
return hasExpiredSubscription && !hasActiveSubscription
}
// Show win-back paywall on app entry if needed
func showWinBackOfferIfNeeded() async {
guard await isWinBackCandidate() else { return }
guard let offerSignature = try? await apiClient.fetchWinBackSignature() else { return }
await MainActor.run {
presentWinBackPaywall(signature: offerSignature)
}
}
Push Notifications for Win-Back
Server-side logic: select users where subscription_expired_at BETWEEN NOW() - 7 DAYS AND NOW() - 3 DAYS and last_app_open > NOW() - 30 DAYS (still active). Send push via APNs:
{
"aps": {
"alert": {
"title": "Return to Premium",
"body": "Special offer: first month at half price"
},
"badge": 1,
"sound": "default"
},
"deep_link": "app://paywall?offer=win_back_50&utm_source=push&utm_campaign=winback_d7"
}
Deep link opens paywall directly with pre-selected win-back offer. UTM parameters are needed for conversion analytics by channel.
Segmentation — Not the Same Offer for Everyone
Win-back offers work better with segmentation:
| Segment | Trigger | Offer |
|---|---|---|
| Cancelled < 7 days ago | Next open | Pause instead of cancel (Google Play) |
| Cancelled 7–30 days | Push on day 7 | 30% discount on first month |
| Cancelled 30–90 days | Push on day 30 | Free trial for 2 weeks |
| Cancelled > 90 days | Seasonal campaigns | Maximum discount |
The longer user hasn't returned, the more aggressive the offer — this is standard churned user re-engagement model.
Measuring Effectiveness
Essential metrics:
- Win-back rate: (reactivated / candidates) × 100
- Time to reactivation: median days between cancellation and return
- LTV reactivated: compare with LTV of users without cancellation
- Offer conversion by segment: which offer works best for which segment
// Log win-back offer display
Analytics.logEvent("win_back_offer_shown", parameters: [
"days_since_cancellation": daysSinceCancellation,
"offer_type": offerType,
"segment": userSegment
])
What's Included in the Work
- Determining win-back candidates (client + server)
- Apple Win-Back Offers (iOS 18+) or Promotional Offers as fallback
- In-app paywall with win-back offer on next launch
- Push notification with deep link to offer
- Segmentation by time since cancellation
- Analytics: funnel display → click → purchase
Timeline
3–5 days — in-app flow with Promotional Offers. With push infrastructure and server-side segmentation — 5–10 days. Pricing calculated individually after analyzing requirements.







