56

I have Apple iOS IAP successfully implemented in my app and tested in the sandbox. Works great.

I'm concerned that users could buy something with IAP, download it into my app, then complain to Apple and get a refund. There's no obvious way that refunds are reported to my app. Are they simply left out of the list of products I receive during a "restore" operation? Is there some undocumented transaction type that will asynchronously show up in my SKPaymentTransactionObserver when a refund occurs?

Right now I'm operating on the assumption that I need to delete the user's IAP transactions before doing a restore, and that anything refunded will just not be in the list of restored transactions. Is this the right way to do it? Is there any way to test this in the sandbox?

Has anyone seen refunds in a production environment and can explain how they work?

Craig
  • 3,253
  • 5
  • 29
  • 43
  • there is no API that will inform your app; i've never heard it discussed – bshirley Jun 22 '11 at 13:54
  • 4
    This is an epic fail if true. Not unexpected, not unlike Apple, but epic in the scope of its failure to grasp the requirements for its IAP functionality -- especially now that, for many apps, it is the required e-commerce solution. – Craig Jun 22 '11 at 14:48
  • i have not heard of apple providing refunds for IAP. it is stated in the docs that any app purchase would be changed in accounting. you should try the http://devforums.apple.com, that's the most likely place to get a response to what is mostly a business issue, not a technical one – bshirley Jun 22 '11 at 15:51
  • here's the only one i found from a quick search: https://devforums.apple.com/message/459077 – bshirley Jun 22 '11 at 16:00
  • FYI you should post your answer as an answer so it can be properly voted up or down. – Craig Jun 27 '11 at 13:51
  • 1
    Are you validating receipts? Maybe the receipt information returned from Apple has information in it concerning the refund and you can change your implementation based on that. – Hyperbole Aug 13 '11 at 20:05
  • https://forums.developer.apple.com/thread/20656?sr=stream&ru=130892 – cbartel Jan 12 '16 at 22:55
  • With respect to the linked developer forum thread, I suspect the cancellation date field is related to subscriptions. – Craig Jan 13 '16 at 22:49

7 Answers7

14

Update June 24, 2020:

At WWDC 2020, a new notification was introduced that informs you of refunds: https://developer.apple.com/documentation/storekit/in-app_purchase/handling_refund_notifications


Original answer:

I received a response from Apple Developer Relations today (Dec 6, 2018):

Hello Johannes,

In response to your question, unfortunately, there is no supported means to detect that the user has contacted Apple Care and received a refund for the In-App Purchase of a consumable item. The only option which I can refer you to is to submit an API enhancement request for an API to be made available for an app to detect that a refund was provided to a user of an In-App Purchase. Currently, this support is realistically only available to apps which offer auto-renewable subscription In-App Purchase.

You can submit the enhancement request using the Apple Developer Bug Report web page - http//bugreport.apple.com

As this is an enhancement request type issue, I'm going to arrange for this incident to be unbilled from your account for use on a future issue.

