Introductory offers first-period discount in mobile app

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Introductory offers first-period discount in mobile app
Medium
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Implementing Introductory Offers (First Period Discount) in Mobile Applications

Introductory Offers is a StoreKit mechanism that allows you to offer new subscribers a reduced price or free trial period on the first billing cycle. Apple supports three types: freeTrial (free), payAsYouGo (pay for each period at a reduced price), and payUpFront (pay once for multiple periods at a reduced price). Without proper client-side implementation, users won't see the introductory offer.

Configuration in App Store Connect

Introductory Offer is created at the subscription level in App Store Connect → Subscriptions → [Subscription] → Introductory Offers. You need to specify the type, duration, and price. Important: the offer applies only to users who have never been subscribers of this subscription group. Apple verifies this on the server.

Reading the Offer via StoreKit 2

import StoreKit

// Load the product
guard let product = try? await Product.products(for: ["premium_monthly"]).first else { return }

// Check for introductory offer
if let intro = product.subscription?.introductoryOffer {
    switch intro.paymentMode {
    case .freeTrial:
        // Display: "First 7 days free"
        let days = intro.period.value // for example, 7
        let unit = intro.period.unit  // .day
        showFreeTrialBanner(days: days)

    case .payAsYouGo:
        // Display: "First month for $99"
        showDiscountedPriceBanner(price: intro.displayPrice, period: intro.period)

    case .payUpFront:
        showUpFrontBanner(price: intro.displayPrice, duration: intro.subscriptionPeriod)

    @unknown default: break
    }
}

Checking Eligibility — Key Point

Users see the introductory offer only if they are eligible. However, product.subscription?.introductoryOffer doesn't directly report eligibility — the offer is present in the object regardless of whether the specific user has the right to it.

Eligibility check via StoreKit 2:

// Check subscription status via Transaction
for await result in Transaction.currentEntitlements {
    if case .verified(let transaction) = result {
        if transaction.productID == "premium_monthly" {
            // User has been a subscriber before — don't show the offer
            userHasBeenSubscriber = true
        }
    }
}

Alternative: server-side check via App Store Server API (/inApps/v1/subscriptions/{originalTransactionId}): the server returns isInBillingRetryPeriod and full transaction history, which allows you to determine precisely whether the user has used the introductory offer.

Don't rely solely on client-side checks for business logic. Golden rule: UI decision on showing the offer on the client, validation of offer eligibility — on the server via App Store Server API or RevenueCat.

Displaying in Paywall UI

Typical scenario: the same paywall page shows different options depending on eligibility:

struct PaywallView: View {
    let product: Product

    var body: some View {
        VStack {
            if let intro = product.subscription?.introductoryOffer,
               isEligibleForIntro {
                IntroOfferBanner(offer: intro)
                    .transition(.opacity)
            }
            SubscriptionButton(product: product)
        }
    }
}

isEligibleForIntro is a @State or @Published property that is set after an asynchronous transaction check.

Using RevenueCat

If the project already uses RevenueCat, eligibility checking is significantly simpler:

Purchases.shared.getOfferings { offerings, error in
    if let intro = offerings?.current?.monthly?.product.introductoryDiscount {
        // RevenueCat checks eligibility via StoreKit itself
        Purchases.shared.checkTrialOrIntroductoryPriceEligibility(
            productIdentifiers: ["premium_monthly"]
        ) { eligibilityDict in
            let eligible = eligibilityDict["premium_monthly"]?.status == .eligible
        }
    }
}

What's Included in the Work

  • Reading and displaying introductory offer from the Product object (StoreKit 2)
  • Checking eligibility via Transaction.currentEntitlements or server-side validation
  • Paywall UI component with conditional offer display
  • Testing via StoreKit Configuration File in Xcode (sandbox without waiting 24 hours)
  • Analytics logging: offer display, conversion, offer type

Timeline

3 to 5 days depending on paywall UI complexity and presence of server-side validation. Pricing is calculated individually after requirement analysis.