Disable restore IAP when same Apple account but different user account in application - ios

In my app I have items to show just for users that are pays for subscription. User can log to app by e-mail and password and can logout and on same device can log different user.
My problem is that Apple Id in phone is still same. So when different user logged in he could restore purchase even that he didn't pay anything.
So my question is how can I fix it? How can I connect Apple account to my custom account? Or at least somehow when trying to restore IAP check that this Apple account already have this subscription but different user was logged in. How other apps do this? Thanks
Edit:
I want to use Auto-renewing subscription in my app and I just don't know how to connect it to my custom account system.

When a user makes an IAP, you will not know the Apple Id used to make the purchase. What you will know, however, is the transaction id for the purchase. What you will want to do store the transaction id of the original purchase with your custom account.
When a user restores, you will determine if the SKPaymentTransaction's originalTransaction's transactionIdentifier matches the custom account. If not, then you can assume this is a different user. You can read more about that here:
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Restoring.html
Note that the receipt contains a field called the original transaction id. This is what you would use for subscriptions to track the original transaction id. This is because each time you auto renew, a new transaction id will be generated for the auto renew. The receipt will actually contain all the purchases.
On your server, you would want to save the original transaction id and potentially the receipt. Essentially the more metadata your store around this, the better off you will be if you have to do any form of double checking transactions.

Related

iOS & Firebase Auto-renewable Subscriptions

