IOS buy, erase phone, detect previously purchased - iphone

I got most of IAP purchase working, using user defaults etc
But supposing a user buys an app, then erases or updates to a new phone.
Then reinstalls the app.
How do I detect that the user has previously purchased the app?

That is what restore functionality is for. See the Restore section of:
https://developer.apple.com/documentation/storekit/in-app_purchase/offering_completing_and_restoring_in-app_purchases

There are three ways:
If you store some purchase indicator in the app keychain, it will be present when the app is re-installed. If you are doing subscriptions also include an expiration date you can check if a subscription is possibly out of date.
If you send the app receipt up to Apple, it will always give you back the most current purchase data - which includes any prior purchases. So on first launch, always send the receipt up to Apple to see if you might want to automatically restore a purchase made previously. Note that sometimes an application receipt will not be present immediately after install (although it should always be there), which is why you also need to allow for option 3.
There is also a SKPaymentQueue’s restoreCompletedTransactions() call you can make to basically replay previous purchase transactions. This is something you should wire to a button somewhere in your UI - Apple requires that. It's not something you want to automatically call as it can prompt for the user to enter their iTunes account password, so you should not rely on this method to restore purchases automatically, but it is good to have in place as a backup for the user.

Related

Removing autorenewable subscriptions from iPhone App

I have an iPhone app that also features autorenewable products as an in-app purchase. The products are subscriptions to our service for up to 1 year in the future. We wanted to remove the whole in-app-purchase and autorenewable product from our app in the next version.
To accomplish this, we removed the signup option inside our app, so no new user should be able to sign up. Now we would like to disable the automatic renewal for all existing users.
How can I accomplish this? Is it sufficient to remove the in-app-products for our app inside iTunes Connect? Do the users get notified about this?
According to Apple (see WWDC 2011 Session 510, In App Purchases for iOS and OS X, at the 48:55 mark), the only things you can do as a developer to prevent subscriptions from auto-renewing are:
Raise the price.
Remove the auto-renewing IAP product from iTunes connect.
In both cases, notification emails are sent to subscribers, though not immediately. The talk says Apple checks 10 days before a (yearly) subscription renewal and sends email at that time. It's not documented anywhere, though, so I'd treat that as an implementation detail.
I've done the latter (removing the product) several times with my own (monthly) apps, and it seems to work as advertised.
One important note: if your app is a Newsstand app, it must have at least one auto-renewing subscription available. If you remove the last one, the app will be removed form the App Store. Users who have already purchased it will still be able to use it, and will be able to download copies from the "previously purchased" section of the app store, but no new copies will show up for purchase in the App Store proper.
It will depend on how you've implemented your system. Do you check receipts (and provide data/service) from your own server, or do it all within the app directly with Apple's servers?
In iTunes Connect you can remove a product from sale, effective immediately or at a future date. I suspect that's enough to stop a renewing subscription. (Remember you can test this with shortened timescales in the Sandbox.) But if not:
If you use your own server to validate receipts, go and give it an incorrect shared secret so that the verification step fails. That means the subscription validity will return as false (although for the 'wrong' reason) so your customers won't be able to renew.
If you do it within the app, generate a new shared secret so the one within your existing structures is incorrect. Then, as above.
If you can, I suggest sending a notification to your current users notifying them of the change, suggesting that they change, and letting them know it will 'fail' in the future but that's ok.

How to restore purchase within in-app purchases?

