Is it ok to repurpose SKPayment.applicationUsername? - ios

Regarding SKPayment.applicationUsername, it says here...
This is used to help the store detect irregular activity. For example,
in a game, it would be unusual for dozens of different iTunes Store
accounts making purchases on behalf of the same in-game character.
The recommended implementation is to use a one-way hash of the user’s
account name to calculate the value for this property.
This property is optional. So, am I allowed to use it for something else entirely? I want to use it to store details about the product that are not stored in iTunes.

SKPayment.applicationUsername is only used by Apple on the backend for fraud detection. They can use it to see if a single user is processing purchases on multiple devices, or processing purchases more rapidly than normal.
This value is unreadable except for on the payment you set it on, you aren't able to read SKPaymentTransaction.payment.applicationUsername it is always nil.
On a side note, I think this is a flaw in StoreKit as it makes it impossible to link transactions within an iTunes account to an application account, so if you have users sharing an iTunes account it is very difficult to not double grant purchased content.

Related

In-App-Purchase Bug

I'm developing an iOS application in which users can buy an extra feature through an in app purchase. I have gotten the in app purchasing and restoring the purchases working correctly, where I save a boolean to the NSUserDefaults saying whether they have successfully bought and or restored the purchase.
However there is a bug where if User 1 buys the in app purchase on their phone, then logs into the App Store on User 2's phone and restores purchases. Then logs out again and allows User 2 to log back in, it means User 2 still has that in app purchase unlocked for free as the boolean in NSUserDefaults is still set to true.
I'm trying to find a way to query the user's App Store email or another way to check if the user's App Store email is the same as the one they bought the purchase under. But this needs to be done locally as the user could use this feature when they don't have any internet connection.
Does anyone have any suggestions of how I can do this or any better practices?
Thanks
This is, unfortunately by design, so as the old adage goes "it's not a bug, it's a feature".
It's designed like this so a user with multiple devices (e.g. iPhone and iPad), can make a purchase on the first device and benefit by restoring their purchases on their other device(s).
Of course this opens the door for people to share purchases across other peoples devices just as you describe.
The good news is there is a property on SKPayment called applicationUsername, its purpose as the docs state...
Use this property to help the store detect irregular activity. For example, in a game, it would be unusual for dozens of different iTunes Store accounts to make purchases on behalf of the same in-game character.
If you don't have anything unique to identify the user, e.g. an email, username, id, etc then your problem can't be solved I'm sorry, but if you do, keep reading, this is where it gets interesting.
When the user restores their purchase you should use the restoreCompletedTransactionsWithApplicationUsername method to make the call instead of restoreCompletedTransactions.
The docs are unclear what happens next, but my understanding is transaction state will be SKPaymentTransactionStateFailed if the username sent in the purchase call is not the same as the username sent in the restore call.
But if i'm wrong then you should be able to find the applicationUsername on the payment property of the transaction and compare it yourself before setting your property on NSUserDefaults. Sorry I haven't tried this myself, I only know the theory.
When setting the username, don't set it as plain text, the docs on applicationUsername suggest that...
The recommended implementation is to use a one-way hash of the user's account name to calculate the value for this property.
And further in Detecting Irregular Activity they explain...
Don't use the Apple ID for your developer account, the user's Apple ID, or the user's unhashed account name on your server.
Also, ideally you will be using a constant identifier, i.e. something that can't be changed for the user. If you use a hash of their email address for instance, if they change it and you didn't keep that hash as a constant restoring will fail as an unintended side effect.
Hope this helps.
You can control it via your servers by checking originalTransactionIdentifier of the receipts. As this remains same for all the purchased from one itunes account. You can associate this originalTransactionIdentifier with the username. Next time if you receive some already stored originalTransactionIdentifier against a different username, you can take the actions according to your suitability.

Using undocumented fields in an App Store Receipt

