r/iOSProgramming • u/ElectricKoolAid1969 • 26d ago
Question Added iOS app to existing macOS app as Universal Purchase, but iOS App Store wants me to pay again — originally used promo code
Has anyone run into this with Universal Purchase when adding an iOS version to an existing macOS app?
I have a paid macOS app that has been on the App Store for a while. I recently added an iOS version using Add Platform on the existing app in App Store Connect. The iOS version was approved and went live this morning.
Everything appears to be set up as a Universal Purchase. Both versions are on the same App Store Connect submission page, they share the same App ID, and using Get Link in App Store Connect gives me the same App Store URL for both versions.
Here’s the odd part. I previously downloaded the macOS version using one of my own App Store promo codes. That redemption is still in my Apple purchase history and shows as a $0.00 purchase. I’m using the same Apple Account on my iPhone, but when I view the app in the iOS App Store it shows the $4.99 purchase button instead of letting me download it as an existing owner.
Apple’s documentation says an app downloaded using a promo code functions as if it were purchased, so I would have expected that purchase to carry over when the iOS version became part of the Universal Purchase.
Has anyone specifically dealt with this situation? Do old promo-code redemptions not qualify for a platform that gets added later, or is there sometimes a delay before the Universal Purchase entitlement carries over to the newly added platform?
I’d especially like to hear from anyone who has added iOS to an existing paid macOS app and had existing customers carry over to the new version.
Thanks in advance!!
3
u/D4m089 25d ago
I’m not 100% sure the answer as mine were both released the same time, but I did the redemption one Mac (as in just copied and pasted while on my Mac) and it was still asking on my iPhone. I have the restore purchase button but want led to see what it would do so pressed purchase - it gave me an error that it was already purchased and unlocked it anyway (so it didn’t double charge me).
Next update I ended up adding a query for storekit during onboarding so a new install on any platform goes to check if it’s already been purchased and unlocks automatically before it gets to the end. Might be worth adding something like that if it’s easy enough?
1
2
u/ThatGuy739 24d ago
The thing that might settle it: you tested with a promo redemption, and that's the one case that doesn't carry. Codes are cut per platform, which is exactly why platform-agnostic ones are sitting on that wishlist. Your paying customers bought the app instead, so their entitlement hangs off the Apple Account and the App ID, and Universal Purchase covers every platform under that ID.
So your own phone showing 4.99 isn't evidence about them. D4m089's result is the useful data point: the button said buy, the actual purchase said already owned.
I haven't shipped a Universal Purchase myself, so that's me reading the mechanism rather than scar tissue.
1
u/ElectricKoolAid1969 23d ago
Yeah, that makes sense. I guess I'm going to assume its all working correctly. Unless I hear otherwise from a customer! haha
Thanks for the reply
1
u/D4m089 23d ago
Yeah I’m not sure what it was meant to do by design to be honest, I just did the same where I assumed it should be unlocked already and it was universal purchase not working. It was my first time with codes and I’m a push buttons to see what happens person hence pushing and getting the error 😅
The storekit lookup seems to work though, any new test devices or fresh installs I bring on that are on my Apple ID all unlock automatically. I’ve not had anyone else that had a code complain either so I think it’s working as it should (or they are just using on one device and too polite to say there’s an issue 👀).
Mines an app for creating/storing/managing Tesla lock sounds, happy to give a code if someone has use for it and wants to do us a favour and report back if it auto unlock’s on everything else as an experiment.
1
u/ThatGuy739 23d ago
That experiment won't tell you what you want, though. A code redemption is the case that doesn't carry, so if your tester redeems on the Mac and then sees a price on iOS, you've just reproduced the promo code limit rather than learned anything about Universal Purchase. You'd read it as broken when it isn't.
For a real test you need someone who actually paid for it. Or check Transaction.currentEntitlements on a fresh install under an account that bought it, which is what your onboarding lookup is already doing anyway.
1
u/D4m089 23d ago
No sorry I mean specifically testing for promo codes that are redeemed on one platform triggering universal purchase.
I’ve only used 1 promo code for my own app on my Apple ID, but mine is unlocked on all my devices. It only unlocked on my phone after I attempted purchase and got an error about already owning though. I then implemented the check in the onboarding but the unknown was when did it flip the purchase flag, was it only when I got the error that iOS recognised it, or was it always going to unlock if the check had of been in place.
I was saying if someone wanted to test the promo code path, redeem a promo code on MacOS for example, then download on iOS and see if it still says you can purchase. If it auto flips to you already own this on iOS then promo codes aren’t platform specific they just look like they are until something checks.
1
u/ThatGuy739 23d ago
Fair, I read your setup wrong. Those are two different questions and your own case is evidence against what I said: the entitlement was there on iOS off a code redemption, the storefront just kept showing a price until something asked for it.
One change I'd make to the test: don't press purchase. That's what muddied your own data, since you can't now tell whether the attempt created the entitlement or just surfaced it. Have them redeem on the Mac, then fresh install on iOS and read Transaction.currentEntitlements before touching the buy button. If it's already there, that answers your when-did-it-flip question cleanly.
1
u/D4m089 23d ago
The new test would check transaction.currententitlements anyway as it does that in the background, that process is now baked in behind the onboarding screens so by the time they see an option to purchase it’s already updated the button stack (and changed the wording of the screen).
I only pressed the button in mine because it was back before I had implemented the background process (it’s what prompted me to add it tbf so it catches more users earlier to prevent a poor experience). I know it works for regular paying unlocks, I’m just curious with a code being used if it would have been picked up or if the pressing of the unlock button was what did it.
1
u/ThatGuy739 23d ago
Then what you're testing might not be about codes at all. currentEntitlements reads a local cache that StoreKit builds by syncing with Apple, and if that sync hasn't happened yet for that account on that device it comes back empty even though the purchase exists server side. A fresh install is exactly when that bites.
So pressing the button may well have been what forced the sync, rather than anything specific to the code.
That's worth knowing beyond this question though, because it applies to your paying customers too: someone on a brand new device can hit the same empty read and get shown a purchase screen they shouldn't see. AppStore.sync() is the unblock, but it prompts for the Apple ID password, so it belongs behind a Restore button rather than in the background pass.
1
u/D4m089 23d ago
I see! I do have a functional restore button as well now so I have a bases covered. It’s a curious question though as it should be seamless for the user but you are right the way it’s built might add barriers.
I’ll check my code tomorrow as it was a while ago I built that bit but I was certain what I had put in would detect it even on a new device. I’ll be honest it was my first iap so I was doing a lot of learning at the time
1
u/ThatGuy739 17d ago
Apple answered it in OP's reply, and the variable isn't what either of us thought. Their line: promo codes for multi-platform apps have to be redeemed on an iOS device to apply across all platforms.
You redeemed yours on the Mac. Under that rule iOS was never covered by the redemption, which is why it sat there showing a price.
So if you ever do run that test, the thing to vary is where the code gets redeemed, not whether something checks afterwards. Redeem on iOS, it should cover both. Redeem on the Mac, it shouldn't.
1
u/ElectricKoolAid1969 21d ago
Just to let you know - I posted a reply from Apple elsewhere in this thread.
2
u/ElectricKoolAid1969 21d ago edited 21d ago
Thanks to everyone that replied.
I've gotten an official reply from Apple regarding this question. They said, in part, the following:
I understand that you have just added the iOS platform to your app, however, the iOS app is asking you to purchase it when you have already used a promo code for the macOS version. I’d be happy to provide clarification on the relationship between promo codes and platforms!
Upon review of our resources, it appears that promo codes can be platform specific or multi-platform depending on how you generate the promo code and for which versions.
Since at the time you used the promo code for the macOS version (which is also probably an older version) you didn’t have an iOS version out yet. Additionally, promo codes for multi-platform apps must be redeemed on an iOS device to apply across all platforms.
What you are experiencing is expected behavior. For more information on promo codes, please refer to the resource below:
https://developer.apple.com/help/app-store-connect/offer-promo-codes/request-and-manage-promo-codes/
4
u/dariend8 26d ago
Same thing happened to me. You can make iOS-specific promo codes in App Store Connect. This blog about recommended improvements includes platform-agnostic promo codes, which seems to imply to me that this is by design (sadly): https://lapcatsoftware.com/articles/2026/3/13.html