ixsoftum
What happened when a $4k/month app moved from one-time purchase to subscriptions
Ship and monetizeNarrative

What happened when a $4k/month app moved from one-time purchase to subscriptions

Twelve months of payout reports from a two-person iOS utility: what the migration earned, what it cost, and the two months it looked like a mistake.

ixsoftum Editorial·Published 29 Jul 2026·4 min read
Key takeaways
  • Revenue fell 34% in the first month after the migration and did not pass the old baseline until month four. Plan cash flow for a full quarter below trend, not a bad week.
  • Grandfathering existing purchasers permanently cost nothing in revenue and removed the entire category of one-star reviews about paying twice.
  • Annual plans refunded at 4.1% in the first 45 days against 0.7% on monthly, almost entirely because annual was the pre-selected option on the paywall.
  • Twelve months on, gross is 2.3x the pre-migration run rate; take-home after commission, refunds and tax is 1.7x. That gap is the number worth planning against.

The app is a file-transfer utility for iOS, built and maintained by two people, on sale since 2021 at a one-time price of $14.99. In March 2025 it earned $4,180 gross across 279 units. Twelve months later, on a subscription model, it earned $9,640 gross in a month, and the developers took home less than that difference suggests, for reasons that are entirely predictable and were not predicted.

The payout reports were shared with ixsoftum on condition that the app is not named. Figures below are reproduced from App Store Connect financial reports, not from analytics estimates.

The numbers before the change

One-time purchase revenue on this app was flat, not declining. That matters, because the usual argument for migrating is a decaying install base. Here the case was different: support load was rising with each OS release, and a fixed $14.99 taken once in 2021 was funding maintenance in 2025.

MonthGrossUnitsRefunds
Dec 2024$4,310288$104
Jan 2025$3,970265$74
Feb 2025$4,062271$89
Mar 2025$4,180279$59

Source: App Store Connect financial reports, provided by the developers. Commission on all units was 15% under the App Store Small Business Program.

What the migration actually involved

Existing purchasers were grandfathered permanently: every feature they had paid for stayed unlocked, with no expiry and no nag. New installs got a two-tier subscription at $3.99/month or $29.99/year, with the previous feature set behind the paid tier and a genuinely usable free mode.

The entitlement check for grandfathered users reads the original transaction from the receipt rather than a server flag, which is what kept the migration from needing accounts:

swift
// Legacy buyers keep everything; no server, no account.
func legacyEntitlement() async -> Entitlement {
    for await result in Transaction.all {
        guard case .verified(let t) = result else { continue }
        if t.productID == "com.example.app.pro",
           t.purchaseDate < migrationDate {
            return .grandfathered
        }
    }
    return .subscriberOrFree
}

Reading Transaction.all rather than Transaction.currentEntitlements is deliberate: consumed and revoked transactions still need to resolve for people who bought on a family-shared account. Apple's StoreKit Transaction documentation is explicit about the difference; the App Review team is not, and one build was rejected before this was corrected.

Where the revenue went

Month one after launch was down 34% against the trailing average. Month two was down 11%. The line crossed the old baseline in month four and kept going. This shape is consistent with what RevenueCat's State of Subscription Apps report describes for utility-category migrations, and the developers had read it beforehand; that did not make months one and two feel any different.

Gross monthly revenue, 12 months after migration
2,760M13,720M24,010M34,640M45,290M55,810M66,340M76,980M87,510M98,240M108,930M119,640M12
Source: App Store Connect financial reports (developer-supplied)

We budgeted for a bad first month. We did not budget for a bad first month while also answering forty support emails a day from people who thought we had taken their app away.

Developer submission, name withheld at their request. Figures cross-checked against the supplied payout reports.

The refund window nobody planned for

Annual plans refunded at 4.1% in the first 45 days, against 0.7% on monthly. The cause was mundane: the annual price was the default selection, and a portion of buyers did not register that they had chosen it. Making monthly the default and moving annual to a secondary line cut annual refunds to 1.6% without measurably changing the annual share of new subscriptions.

Refunds are deducted from the payout period in which they are processed, not the one in which the sale occurred. If you are modelling this, do it on net payout, not on gross sales. A subscription income calculator that takes commission and estimated tax off a gross figure is the right starting point but not the finishing one.

What we would do differently

Three things, in order of how much they cost. Ship the grandfathering entitlement check in a release before the paywall goes live, so that the code path has been exercised in production against real receipts. Write the support macro explaining grandfathering before launch, not on day three. And model the first quarter on net payout after refunds, which for this app was 22% below the gross figure that the launch plan was built on.

Whether this was the right call is now a settled question for this app and an open one for yours. The migration worked because the app had ongoing costs, an active maintenance burden, and a free tier that was honestly useful. Remove any one of those and the same numbers do not follow.

Sources
Related