Promotional offers for existing subscribers 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
Promotional offers for existing subscribers 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 Promotional Offers for Existing Subscribers in Mobile Applications

Promotional Offers are personalized discounts for users who have already been subscribers. Introductory Offer can be offered only once and only to a new subscriber. Promotional Offer can be repeated and only to those who have had or have an active subscription. Typical scenarios: bring back a user who cancelled their subscription; prevent cancellation with a win-back offer; transition to a higher tier with a discount.

Difference from Introductory Offers

Introductory Offer Promotional Offer
For whom New subscribers Existing/former
How many times Once Multiple times
Requires server signature No Yes — mandatory
Configuration App Store Connect App Store Connect + server

Server signature is the key difference. Apple requires that the offer be signed with a private key generated in App Store Connect. Without this, the offer won't apply — StoreKit will return an invalidSignature error.

Configuration in App Store Connect

  1. Subscriptions → [Subscription] → Promotional Offers → +
  2. Set Reference Name, Offer ID, type (freeTrial / payAsYouGo / payUpFront), duration, and price
  3. Save the Offer ID — you'll need it when generating the signature

In parallel: Keys → Subscription Key → create a key, download the .p8 file, and remember the Key ID.

Server-Side Signature

The server generates a signature using the ECDSA algorithm with the .p8 key. Parameters:

  • appBundleId — application bundle ID
  • keyIdentifier — Key ID from App Store Connect
  • productIdentifier — product ID
  • offerIdentifier — Offer ID
  • applicationUsername — user ID in your system (optional but recommended)
  • nonce — UUID generated by the server
  • timestamp — current time in milliseconds

Signature is created from concatenating these values through \n, signed with SHA-256 ECDSA:

# Python example for server (simplified)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
import base64, uuid, time

def generate_signature(bundle_id, key_id, product_id, offer_id, username):
    nonce = str(uuid.uuid4()).lower()
    timestamp = str(int(time.time() * 1000))

    message = "\n".join([bundle_id, key_id, product_id, offer_id, username, nonce, timestamp])

    private_key = serialization.load_pem_private_key(PRIVATE_KEY_PEM, password=None)
    signature = private_key.sign(message.encode(), ec.ECDSA(hashes.SHA256()))
    encoded = base64.b64encode(signature).decode()

    return {"nonce": nonce, "timestamp": timestamp, "signature": encoded, "keyIdentifier": key_id}

The server returns this data to the client; the client uses it when confirming the purchase.

Application on the Client (StoreKit 2)

import StoreKit

// Get signature parameters from server
let signatureData = try await apiClient.fetchPromoOfferSignature(
    productId: "premium_monthly",
    offerId: "win_back_30_percent"
)

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

// Find the offer by ID
guard let offer = product.subscription?.promotionalOffers.first(where: {
    $0.id == "win_back_30_percent"
}) else { return }

// Create signed offer object
let signedOffer = try await offer.purchase(
    confirmIn: self, // WindowScene or UIViewController
    options: [
        .promotionalOffer(
            offerIdentifier: signatureData.offerId,
            keyIdentifier: signatureData.keyIdentifier,
            nonce: UUID(uuidString: signatureData.nonce)!,
            signature: Data(base64Encoded: signatureData.signature)!,
            timestamp: signatureData.timestamp
        )
    ]
)

Common Mistakes

Expired timestamp. Signature is valid for 24 hours. If you cache it longer — Apple will return an error. You need to generate the signature immediately before showing the paywall, not at app startup.

Invalid nonce. Nonce should be lowercase (UUID.uuidString.lowercased()). Case affects signature validity.

Offer shown to everyone. Checking eligibility for promotional offer is the developer's responsibility. Apple doesn't block the purchase if eligibility wasn't verified. You need a server-side check of transaction history: was the user a subscriber at least once?

What's Included in the Work

  • Setting up Promotional Offer in App Store Connect
  • Server endpoint for signature generation (ECDSA)
  • Client-side StoreKit 2 integration with promotionalOffer options
  • Eligibility check (server transaction history)
  • Testing in Sandbox via StoreKit Configuration File

Timeline

3–5 days — including the server part (signature generation). If server infrastructure is already ready — 2–3 days. Pricing is calculated individually.