So there we have it.

Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165
  • 1
    There is a related tech note [TN2413](https://developer.apple.com/library/archive/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPT-HOW_DO_I_USE_THE_CANCELLATION_DATE_FIELD_) about that. – Viktor Kucera Mar 04 '19 at 10:47
  • They probably wont be able to detect the refund on your incident request – GP89 Oct 10 '19 at 10:29
  • I have got almost the same response today. I am not sure if this will be solved ever. Today is 02.03.2020, so more than a year. Still not available cancellation_date field for consumable or non-consumable purchases. – Endre Olah Mar 02 '20 at 19:09
  • 1
    I watched the WWDC20 sessions on StoreKit also. It's great they're finally going to notify us of refunds, but it's hard to be grateful for the fix. This should have been in StoreKit on day 0. :-) – Craig Jun 25 '20 at 19:23
  • Sooo how do we actually do this? apple? Appleeee? –  Mar 11 '22 at 00:23
13

The in-app purchasing guide discusses the concept of "cancellation" of subscriptions. This is the only place I've seen discussing the subject.

Further details about the cancellation date field can also be found in the App Store receipt validation documentation.

cancellation_date

After decoding a receipt, you can get the cancellation date which will tell you the following:

For a transaction that was canceled by Apple customer support, the time and date of the cancellation.

aradil
  • 492
  • 7
  • 12
  • Does it mean that I'll have to store the receipt and decode it once a while to check if it's cancelled? Would the receipt that I persisted in my app locally after completeTransaction get updated to show the information of cancellation? – John May 20 '14 at 11:23
  • @aradil does the cancellation_date appear even on non-consumable IAPs? – John Estropia Jun 23 '14 at 08:18
  • @user1256663 I believe that any time there is a modification to your receipt you will get a new one. In fact, decoding the old receipt after it's been cancelled wouldn't make much sense, since it would be the same receipt. – aradil Jul 31 '14 at 13:50
  • @JohnEstropia If by non-consumable you mean subscription IAPs, then I would say yes; in fact, I believe those are the only type of purchase that has cancellations. – aradil Jul 31 '14 at 13:51
  • @aradil This is good to know. I'm assuming that in order to learn of a change in this cancellation_date field the app would need to do a receipt refresh or a purchase restore. I'm assuming submitting/decoding the receipt to the standard URL with Apple would not update the cancellation date field. I'm thinking about this because my backend server stores the initial purchase receipt. – Chris Prince Mar 09 '15 at 14:56
  • 3
    @ChrisPrince As long as you always store the original receipt on your server, you can send it to Apple's servers again, and they will send you a response with a newest_receipt_info field (which contains updated entries for the in_app field) and a newest_receipt field (which is the new original transaction you should store). In the updated receipt_info entries, you will get a cancellation_date if the purchase was charged back through Apple Support. – Erik S Mar 20 '15 at 17:48
  • Does that work for both recurring and nonrecurring subscriptions? They are two separate IAP types in iTunes Connect. Maybe someone knows before i waste a whole afternoon. – Maciej Swic Aug 07 '15 at 13:40
  • Can I use the original receipt to check for the refund? Or does the iOS app need to fetch a new receipt after the refund and then check that receipt for the refund? Basically, will the old receipt contain the cancellation_date field? – cbartel Jan 12 '16 at 22:47
  • @MaciejSwic The documentation doesn't make that clear, sorry. I would imagine that's the case, although that's just an educated guess. – aradil Jan 13 '16 at 15:27
  • @cbartel The old receipt doesn't have an internet connection, so it wouldn't update itself. You'll need to fetch the new receipt in order to ensure it is still valid. – aradil Jan 13 '16 at 15:27
  • 3
    @aradil We started getting `cancellation_date` for refunded nonrecurring subscriptions now. Finally we can catch people who buy a year, claim it was an accident and continue to use the app for free. – Maciej Swic Jan 14 '16 at 15:52
  • @MaciejSwic That is great news! Can you share the JSON that contains the canellation_date? Also, Can I use the original receipt to check for the refund? Or does the iOS app need to fetch a new receipt after the refund and then check that receipt for the refund? Basically, will the old receipt contain the cancellation_date field? – cbartel Jan 14 '16 at 19:24
  • @MaciejSwic Yeah, I still haven't seen one myself. It would have been nice to see some more fleshed out examples in the documentation, rather than having to wait to see one and handle it in real life ;) – aradil Jan 14 '16 at 20:44
  • @cbartel I don´t have the JSON but i use the `obtainInAppPurchases` method from https://cocoapods.org/pods/VerifyStoreReceipt. You can then get the value using `if let cancelDate = iap.valueForKey("CancelDate") as? String` where iap is one in app purchase from the `obtainInAppPurchases` array. – Maciej Swic Jan 15 '16 at 11:29
  • 1
    @cbartel If you still have trouble, buy the subscription yourself and cancel the purchase and put developer test as the reason. You have to refresh the receipts and get the property from the updated receipt! One strategy is to do it on new purchases (receipts will refresh) and lock the account until the user contacts you and you can inquire about what they did, or manually refresh receipts once a month or so (but not every start as user will have to enter password). If you post a new question i can provide a better answer with more code, just link it here. – Maciej Swic Jan 15 '16 at 11:31
  • @MaciejSwic hi, have you tried to check the consumable IAPs with verifyStoreReceipt? Does the cancellation_date field exists as same as the subscriptions after the user cancel the purchase in Apple Support? thx a lot – treemanz Jan 26 '16 at 04:05
  • @treemanz Sorry i dont know, you have to actually test it yourself to find out. There is no documentation that i know of. – Maciej Swic Jan 26 '16 at 09:07
  • @MaciejSwic I've tested it before. From my experience, there's no differences apple would tell in the verifyStoreReceipt interface before or after I canceled my consumable IAP. So when I read your answer, I was so excited but now... Maybe there's really no differences. thx the same. – treemanz Jan 27 '16 at 02:58
  • 3
    According to this, https://developer.apple.com/library/ios/technotes/tn2413/_index.html, there is no cancellation_date for consumable and non-renewing subscription products. – Kamchatka Jul 23 '16 at 07:47
9

The strategy is:

  1. You save the latest_receipt ("MIIUJgYJKoZIhvc..." base64) field in your DB, associated with the user account.

  2. Every day you query apple to validate all the receipts, by sending them the base64 receipt from saved latest_receipt field.

  3. In the receipt you check if there is a cancellation_date field. If you find it, treat it according to documentation:

Treat a canceled receipt the same as if no purchase had ever been made.

Same way you also checking subscription renewals (check expires_date_ms field).

Kirill Groshkov
  • 1,535
  • 1
  • 22
  • 23
  • 2
    Are you sure by validating an old receipt, apple will append the cancellation_date field to the response on iOS 7+? To my best knowledge and practice, I don't think apple will do that. – Zheng Te Sep 17 '17 at 14:41
  • I'm sure it does it on newer iOS versions. Not sure about iOS < 9 – Kirill Groshkov Apr 16 '20 at 15:53
5

Refunds are given, but your app gets no notification of them at all. Whether it's an In-App Purchase, an app download or any other iTunes content, the user can still use the content even if they have asked for a refund.

Benjamin Mayo
  • 6,649
  • 2
  • 26
  • 25
  • OK so when I do a "restore" I will get back a transaction for the refunded item, with no indication it has been refunded? Or will I NOT get a transaction for that item? Are you speculating or have you experienced this? – Craig Jun 27 '11 at 13:50
  • 1
    It will appear like any purchase. There is no distinction between refunded purchases and purchases. You can't discriminate against refunds. – Benjamin Mayo Jul 03 '11 at 13:36
  • @BenjaminMayo - Are you talking about subscriptions as well, or just one time purchases? If a user gets a refund for a subscription and you subsequently make a call to verify the receipt, will the expires_date and status code not reflect the cancellation? – DougW Nov 28 '11 at 22:35
  • I'm not sure on subscriptions, but I think it is counted until the end of what-they-would have paid for. Say they got a refund on a month, it would invalidate at the end of the month. Don't quote me on that though. – Benjamin Mayo Nov 28 '11 at 22:50
  • 2
    @BenjaminMayo A receipt will contain a cancellation date if the purchase was cancelled a a refund given for a subscription purchase. – aradil Jan 14 '14 at 21:18
  • @DougW There will be a field called cancellation_date. This may not have existed at the time this question was asked originally. – aradil Jan 14 '14 at 21:19
  • 2
    So as I understand the only one possible solution is to store receipts and verify them every day/every time period to check if subscription has been cancelled? – Maciek Czarnik Jan 29 '14 at 12:58
  • @MaciekCzarnik that would seem sensible. I don't see any mechanisms that Apple provides that push cancellation notifications to you. – aradil Jan 13 '16 at 15:22
  • This is no longer true. We **do** get a notification once the receipts are refreshed now! See my comments on the other answer. – Maciej Swic Jan 15 '16 at 11:32
2

In iOS 14 a new method is added to the SKPaymentTransactionObserver protocol that is called when the user is no longer entitled to one or more in-app purchases https://developer.apple.com/documentation/storekit/skpaymenttransactionobserver/3564804-paymentqueue Although the documentation doesn’t tell in which situation this method is called, Apple does tell this in this WWDC2020 video at 26:55 https://developer.apple.com/videos/play/wwdc2020/10661/

1

According to latest documentation, server to server notification with type CANCEL is now handling cancel scenario by Apple customer support.

Nirav Bhatt
  • 6,940
  • 5
  • 45
  • 89
0

Starting October 21, 2021, App Store Server Notification Version 2 became available and the notificationType in the notification payload will be returned with REFUND when the App Store refund process is completed.

See below for more information. https://developer.apple.com/documentation/appstoreservernotifications/app_store_server_notifications_v2

Kazunori Takaishi
  • 2,268
  • 1
  • 15
  • 27