SKPaymentQueue restoreCompletedTransactions: no transaction restored - ios

I can't receive any transactions in my paymentQueue:updatedTransactions: delegate Method. Only the delegate method paymentQueueRestoreCompletedTransactionsFinished: gets hit by the debugger. When i try to retreive the transaction array, i get an empty array.
First of all, i buy a non-renewing product. After successfully purchaseing and verifying it from the server, it will be removed from the payment queue. Than at startup, i call the paymentqueues restoreCompletedTransactions method. As expected the Storekit asks me for the password (from the sandbox-user) which i enter. But the update Method won't get cought. Only the finish Method gets caught and as discribed, the transactions are empty.
What am i doing wrong? Is it my fault, or is it apples restoreCompletedTransactions broken??
Thanks and BR
Nic

Sorry for letting you wait for such a long time!
The problem is that in Apples In-App Purchase Programming Guide it clearly says, that i cannot take non-renewing subscriptions to be restored. I'll have to store the purchases on my own and implement an own restore functionality.
What i did was including some identifiers in the product name, that tell me, what kind of product it is. (Its one method to store information of the product without using my own server) So my Products name is e.g. com.mycompany.iphone.non_renewing_30d (This product is really configured as a non renewing product)
So when the user makes a purchase, i store this information in the iDevice's CoreData Database. I also include my expire functionality, because i have the purchase date and the products expire date (30d).
When the time expires, the user sees a message dialog which tells to make a new purchase. I really don't like apples implementation of non-renewing subscriptions because it tells the user, that he/she already bought the product and asks them to repurchase, but i had to deal with that.
I really suggest to store the purchase information on the iOS Device, because you don't need to create your own user recognize functionality (e.g. a userId or email adress), since Apple turned off the UDID uniqueness.

Related

How can I get the cancellation_date field?

First,i want to know if the user has refunded in my App.
So i find the cancellation_date field can judge it, and it can use by this method from In-App Purchase FAQ
The appStoreReceipt is refreshed only in the following cases after the
refund has occurred - when there is a successful purchase of an in-app
purchase item, when the SKReceiptRefreshRequest call is used and
when the restoreCompletedTransactions is used to restore previously
purchased products.
but this method will call the iTunes Store login alert.
So i want to know if there is another way to get the cancellation_date field or another way to know the user has refunded?
The recommended way is to store the entire receipt on your server and refresh it with the /verifyReceipt endpoint to get the current state of the subscription. Even if you only refreshed the receipt daily it would prevent a user from getting much access to your app after a refund, and you could track them even if they didn't open the app again.
Here's a good post that covers device-side and server-side nuances of Apple receipts: iOS Subscriptions Are Hard

Apple In-app purchase transaction_id of one purchase changes sometimes