I have implemented In-app purchase functionality in my iPhone app. In that I have given Restore Option. Sometimes, when I tapped restore button, I am not getting any product ids from App Store. But I have done purchases using Apple ids (Test User Account).
But sometime it works with some-other Apple Ids (Test User account). Why is this behaving differently?
How long should I wait to restore the purchase once after purchase is done. I use to check it on after 10min. Is that OK?
Apple (and Google) does give the option to 'save' the purchase history for you so a user can restore it if he deleted his app or switched to a new device. The functionality given by Google and Apple is very limited, however and you can easily implement a better functionality on your own.
Set up a server side for user verification
Make your own restore purchase function
Apply some rules on top of it (fraud protection, expiration, logs, ...).
To do that, you need to configure all products as "consumable" or in Google "unmanaged" and basically manage everything on your side.
I really wouldn't worry about it. No one is ever going to make an in-app purchase and then delete and re-download the app in the span of 10 minutes. If you have gotten it to work, you should be fine for any realistic case.

Restricting app store purchases to one per registered device. Is it possible?

I am working on a application which has a more peculiar requirement. Basically it is something which is not targeted at end users but at a system integrator who will embed an iPad into a larger system and sell it to an end user as a whole.
However, the problem I'm facing is that the system integrators could simply purchase the app once and then keep cloning thousands of iPads from a single iTunes account, my company would not get any revenue from this.
Is there any way around this. I've looked at in app purchases but according to the guidelines I'm supposed to give in app purchase restore functionality so I guess if I don't the app won't get approved.
I could use external authentication servers I guess, but that may be viewed as circumventing the app store.
I've loked at the volume B2B stuff but I'm not quite clear on how that works or if it would help me in this case.
Any ideas?
Thanks
Last time I checked an application can only be installed on five devices, and then the other ones simply refuse to install the application.
If this system integrator managed to circumvent this, it's he who is breaking the App Store rules.
You can't use the App Store mechanisms as you described (you can't change iTunes). In-App purchases of non-consumable items must include a restore option so the user can restore it on all his devices even if it's thousands (this also for subscriptions etc). If you won't enable that you would be rejected.
You can think you can send the Device-ID for each device that purchase the item and have control over that(or any information) but apple would simply reject your app because it's forbidden to send device-ID.
If your service is online you can simply use some kind of tokens created on your servers which would be given to each client (from some kind of private key), This way you must be connected to each purchased item (only those would contact your servers and you would grant access).
Security wise you must consider leaving some of the functionality on your server side. This is not illegal same as you can't access Facebook without username& password.
And now for the easy way, Define your service as consumable item for in-App purchase(if you can). What does it mean? Lets say you are selling a special feature like "Ad-Free" you can sell credits that would be consumed with each app open or any other process you have in mind, You can even set this credit to 1 million for 0.99$ (so the user never gets to that) but still the consumer would have to buy it again and again for each device and it would be absolutely legal by Apple. Pay attention that the problem would be on the consumer side such as that if user have deleted his app you should find a way to help him or refund him on next buy. Also, If you can and would use this method pay attention to save those credits on the restored folder on the device, so if the user would upgrade or restore the device he would still have the credits he bought.
Pay attention that if you are going to use in-App there are lots of methods to steal this content on jailbroken devices and you must use your own server to check the buying process (according to Apple).
Another important thing is that the app without the in-App purchase must have some value to the user.

Clearing purchases from iOS in-app purchase sandbox for a test user

