The documentation doesn't say what appStoreReceiptURL exactly is ( i mean [[NSBundle mainBundle] appStoreReceiptURL]). My current suggestion - it is the receipt for last performed IAP incomplete transaction.
But what if we have multiple incomplete transactions?
Ok, looks like I've found the answer (experimentally).
The receipt file at path returned by appStoreReceiptURL method contains all receipts ever issued by Apple for particular application. Moreover, it contains even those purchases that were made on other devices with the same AppleId.
Related
In application I need to implement auto-renewable subscriptions purchases.
As I understand documentation, I should check AppStore receipt on app launch and then validate that receipt with my server.
In my code I do following to retrieve receipt:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
Sometimes receipt is becoming nil.
For example:
I've launched an app and made purchase. After that the code above returns non empty receipt.
App is stopped with Xcode.
On next app launch receipt is nil.
I do not understand, why this happens. receiptURL points to file with receipt, but [NSData dataWithContentsOfURL:receiptURL] returns nil.
If I request receipt refresh, then app shows dialog for entering Itunes credentials. This is not good, because this dialog will appear on app launch and this is unexpected behavior.
Am I doing something wrong?
Or maybe this is sandbox-specific case?
In production there will always be a receipt (even if the app is free), that can contain your additional purchase, in case you have bought something or restored from an already purchased item.
In test environment until you buy something the receipt is nil, if you want to test a case close to production, you should first try a restore (now you will have a receipt) and do what you need.
Here it is suggested that your payment procedure is being hacked, by calling the SKPaymentTransactionStatePurchased regardless. You need to validate the receipt on a server before accepting any receipts.
You can find a great description of receipts and hacks here.
I am currently validating transaction.transactionReceipt base64encoded as a string server side. The problem is that transaction.transactionReceipt is deprecated now and I don't know what to replace this with for in app purchases.
The new documentation suggests using NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
, but the docs say that it is only for app purchase validation and not for in app purchases.
What do I do in this case?
As can be read in other parts of the documentation the receipt does contain the data for IAP as well. I've also verified this from my own experience, as this is the way we did it where I worked. Basically the only change is that instead of having a receipt for each IAP there is a single one for both the app and all IAPs.
My guess however is that the URL remains the same, although I haven't verified it, but the data contained at the URL changes.
I am trying to get the new inapp purchasing API (ios 7+) to work and I see that the receipt data is so much larger when I read receipts in this way:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
I am doing this inside of:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
So what I noticed is I send the receipt to
"https://buy.itunes.apple.com/verifyReceipt";
And the data I'm sending is much larger than transaction.transactionReceipt.
In fact, the apple response seems to have dozens of receipts in the "in_app" array. Is this normal? I have finished these transactions yet there is so many receipts in the unified receipt. Is that right? Would my big spenders (those who buy thousands of IAPs) be sending huge receipts with thousands of transactions in them?
Also what is SKReceiptRefreshRequest for?
Update! I found out that in a live environment, all consumable receipts are removed after they calling finishTransaction. On the sandbox environment this is not the case, and it just keeps them, so the number of receipts can get into the hundreds.
iOS7 introduced many IAP changes. Amongst those:
1) receipts are stored in the app bundle (yes, plural on receipts)
2) whether a particular receipt persists in the bundle depends on the kind of product. Eg, I think receipts for consumables don't get persisted
What kind of products do you have?
Look up the API for the receipt refresh. Sometimes the receipt is not in the bundle, and you can refresh it. The user gets prompted for apple id/password so it can be annoying for the user if/when you use it.
Yes, this is normal
Apply sends you a bunch of all receipts. You have to figure out your own what to do with these. That's because even if the transaction is finished you might need the receipt to handle the content of the purchase.
I'm releasing an updated version of my app, and I'm transitioning from paid to freemium. To give existing users an ad-free experience, I'm looking to track when they've originally purchased the app.
I'm looking into RMStore, but it's not clear to me how to test reading the original purchase date out of the receipt. I've come up with some simple code which I think should work, but I don't have a good way to test it.
[[RMStore defaultStore] refreshReceiptOnSuccess:^{
NSURL *url = [RMStore receiptURL];
NSData *data = [NSData dataWithContentsOfURL:url];
RMAppReceipt* r =[[RMAppReceipt alloc] initWithASN1Data:data];
// Cheap and easy conversion to a float...
// IRL do a real comparison with the strings...
if ([[r originalAppVersion] floatValue] < 2.0)
{
// Do something for early-adopters
}
} failure:^(NSError *error) {
// Ruh-roh!
}];
I have two issues:
I don't have a valid receipt. What's the procedure for getting one? Do I need an app bundle ID that's already live? Are there test receipts somewhere?
If I want to base the logic on dates instead of version numbers, can I do that? There's no originalPurchaseDate property on RMAppReciept. (I did file an issue on GitHub.)
What's the correct way to use RMStore to get the original purchase date of an app?
There is no such information in the receipt. You can get the purchase date of in-app purchases, though. RMStore helps you with this via the RMAppReceiptIAP object.
To give existing users an ad-free experience, I'm looking to track when they've originally purchased the app.
As suggested by #sergio, you can read the original app version from the app receipt. Bear in mind that receipts are only available in iOS 7.
I don't have a valid receipt. What's the procedure for getting one? Do I need an app bundle ID that's already live? Are there test receipts somewhere?
Apps in sandbox environment will have a test receipt, but you can't manipulate its fields.
As an alternative, you can configure RMAppReceipt or mock it to test your various workflows.
If I want to base the logic on dates instead of version numbers, can I do that? There's no originalPurchaseDate property on RMAppReceipt.
Not with the app receipt, as there is no such field at app level.
BTW, avoid refreshing the receipt on startup as it tends to show the Apple ID password prompt. Only refresh if you don't find the receipt or if the info is missing.
Maybe you want to try with:
RMAppReceipt* appReceipt = [RMAppReceipt bundleReceipt];
if ([[appReceipt originalAppVersion] floatValue] < 2.0)
/**
Returns the app receipt contained in the bundle, if any and valid. Extracts the receipt in ASN1 from the PKCS #7 container, and then parses the ASN1 data into a RMAppReceipt instance. If an Apple Root certificate is available, it will also verify that the signature of the receipt is valid.
#return The app receipt contained in the bundle, or nil if there is no receipt or if it is invalid.
#see refreshReceipt
#see setAppleRootCertificateURL:
*/
+ (RMAppReceipt*)bundleReceipt;
I'm implementing security in my iOS 7 application for iOS In-App purchases. When testing with LocalIAPStore hack I see that when I call the method [[NSBundle mainBundle] appStoreReceiptURL] the returned receipt is null, so it's all right here.
The problem comes when the user purchases an option without having activated LocalIAPStore. If this is done, a receipt is generated. Then, I send it to my server to check that everything is correct and show purchase, but if active again LocalIAPStore, I keep returning a correct receipt, but has actually purchased another option and this is returned as a calidate.
To verify receipt, what else can I do? What should I check locally to avoid it? Any tips? How do you do it?
Thanks!