Once I get a receipt from client and validate it by apple server, then I got a transaction id of the new purchase (it's an auto-subscription purchase) in this receipt. After a few days , I get a new receipt from the same client and validate it , strange things happen : the transaction id of that old purchase changes.
I compare the data of this purchase in the old receipt with the data in the new receipt , the only thing changes is the transaction_id field ,the original_transaction_id, purchase_date, expires_date, web_order_line_item_id and other fields are exactly same.
Than I check the database, I found about 1% transaction records have the same situation. And there is a trait, Most of their transaction id were increased or decreased by 1-2 .
I used to think the transaction id is the identifier of a purchase. Does anyone meet the same problem or know the reason?
Yes we see this happen as well. We see this triggered by 'Restore Purchase' button clicks.
If your 'Restore Purchase' button uses the restoreCompletedTransactions API then this will cause your transaction ID's to change. We have confirmed that this with Apple developer support.
Apparently you can call SKReceiptRefreshRequest instead which will just grab the latest receipt instead of replaying all of the transactions. It is my understanding that this will not cause the transaction ids to change.
We have, anecdotally, witnessed that the web_order_line_item_id values do not change across calls to restoreCompletedTransactions. However we only received an ambiguous, at best, response from Apple developer support when we asked for confirmation:
In regards to the web_order_line_item_id field, the value will change on every subsequent renewal.
You can use this, as long as you continue to store the new value as the renewal subscription events come in.
We take this to mean that the web_order_line_item_id is unique per renewal-purchase. Which neither confirms nor denies that it remains constant across calls to restoreCompletedTransactions
Additionally, I found this conference recording on Apple/Google/Amazon recurring billing very helpful (though, not for this particular case):
Rosa Gutierrez - The recurring nightmare. Implementing cross platform in-app subscription purchases
Based on latest Apple developer documentation,
web_order_line_item_id - A unique identifier for purchase events across devices, including subscription-renewal events. This value is the primary key for identifying subscription purchases.
transaction_id - You can use this value to:
Manage subscribers in your account database. Store the transaction_id, original_transaction_id, and product_id for each transaction, as a best practice to store transaction records for each customer. App Store generates a new value for transaction_id every time the subscription automatically renews or is restored on a new device.
Differentiate a purchase transaction from a restore or a renewal transaction. In a purchase transaction, the transaction_id always matches the original_transaction_id. For subscriptions, it indicates the first subscription purchase. For a restore or renewal, the transaction_id does not match the original_transaction_id. If a user restores or renews the same purchase multiple times, each restore or renewal has a different transaction_id.

How to track In-App Refund in ios app [duplicate]

Situation:
- User makes an IAP and is awarded some content, we store the users device id to ensure they can access this content whenever they want.
- User decided they do not like the content, so they call Apple and get a refund.
- User can still access the content, even though they have been refunded for their IAP
Problem:
We don't want the user to be able to access this content anymore. This could become a loophole that they would take advantage of. (unlock content, then get refunded and keep their access to said content)
Question:
Is there any way for us to check if a user has been refunded for an IAP with either their transactionId, transactionReceipt, or any other information we may have?
For Reference, I've read the StoreKitGuide, it did not mention this case.
No. There is no way to revoke access to content if they have been refunded. I believe this is by design. It is the same with App Store refunds; if someone buys an app and then asks Apple for a refund, Apple does not stop the user from continuing to use the app.
No. you cannot stop user to access. Apple does not stop user to use that feature after refund
Below a recent answer from the overbearing Apple!!!
At April 11
Hello Joe,
Thanks for your quick reply at first.
I think there were some misunderstandings. We understand that you must protect the user information. And we do not require any user info. We do not require any user's information---- iTunes account, User Name, email address and other information about himself/herself.
We require only one data----- transaction id (named "transactionIdentify" in your code), for example "1000000033409668" (this transaction id is a record at March 13).
A user buys a product from IAP, you will generate a "transaction id" and send it to us. This transaction id is stored in our database then. Now he/she gets refunds from you, so please send the transaction id to us. Let us know which one in the game canceled the IAP.
When a refund occurs, we think you should provide the transaction id to us. With these data, we could make a more fair service for all players. If you do not do it, more and more players will use your refund mechanism to get game items without real payment. You and we will lose money then. We think it is very important.
Regards,
Baibo
At April 12
Hello Baibo,
Thank you for following up with me regarding the refund transaction data.
We will not be able to provide you with the refund transaction data you have requested as it is not a feature or benefit provided to you per the contracts you have agreed to.
To view your paid applications contract and review your membership benefits, please access the contracts, tax and banking module within iTunes Connect:
http://itunesconnect.apple.com
If you are not happy with large refunds, you may want to consider adjusting the price of your in-app purchases as we will not be able to provide you with the refund transaction data in the future.
According to an answer one of my users received from Apple, refunding an app means they will no longer receive updates to the app, but doesn't remove the app from the device. IAP appears to work the same.
I am wondering if there is not a way by using restoreCompletedTransactions to check for the iAP purchase, but this would pop up a request for the user's iTunes password, so its use is limited.

Refund the previously purchased non-renewing subscription, not the current one. Possible?

I'm implementing an iOS app with non-renewing subscription. Not much relevant information was available online; so I seek your guidance.
A use case which worries me the most is when a user purchased the subscription once and then immediately after purchase it again to extend the duration of service (see such scenario here). What if that user were to refund the first purchase, leaving the second one intact? Is this even possible in practice, or am I just too paranoid?
Assuming the above case is possible, my app will run into a problem because, as far as I know, verifyReceipt only returns the latest, good receipt (watch Managing Subscriptions with In-App Purchase in WWDC'12).
I find nowhere Apple provides relevant information about refunding policies.
(Auto-renewable subscription seems to rule out this case as a renewing action is taken care by iTunes automagically and it seems not possible to extend this type of subscription until iTunes allows it.)
verifyReceipt will not only return the latest good receipt, it will also tell you if the receipt you submitted for verification is good. So if you're concerned that a user may have cancelled* a transaction, then submit every receipt you're questioning.
*But what do you mean by "cancel" the first purchase? Do you mean when they tap "cancel" instead of "buy?" Well then the transaction won't go through and you won't even get a receipt.
Or do you mean when they request a refund? I don't know any other way they can cancel a purchase.
Edit:
If you're referring to refunds, there's no way for a developer to tell if a user has been issued a refund. The assumption is that Apple expects you to continue to deliver services to that user regardless of the fact that they received a refund. Your receipts will probably all verify correctly regardless of the status of a refund.

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