Does anyone have any ideas on how to reset and/or clear the iOS in-app purchase sandbox?
I have an app that I'm testing with the sandbox, and I'd like to test new purchases without having to create a new test user every time I purchase something.
If I don't do this, then I (of course) always get a message that the in-app purchase item has already been purchased when I click on my app's buy button.
IMO there are 3 things you can do to make testing non-consumables bearable:
You can have many test accounts associated to one email. Gmail for example lets you add a "plus" string to the email to create aliases for an address: so tester+01#gmail.com and tester+02#gmail.com both really just go to tester#gmail.com. Probably other email hosts do the same. When you create a test account you need to introduce: first name, last name, email address, password, secret question, secret answer, date of birth, and iTunes store country. You can put exactly the same data (including password) for tester+01#gmail.com and tester+02#gmail.com and you will have two test accounts. Finally, in your tester#gmail.com inbox you will receive two verification emails from Apple to confirm both test accounts.
Say that you have a non-consumable with product ID #"Extra_Levels". Instead of writing #"Extra_Levels" in all methods (requestProduct, purchaseProduct, ...), just write PRODUCT_ID1 and at some header file put #define PRODUCT_ID1 #"Extra_Levels" (with no semicolon!), then the preprocessor will search PRODUCT_ID1 and substitute it for #"Extra_Levels". Then creating a new non-consumable called #"Extra_Levels_01" and changing the #define will be as good as resetting the purchases for all your test users.
As appsmatics pointed out, you can test the correct behavior of your code when you buy a non-consumable IAP by first using a consumable IAP (so that test user can make as many purchases as needed) to get rid of some bugs. Of course, you should also test the code with the real non-consumable IAP after that.
You can't do this, as far as I know. The sandbox backend works like a real account-- once it's purchased, it's purchased (and thus you can test restore). You should do most of your development with the store stuff shimmed out, and then when you get to testing it for real, just expect to create several test accounts.
You can clear the purchase history for a tester so that you can continue to use the same sandbox Apple ID for ongoing testing. Clearing purchase history will delete all past auto-renewable subscriptions and non-consumables purchased by the selected testers in the sandbox environment. In-app purchases made by customers on the App Store are not affected.
To clear tester purchase history:
From Users and Access, under Sandbox, click Testers.
Click Edit.
Select the checkbox for each tester you want to modify and click Clear Purchase History.
Click Clear Purchase History in the dialog that appears.
Sandbox Apple IDs with a high number of purchases may take longer to clear. This action cannot be reversed.
I have 2 in app purchase items.
1 for production.
and the other for testing. when I need to "clear" I delete the in app item and create new one (15 seconds in itunes connect and 1 second to change the product id in code)
if i dont need to test "new user", i use the production in app item.
Deleting your app and reinstalling works also for sandbox testing. Depends on the app obviously, but I'm testing a subscription based app that only purchases during sign up at the moment so it's been the easiest solution.
Well, technically you don't need that.
If you get SKPaymentTransactionStateRestored, it is 100% equivalent to the app store verifying the user and granting him the purchase. I have a switch like:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for( SKPaymentTransaction *purch in transactions )
{
switch( purch.transactionState )
{
case SKPaymentTransactionStateRestored:
info( "PURCHASE RESTORE" ) ;
// fall thru
case SKPaymentTransactionStatePurchased:
[[SKPaymentQueue defaultQueue] finishTransaction:purch];
// Do regular changes to app state for this purchase,
// register in keychain, etc.
break ;
//.. other cases
}
}
}
The question of having your app logic / take back the purchase is simple: if you're caching purchases in keychain, delete your keychain. If you're doing it some other how, just change your local app state to pretend like the user never purchased it before. The request to purchase dialog is still exactly the same, the only difference is when you punch YES, it gives you SKPaymentTransactionStateRestored instead of SKPaymentTransactionStatePurchased.
You can clear the purchase history for a sandbox Apple ID in App Store Connect.
To clear tester purchase history:
From Users and Access, under Sandbox, click Testers.
Click Edit.
Select the checkbox for each tester you want to modify and click Clear Purchase History.
Click Clear Purchase History in the dialog that appears.
see Documentation https://help.apple.com/app-store-connect/#/dev7e89e149d
Check out SimStoreKit. It's a "simulated version of the iPhone's StoreKit, for testing store UIs on the iPhone Simulator, or even on device without having to set up IAP in Connect."
SimStoreKit stores purchases in the user defaults under the key ILSimSKTransactions. So to clear all purchases you can do:
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"ILSimSKTransactions"]
On the simulator, you can simply remove your app and install it again.
I've successfully used SimStoreKit to debug my app's store front before testing with the sandbox. The beauty of this library is that it can be set-up to use the same class names as the real StoreKit framework (by doing #define ILSimReplaceRealStoreKit 1 before doing #include <ILSimStoreKit.h>).
In source files where I need to access StoreKit, I include this header file:
#import <TargetConditionals.h>
#if TARGET_IPHONE_SIMULATOR
#define kILSimAllowSimulatedStoreKit 1
#define ILSimReplaceRealStoreKit 1
#import <ILSimStoreKit.h>
#else
#import <StoreKit/StoreKit.h>
#endif
This has the effect of using SimStoreKit when I run on the simulator and the real StoreKit when I run on the device.
This is now possible for subscriptions purchases through Reset Eligibility
On the test iOS device, open Settings > Apple ID > Media & Purchases (or iTunes &/or App Store for iOS 13 and earlier). Under the Sandbox Account section, tap your highlighted Sandbox Apple ID then tap Manage to open the sandbox Subscription Management page.
If the subscription has expired, tap one of the options to resubscribe.
Once you resubscribe or when you have a subscription going on, you can use the Reset Eligibility button to reset and redeem another introductory offer (trial or discounted price).
Just keep using the same test account, restoring purchases as opposed to completing new ones. After all, whether you start a new purchase or restore an old one, YOUR APP will do the same thing (at least initially, maybe the user interface will update differently upon completion). Apple are the folks handling things differently in those different situations - don't worry about it.
Place your delivery logic in the SKPaymentTransactionStateRestored case within this method's implementation for testing:
- (void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions;
Then be sure to put that delivery logic into the SKPaymentTransactionStatePurchased case.
At the end, because most of us are obsessive-compulsive to varying degrees, do a final test with a fresh account (not a big deal to make a second one for absolute certainty).
The final thing to note: consider apple's position. If there was a problem with developers having to waste time creating tens or hundreds of accounts to test IAP thoroughly, they would have solved the problem. There is no problem.
alternatively to create multiple test user solution you can create multiple test in app purchases in iTunes connect then you don't need to change a user account.

How to redownload an in-App purchase application programmatically

I am able to implement in-App purchase successfully. I am also able to purchase a product through in - App purchase.I am storing purchase information in NSUserDefaluts.So if next time user tries to purchase same product again i am able to handle it locally.My problem is if a user deletes the application from the device,How do i handle the re-downloading of the application without charging for the same product again.I know that if an application has been deleted from the device having in App purchase it has to be downloaded again.Can anybody show a sample code for the same?
Thanks
Aditya
Hi
Thanks for a prompt reply.I have implemented the same as you have suggested.What i'm wondering is if i delete my app and install it again , i am asked to purchase it again.Do i have to pay again for the upgrade or is it handled from the apple server(i.e if i upgrade the same product again am i charged again?).Is there a way to know it was upgraded without asking to upgrade again?
The StoreKit api takes care of this and gives you, on request, a list of identifiers of the purchased items. Once you got those, it's up to you to re-download the products again(if not already bundled inside the app).
Excerpt from the StoreKit API help:
-(void)restoreCompletedTransactions
Asks the payment queue to restore previously completed purchases.
Your application calls this method to restore transactions that were previously finished so that you can process them again. For example, your application would use this to allow a user to unlock previously purchased content onto a new device.
When you create a new product to be sold in your store, you choose whether that product can be restored or not. See the In App Purchase Programming Guide for more information.
The payment queue will deliver a new transaction for each previously completed transaction that can be restored. Each transaction includes a copy of the original transaction.
////Comments pseudo code////
// Reinstall App:
// Restore already purchased transactions...
// Does the NSUserDefaults have the Purchase History??? -
// YES: Load the Table View of Items remaining for sale, Else -
// NO: Get the restoreCompletedTransactions list from Apple
// and create a container to hold previous transaction purchases.
// Fresh new App install:
// When a user makes the first IAP. Add it to the container and NSUserDefaults
// This container will be added to the NSUserDefaults when at least one purchase has been made.