Skip to content

Cross-platform subscriptions and coupons

Moved from vulture-ios/Docs/cross-platform-subscriptions.md

How a Vulture Pro subscription could be shared between the Apple apps and an Android app, and how one coupon could work on both. Written 2026-10-19 as a plan: nothing here has been built, and the RevenueCat endpoint and dashboard names are from memory — check them against RevenueCat’s current documentation before building on them. The Android side is the subject of Docs/swift-on-android-investigation.md.

RevenueCat ties a subscription to an App User ID, never to a store. Two apps that log in with the same ID see the same all entitlement, wherever it was bought.

AppStoreSubscription.identifyUser() (Modules/App/Sources/Services/Subscriptions/AppStoreSubscription.swift) logs in with the CloudKit user record name of iCloud.be.fousa.vulture.cloud, falling back to RevenueCat’s anonymous ID when iCloud gives none. That is why a subscription already follows a pilot across iPhone, iPad and Mac. Android cannot read a CloudKit user record, so it has no way to arrive at the same ID by itself.

Without one, RevenueCat sees two customers. The candidates are Sign in with Apple (which Android can use through its web flow), email, or an account on the vulture.aero backend.

The CloudKit record name stays the RevenueCat ID

Section titled “The CloudKit record name stays the RevenueCat ID”

When an iOS pilot creates or links an account, the account stores their CloudKit record name as its RevenueCat ID. Android signs in, reads that ID from the account and calls Purchases.logIn(id). A pilot who started on Android has no CloudKit ID: the account gets a fresh ID, and the iOS app logs in with the account’s ID instead of the record name.

Existing subscribers need no migration this way, because their purchases already sit under the record name. Switching iOS to a new account ID instead would lean on aliasing or on restores transferring purchases, which is where it gets messy.

  • The Play Store app joins the same project.
  • The Google products attach to the same all entitlement as the Apple ones.
  • Restore behavior: the default, transfer to the new App User ID, lets a restore move a subscription between accounts; keeping it with the original App User ID is stricter and stops one subscription serving several accounts.
  • A subscription is managed and cancelled in the store it was bought in. Link to RevenueCat’s managementURL, which names the right store, never to the App Store directly.
  • Prices are set per store.
  • The paywall stays away when the entitlement is already active from the other store, so nobody pays twice. The app reads the all entitlement (PurchaserInfo+Active.swift), not the store, so it does this already.

Apple’s Offer Codes and Google’s promo codes each belong to one store; no code is accepted by both. RevenueCat leaves two ways around that, and both need the shared account of the previous section, or the code reaches only the device that redeemed it.

Our own codes on our own backend, each with a duration (“one month of Pro”). Redeeming one calls RevenueCat’s REST API to grant a promotional entitlement to the account’s App User ID (v1: POST /subscribers/{app_user_id}/entitlements/{entitlement_id}/promotional with a duration; the v2 API has an equivalent grant). Pro unlocks on every platform at once.

It is not a subscription: nothing renews, and when it ends the paywall comes back. It suits gifts, clubs and competitions.

A subscription sold on the web through RevenueCat Web Billing, or Stripe connected to RevenueCat, with Stripe promotion codes for the discount. It renews, the entitlement reaches iOS and Android once the pilot is logged in, and the codes are entirely ours.

No code field in the iOS app that unlocks Pro. Guideline 3.1.1 bars apps from “their own mechanisms to unlock content”, license keys and the like among them. The code is redeemed on a web page (vulture.aero/redeem, say) where the pilot signs in. Guideline 3.1.3(b), Multiplatform Services, then lets the app honour access bought or granted elsewhere, as long as Pro is also for sale through in-app purchase — which it is.

  1. The account, keeping the CloudKit record name as the RevenueCat ID. Everything else depends on it.
  2. A web redeem page granting promotional entitlements through the REST API — cheap, and enough for free months.
  3. Web Billing with Stripe codes, only if renewing discounted subscriptions are wanted.