in app purchases after cancellation in auto renewable process - ios

I am looking to build a ios application that will use an auto-renewable subscription on a yearly biases.My question is, how can we be notified when a user cancels a subscription in IOS, so that we can cancel the users features

sorry if I'm duplicating the reply:
if the receipt status is 21006 and there is a key named cancellation_date, then it's a cancellation, you can find the new expiration date in that key but it's a formatted date, if you need a better value to parse check for receipt['latest_expired_receipt_info']['cancellation_date_ms'] same as expires_date
do someone known how to be notified by apple without sending verify requests? it would be reasonable to check only when the product is expiring instead to querying each times for all products because of possible cancellation

First things first. If you're app is not a periodical like a magazine, then you should steer away from ARS (auto-renewable subscriptions). Apple may reject it and insist you use NRS (non-renewing subscriptions).
It's more code than I can practically paste in here, but please follow this guide from Apple on Verifying In-App Purchase Receipts.
Basically you'll be putting the receipt and your secret in a JSON object and sending it to Apple. It's preferable to do this from your server and not from the app.
Here is a good guide on Verifying Store Receipts Using PHP and cURL
The JSON object will look something like this:
{
"receipt-data" : "(receipt bytes here)",
"password" : "(shared secret bytes here)"
}
Apple will respond with another JSON object that has info such as expiration date.

Related

StoreKit Subscription with free trial not present in receipt after purchase (tx id off by 1)

I offer a subscription in my application, and after purchase I validate that transaction against the receipt to get its expiration date. I do this by loading the receipt from disk, sending it to Apple for validation/ decryption (I know this is bad practice), and then iterating over the latest_receipt_info in the JSON response looking for a transaction matching the transactionIdentifier of the SKPaymentTransction object.
This works for most subscriptions, but not those offered with a free trial/ introductory offer. Please take a look at my example below:
I purchase a subscription that is returned to my application with a transactionIdentifier of 1000000506350685 and originalTransaction.transactionIdentifier of null.
I get the latest JSON receipt from Apple whose latest entry is one matching my product identifier and timestamp, but the transaction_id is 1000000506350686 and so is original_transaction_id.
Therefore I am unable to validate the purchase.
There are no problems when purchasing a subscription without a free trial/ introductory offer. There are no other objects in the returned receipt that matches the transaction id returned to my application.
This is in the sandbox environment, I am not sure about the behavior in production.
Has anyone had any similar experiences? What can I do to fix this?
I think this is probably due to the fact that you've already purchased that product before in Sandbox, and there's no device receipt in sandbox until you make a purchase (unlike production).
Either way, what you should be looking at is the latest product_id and expiration_date from the receipt to validate if the subscription is active. Transaction IDs can be keys for your transactions if you're saving them in a database, but shouldn't be used to validate a purchase.
Here is a really good blog post that goes over what your in-app purchase server should be doing to validate and keep subscription status up-to-date: iOS Subscriptions are Hard
I do this by loading the receipt from disk, sending it to Apple for
validation/ decryption (I know this is bad practice).
This is actually the recommended approach by Apple.

How do I verify IOS In-App Purchasing Receipts in Bulk?

I'm working on an iOS app that will use Apple's in-app purchasing framework to let the user create auto-renewing subscriptions.
I want my server to check periodically that none of the subscriptions have expired. Apple's Receipt Validation Programming Guide describes a verifyReceipt web service that appears to validate a single receipt.
However, if I want to bulk-verify my entire database every few days, is there a better way to do that than a storm of individual verifyReceipt calls?
You don't need to need to bulk-verify your entire database. A subscription is valid for a fixed duration. If the user cancels their subscription then this takes effect at the next renewal period, not immediately.
Apple customer service can refund a subscription if a user has a case (wrong product purchase is one example they give) but this would be a pretty low figure - and your app should also check for a valid subscription when it starts.
So you should only need to verify subscriptions that are known to be expiring "today".
To my knowledge there's no way to send multiple receipts in one bulk request to validate them in the iTunes servers... You have to send one by one. (Multiple requests)...
I don't know if how you are planing to solve this is the best approach. Apple has extended documentation on how to handle auto renewal subscriptions, most of the times you check the validity of the subscription in the app itself.
Check the in-app purchase documentation. It's a very good read if you plan to depend on in-app subscriptions for your business model:
https://developer.apple.com/library/ios/technotes/tn2259/_index.html