OK I'm working on a app where the user purchases the app from the App Store, gets access to the app for a year, and then after the year has passed they are sent to an In-App Purchase screen where they can purchase a subscription for continued access.
(this is not the subscription model I'd implement if it were up to me, but the client insists that it follow this model)
In order for it to work, though, I need a reliable way to check when the app was purchased, in order to calculate when the first year of access ends. Following the suggestions in one of the answers in...
iOS App Purchase Date
...I've gotten the app receipt and I can find all the data fields that the documentation says there are, but there are also a few others including "original_purchase_date" (not the one in the IAP receipt array; the one for the app receipt itself). This would appear to be what I want. However this field is undocumented.
As far as I can tell, Apple security operates via a form of "Security Through Obscurity/Diversity", so it's been difficult to find further information about these undocumented data fields. My concern is that I might use the data from this field, and then discover that it was only there in sandbox mode and actual receipts don't have it. Worse, what if I reference that field and then Apple releases a new iOS update that completely discards that field from its implementation? Undocumented features are even less reliable than deprecated features, after all.
My question is this: is there a reliable source of information somewhere where I can find out what Apples intends regarding this field? Or better yet, is there another, safer way of achieving the system my client wants?
You have to implement it as a free app that requires a subscription IAP. Paying for an app cannot be its subscription cost.
Check rule 11.12 here: https://developer.apple.com/app-store/review/guidelines/#purchasing-currencies
Apps offering subscriptions must do so using IAP, Apple will share the
same 70/30 revenue split with developers for these purchases, as set
forth in the Program License Agreement
Implementing it the way your client wants will result in rejection.

iPhone app consumables - Server vs keychain method

I had an iPhone app developed by a company, which contains consumables purchases, specifically a currency named as "coins", used for app-specific purchases. In the current version, the team has added the ability for a user to register under a username/email/password. This information is stored on my external server and used as a recovery for the user's purchased coins via signing in using these credentials.
The model looks fine programmatically but is not convenient in terms of usability. Ideally, i would want my users to not have to register at all. I have been reading that it is a possibility to store the user's purchased consumables on the client side, using the internal keychain.
Now this would imply that consumables can only be restored on this single device (i guess unless iCloud is used). But is this allowed by Apple ? Or there should be a method to restore across different devices ?
If the keychain method is allowed, I believe that it would be way better for my app to go with that. I just wanted to make sure that Apple would not have a problem with that. I have been reading that it's allowed, but do not want to have unpleasant surprised after changing the whole client-server model to client-only keychain.
If you have experience with that, could you please let me know what the case is ?
EDIT : Somewhere on SO I read that using Gamecenter's GKPlayer class is also a way to store information on my server and retrieve that automatically in order to identify a user purchase. This would mean that the step of username/email registration is avoided, which is something i really really want to avoid. Is this really a possibility that is allowed by Apple ?
EDIT 2 : I am now thinking that I will keep the client-server model but lose the username/password and use the device id instead. This way i will be able to keep the restoration of coins working on a specific device. Would that be ok you think?

In app purchase - method to allow full control of adding products from personal server = allowed?

I have a very strait forward question (and yes I've looked though apple documentation to see if this has an answer but no luck... I may have accidentally missed it though)
Here's my plan:
The problem I've been trying to find a workaround for is that if the admin would ever want to add a product, he would have to log into iTunes connect to add it and also add it in a custom control panel. We, obviously, don't want to make him suffer that so I've been looking for a solution but I need you guys to tell me if it's allowed by apple. Basically I will take over most of the product handling on our servers and will only go to apple with the transactions. This means that apple will not have an in-app purchase set up for ALL the products... only one for each length of subscription (1 month, 3 month... etc) and a few consumable in-app purchases for the various prices of the issues/singles
Side note: I will be selling monthly issues that contain multiple singles for each day of the month. The user will be able to download a full month or a single day at a time if they like.
DEFINITION OF CONSUMABLE PURCHASE - products must be purchased each time the user needs that item. For example, one-time services are commonly implemented as consumable products.
So I will store all the information in our server about the products and if someone chooses to buy a single month's issue that was set to 4.99 (on our server, not apples) then the app will run the in-app purchase with apple that is listed for the 4.99 tier. Whenever a person opens the app for the first time, their app will send some information to our server and they will have a row set aside for them where all the information about their purchases will be recorded so that they can restore them if they switch over to another device.
If you guys think i'm safe in doing this, please let me know so that I can proceed. Also, if this method helps anyone, feel free to use it!
Thanks,
Matt
I think your restore process might be flawed. You talk about the app sending some information up to your server, but what is that information? There is no reliable way to uniquely identify a user across different devices.
If you want to continue on this path you'll want to make sure that your recovery and failover process is very solid. Try out every imaginable scenario. From an app store submission point of view, you'll want to consider a token/coin-based approach. Of course, Apple's guidelines are fairly loose and subject to change so it's always possible you'll get rejected, but tokens are certainly more solid than simply using the same generic in-app purchase.
In a token system, you would set up in-app purchases for different numbers of tokens that the user can purchase as a sort-of virtual currency only valid within your application. Then users can spend these tokens on any items that you dynamically create.
Server-side this means you'll need some way to store how many tokens a user currently has and a way to uniquely identify a user across devices, which is a fairly uncertain proposition. Instead of storing the number of tokens each user has, you could implement some sort of hashing algorithm that generates a hash from an in-app purchase receipt and then sends it up to your server. If the app crashes or the network dies after purchase but before sending your hashes up, next time they open the app you can recalculate all of the hashes, send 'em up, and if the server doesn't recognize a hash it just adds it to the database. Then if a user wants to restore their purchases you simply recalculate the hashes on the device using the in-app purchase receipts you'll receive and then send them up to your server and ask the server to figure out for each of those hashes, how many tokens the user has left. You could think of it like a gift card system, where each hash is one gift card.
Again, app store rules change and if apple thinks you're trying to game the system and not provide a useful experience they have the right to reject you. It could be worthwhile to open a Developer Technical Support request and see if an apple engineer can provide you with a better solution or tell you if the reviewers are likely to accept your application.

Differentiating between initial buy and free "re-buy" in StoreKit/In-App Purchase

From the StoreKit guide:
If the user attempts to purchase a nonconsumable product or a renewable subscription they have already purchased, your application receives a regular transaction for that item, not a restore transaction. However, the user is not charged again for that product. Your application should treat these transactions identically to those of the original transaction.
This presents a huge problem in an app I am working on. We have licensed a large body of content from a publisher for sale through in-app purchase. They require that every time we sell a piece of this content (i.e. user pays us), our server calls an API on their servers to report the transaction. This is for accounting purposes and ultimately used to determine how much we pay them at the end of the month, per our agreement with them.
I have read several suggestions on SO and elsewhere about calling restoreCompletedTransactions rather frequently and maintaining a local understanding, on the device, of what the user has already purchased so they cannot be allowed to purchase it again. This to me seems like something that should be able to be implemented on the server side. However, the receipts that we are getting back from the Apple servers are exactly the same for a buy and a re-buy, as promised by the StoreKit guide.
If payment callbacks from StoreKit cannot be trusted as a valid accounting mechanism in this kind of situation ("you got paid" vs. "you didn't get paid"), what other real-time insights into transaction traffic are available? I don't think the publisher we are working with is going to be happy if we tell them we have to wait 45 days after the end of the month to get the REAL paid dollar amount out of iTunes Connect.
I have recently looked into the same problem. In my case, I wanted to implement accurate revenue tracking using Mobile App Tracking to track revenue generated from different customer acquisition campaigns.
Fortunately enough, there is a way to do it. It should be noted that SKPaymentTransactionStatePurchased vs. SKPaymentTransactionStateRestored solely depends on the initating action, e.g. whether you initiated a restore or a (re-)purchase, so that doesn't work.
What does work instead is checking for SKPaymentTransaction.originalTransaction which will be != nil for restores and re-purchases. The latter is unfortunately undefined behavior (docs). I'd consider a null check fair enough though.
Another option is to validate the transaction-receipt of transactions with SKPaymentTransactionStatePurchased and check that the original_transaction_id property in the returned, validated receipt matches the transaction_id.
The bad news is: In the current iOS version (4.3.x) there's no way to distinguish between a buy and a re-buy of non-consumable products.
To ease the situation I would recommend two things:
First
After a successful purchase, store the product identifier of the purchased product in the NSUserDefaults on the device. You can then hide the already purchased products from the user and thus handle a re-buy situation.
The NSUserDefaults are backed up by iTunes when the user synchronizes his device. So your stored purchase information is not lost when the user gets a new device.
Second
Store the receipt data together with the device ID on your server. Analyze the receipt's product identifier and the device ID.
If you receive another receipt with the same product identifier and device ID combination, then assume a re-buy. At least this would allow you to cover most of the re-buy cases.
Assuming that an ordinary iPhone user switches his device every 1-2 years, you will at least cover most of the re-buy cases and maybe apple will fix this in the future.
I have one solution,
Configure the product as consumable.
this will solve the problem - (They require that every time we sell a piece of this content (i.e. user pays us)).
Next you need to implement a logic in product buy option. It is in a way that once the user purchase a product the buy option need to remove otherwise the user may happendly go purchase and lost is cash once again for same product in same device.
you can use NSUserdefaults for this purpose.
thanks,

Resources