My problem:
I am having a hard time figuring out a way to safely manage auto-renewable subscriptions in iOS with Firebase.
Purchase process:
User1 purchases a subscription
Update User1's account on Firebase w/ the subscription identifier
(used to unlock content)
Store original_transaciton_identifier(OTI) w/ uid of User1 to match w/ receipt verification from Apple.
Grant user access
Edge cases causing my brain to implode:
User1 logs out of AppleId used to purchase subscription, but remains logged in to app w/ Firestore credentials.
Therefore, when I go to verify if the subscription has expired it does not return a valid subscription. I want the user to be able to keep their access until it is expired or canceled. Any tips on achieving this?
User2 logs into the same device User1 was previously using.
Therefore, the same AppleId is being used for both users. I can check if the current user has a subscription, and check the OTI to see if it corresponds to User2...which it won't.
We will show the 'purchase iAPs' screen, but what if this user wants to buy a subscription as well under the same AppleId? Is it normal for me to handle this saying, "Apple Id already connected with another account or something"?
Relevant articles I've been able to find:
How to tie auto-renewable subscriptions to in house user, not appled id
I've been struggling with this for sometime and haven't been able to find many resources. All help is appreciated.
For case #1:
When you attempt to access the receipt Apple will trigger a login prompt for the user to enter their iTunes credentials. If a receipt is still unavailable, you won't be able to verify the subscription status. The "right" way to do this is to store the entire receipt on your server, and periodically refresh it with /verifyReceipt. You'll check if the subscription was cancelled, and update the expiration_date so you know when to cut off access for the user.
For case #2:
Is it normal for me to handle this saying, "Apple Id already connected
with another account or something"?
Yes! If you're able to look at how some other large subscription apps handle this (Netflix, Spotify, HBO, etc.) - it's similar to what you describe. Instead of checking the receipt locally every time, if you maintain the subscription status on your server (as mentioned in #1) this would only happen if the user tries to "Restore Purchases".
This is a pretty extreme edge case, since not many people try to make a purchase on their friends phone and would require TouchID/FaceID in most cases - so it's more of a fraud prevention feature. Once you get millions of users you can get fancy and send them an automated email link to signup with Stripe if you detect this.
Alternative:
RevenueCat can handle all the subscription tracking and these edge cases out-of-the-box, and it plays nice with an official Firebase integration. Disclaimer: I work there.

iOS In App Purchase Receipt Duplicate Management

When building In App Purchases into an iOS app, Apple gives you access to a receipt that you can send to your server to view all In App Purchase transactions for that given iTunes user related to your application.
If in my application I require users to login or create an account through my system before making a purchase, how can I guarantee that iTunes receipt will not be duplicated?
For example, if a user makes a purchase, the application sends the user ID, and iTunes receipt to my server. The server then stores that receipt along with the user ID in a database. The next time the server wants to check the users purchases we can retrieve all the receipts for that given user ID out of the database and run checks on it.
But if the user logs out and logs in as a different user and makes another purchase the receipt will be sent to the server again, with a different user ID. Effectively, allowing 1 purchase to be used on unlimited user accounts.
Due to the fact that the receipts change over time (auto renewing subscriptions, etc), I don't think it's safe to rely on the receipt being the same every time it's sent to my server and using the receipt data as a unique identifier.
I also considered storing the original_transaction_id for one of the transactions, but I don't think that would work since an application might have multiple non-consumable items, or multiple subscriptions, therefor there might be multiple completely valid original_transaction_id's in one receipt. So it doesn't really associate that receipt, but just the transaction.
Ideally, I'd like a system where when a user logs out, and logs in (as a different user or same), the application will send the receipt to the server, and dissociate any of the same receipts for all existing users, and link this new receipt to the user that logged in. Problem is, it doesn't look like there is any type of unique identifier for a given receipt that I can use to check for duplicate receipts in my system.
What is the best way to detect duplicate receipts sent to my server (so I can dissociate the old receipts accordingly)?
PS. I think this system is most important for subscriptions, and non-consumable items. For consumable items it will always have to be linked to 1 specific user, and I can just store the transaction_id and original_transaction_id for that consumable item to ensure it doesn't get duplicated.

How do apps tie accounts with in-app purchase subscription?

I'm working on an iOS app where a user must sign up/sign in to an account for my app. To be able to use my services I want users to pay via in-app purchases (auto-renewing in my case). This would then tie their IAP to their account (they created with us) to be able to use on any other iOS device.
Case 1:
For example if a subscription was bought on phone A for an account, when signing on to phone B (using the same account) shouldn't make the user pay again.
Case 2:
Or if a subscription was bought for account A on a phone, when signing up for account B on the same phone should make the user pay for account B.
Basically I want an in-app purchase to tie to my account (rather than a apple device/Apple account which is how it works to my understanding.)
I understand that their are receipts which is probably part of the answer to my question. Or if this a limitation to in app purchases what other ways can this be done (other than using Apple's IAP)
The answer to the title of the question:
When a purchase is made there is an update received in the app, the next time it is launched which contains a transaction_id (original_transaction_id for renewals), you should associate this transaction id with your app user id.
Case 1: Apple provides an option to "Restore Purchases" in your app. Since, the same account is used, your app servers would already know that a user has already paid and the app shouldn't prompt the user to pay again. However, in order to receive the upcoming renewal updates on phone B, you must "Restore Purchases" which would let Apple link the previous purchase on this device as well. Once the restore is done, the users purchase receipt will be available on the device and all subsequent actions should be taken based on the content validation of the receipt.
Few points here:
Apple will reject your app, if it doesn't provide this option to the user.
Even if your app is buggy, and you failed to restore purchases, user wouldn't be charged again. The purchase would fail saying you already have purchased this.
The above case is completely based on the assumption that the user is using the same Apple Id on both the devices(same application user id doesn't matter).
Case 2: I am not sure if that is anyhow possible. Since it's the same device, the user uses the same AppleId(unless he signs out and changes apple id in App Store) and Apple will restrict the repurchase. At best you may restrict the account B with the subscription content but if your application allows multiple accounts on the same device use case(e.g Instagram), I am not sure if there's a provision for the same.
I tested the same scenario for a music service app I use. Have an active subscription, logged out and signed up with other email (app), but the purchase was denied alerting I have already purchased. However, it was allowing me upgrade and downgrade options. I didn't opt for testing them so not to mess up my account.
P.S: There are a lot of other caveats/gotchas to take care of. Some of them:
In case 2, apple restricts the repurchase but it doesn't restrict if the user chooses an option other than the current active subscription, which will upgrade/ downgrade the subscription. Based on how you have implemented things, it might even change the subscription for user A immediately or next time the user logs back in or on next renewal thereafter.
In both the cases discussed here, user uses the same Apple Id, but consider the cases - same Apple Id different app id AND different Apple Id and same app Id. (See this)
For case 2, you should always refresh receipts or restore purchases(if there's no receipt available) for a fresh user login. The receipt has a original transaction id which you should have linked the first time the purchase was made with user A. Based on the receipt, you can either choose to let user B access the content or restrict. However, you cannot let the user pay for the new account(I suppose so).
Helpful Links:
Restoring Purchases
Receipt Validation

Apple inapp Non-Renewing subscription purchase, force-require login?

here's my straightforward question:
is it allowed for my iOS application to force users to log into my custom account management system, so I can link them to my backend when they want to purchase a non-renewing subscription?
Here's the backstory:
I have a non-renewing subscription. For a good amount of users I can see that the Apple receipt does not contain information about this subscription; the in_app array is empty. This is correct according to the API:
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Products.html
The in-app purchase receipt for a consumable product or non-renewing subscription is added to the receipt when the purchase is made. It is kept in the receipt until your app finishes that transaction. After that point, it is removed from the receipt the next time the receipt is updated—for example, when the user makes another purchase or if your app explicitly refreshes the receipt
For some users, I can still see the purchase in the receipt, but let's consider that a bug from Apple and follow their API documentation to the letter.
(More on why this is a bug here https://forums.developer.apple.com/thread/22345)
In order to restore the user's purchase, I'd need something to identify him with. I could create a unique token, store that on the iCloud keychain and use that across the devices to detect the purchase belongs to that Apple account, but since my application supports account creation, I'd rather just use that mechanism. Else I'm using two different methods of purchase detection side by side: iCloud keychain token or a user account.
Thing is, I'm not sure I am allowed to force users to make an account before making a purchase. Is this something Apple would reject the app submission for with a message like "Your users must be able to buy stuff without going through your lengthy account registration process" ?
From Apple :
Non-renewable subscriptions. Subscriptions that don’t involve
delivering episodic content. Examples include access to a database of
historic photos or a collection of flight maps. It’s your app’s
responsibility to make the subscription available on all of the user’s
devices and to let users restore the purchase. This product type is
often used when your users already have an account on your server that
you can use to identify them when restoring content. Expiration and
the duration of the subscription are also left to your app (or your
server) to implement and enforce.
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Products.html
So its your app's responsibility to check that user has valid subscription or its over and let them purchase it again !
For this you will ask user to first make account or login into app so by this way you can track their subscriptions.

ios auto renewal subscription and user in our service

In our service, each user has a username (email), password and a unique identifier. How can I link any purchases through Apples' auto renewal subscription to that user?
Is there any field that renders an Apple user as unique?
I don't want to link it to the device, because the user can run the app from many devices, so I want a unique identifier for the user.
Apple does not provide you a unique identifier for an Apple user or Apple ID (probably to help protect the users privacy). What I was doing was using their first Auto-Renewing receipt as their unique identifier. So you should be able to use the original_transaction_id in your receipt verifications. When you verify a receipt with Apple's servers, the response should include the transaction id of the original auto-renewing subscription that that user purchased from you regardless of which device they were on. And I've found that even if a subscription lapses and then starts again, there will always be the one original auto-renewing transaction that is referenced.
It's a bit of a trick but it seems to work.
Alternatively, you could also use an iCloud key-value store to store a unique ID. That ID will be the same on all a user's devices, but they have to have iCloud turned on on all their devices. And technically they could be using different accounts for iCloud than they're using to make purchases so this isn't perfect.

Resources