Apple In-App-Purchase receipt verification

A client application successfully performs an in-app-purchase and receives a receipt from iTunes. The client then sends this receipt to the server, which verifies it with Apple and, if successful, unlocks some content for the client.
Is it possible to insert extra data in the IAP purchase request such that the receipt or the receipt validation contains this value?
I'm trying to prevent a man-in-the-middle/packet-sniffing type hack i.e. If someone was to discover another person's receipt and validate it against my server, how is the server to know that this receipt wasn't generated by this, the validating person?
From Apple's IAP documentation there are a number of values returns in the validation response. Is it possible that I could use version_external_identifier value? What is it's use or value?
Thanks
version_external_identifier
An arbitrary number that uniquely identifies a revision of your application. This key is missing in receipts created by the sandbox.
That really answers your question; its a way to determine what revision of your application made the purchase. You can have the same version number, but multiple revisions, and this allows for that and IAP.
Is it possible to insert extra data in the IAP purchase request such that the receipt or the receipt validation contains this value?
Ultimately, no. Apple have control of the receipt, and its contents. If you follow the guidelines in the documentation (see this for more info along with the link you posted), you will be ok against a man in the middle attack (Apple would have considered this). Make sure connections to your server and to Apple are HTTPS.
A man in the middle to make a purchase on someones behalf is slightly moot anyway; the purchase request is tied to an Apple ID, meaning the man-in-the-middle would need to know their credentials.

How to verify a iTunes Store receipt for in-app purchase

First of all, I am not talking about calling https://buy.itunes.apple.com/verifyReceipt/, instead, what I am asking is how to verify an iTuneStore receipt from one of our users.
We have in-app purchase in our app, and we verify in-game receipts on our server before we grant the product. However, this particular user claimed that he got charged by iTunes store but did not get the purchased products, and sent us his receipt as a proof.
So I am wondering if there is anyway we can verify such a receipt. There is "receipt no" and "order number" in the iTunes Store receipt, however, with in-game receipt verification we get transaction_id, and these numbers are quite different. Any idea?
Thanks in advance.
You should contact Apple's developer support. I doubt there is a way to verify it by yourself if you can't find a respective transaction in your own systems.
There is no manual way to do this, unless you can somehow get the receipt data from his device, in which case you can use Apple's API that you mentioned to verify his claims. If what he says is true then suggest him to claim a refund and then re-purchase. He should be able to do this here.
Now, the Lookup Order ID of App Store Server API could get a customer’s in-app purchases from a receipt using the order ID.
GET https://api.storekit.itunes.apple.com/inApps/v1/lookup/{orderId}
You could find the corresponding transactionId from this API response JWSTransactionDecodedPayload
For how to do the Lookup Order ID request, please refer to https://github.com/richzw/appstore#look-up-order-id
There is source code at https://github.com/roddi/ValidateStoreReceipt/blob/master/validatereceipt.m which will validate a receipt, and let you parse it and dump out all the transactions.
If the IAP transaction is there you would see it.
You'd have to write a special-purpose iOS app which uses that code and feed it the receipt. It seems like a lot of work.
I am not talking about calling https://buy.itunes.apple.com/verifyReceipt/
Actually u have online receipt verification tool, accessible by link above.
There is no better way to verify Your receipt. Offline verification is possible, but if even it succeeded, nobody can give 100% warranty that Your receipt is valid(maybe just same signature, but still fake one).
Apple processing every purchase on its servers and if it successful, receipt saved to database. If u want to check receipt u must access to that database and request for Your receipt. So You have a tool, but don't want to use it? No reliable way then.

How does Apple notify iOS apps of refunds of in-app purchases (IAP)?

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?
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.
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.
The strategy is:
You save the latest_receipt ("MIIUJgYJKoZIhvc..." base64) field in your DB, associated with the user account.
Every day you query apple to validate all the receipts, by sending them the base64 receipt from saved latest_receipt field.
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).
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.
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/
According to latest documentation, server to server notification with type CANCEL is now handling cancel scenario by Apple customer support.
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

Resources