In-app purchases are working correctly with my app. I'm even using my own server to validate transaction receipts.
However, I seem to be having an issue with the SKPaymentQueue TransactionObserver and/or DefaultQueue.
An iTunes prompt, for an old test account, will appear whenever I make the following call -
SKPaymentQueue *currentQueue = [SKPaymentQueue defaultQueue];
I cannot figure out how to stop this old test account from appearing whenever I need to make a purchase or restore transactions.
Someone has recommended the following code, which would appear to finish all old transactions, but it doesn't resolve my issue.
SKPaymentQueue* currentQueue = [SKPaymentQueue defaultQueue];
[currentQueue.transactions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[currentQueue finishTransaction:(SKPaymentTransaction *)obj];
}];
I've also tried removing the app, deleting the old test account from the device and also iTunesConnect. The old user prompt still appears!
Any help would be greatly appreciated, thanks.
are they sandbox transactions or production transactions?
sandbox transactions are a big mess, sometime i find old transactions in the queue, and the lifecycle of sandbox transactions is not totally coherent with production transactions's lifecycle
I just solved the same problem in my code. The issue was because I was not removing the transaction observer when the app would quit.
add this to your app delegate:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self.storeManager];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self.storeManager];
}
I also needed to restart my devices to make sure they were completely clear. Then when you do a restore all purchases the system should be back to normal.
Related
I'm trying to get the RMStore library to work, but therefore I need to be able to restore the in app purchases the user could have possible made. I'm aware of the method: restoreTransactionsOnSuccess: but with this method I don't get to know which in app purchases are restored.
[[RMStore defaultStore] restoreTransactionsOnSuccess:^{
} failure:^(NSError *error) {
}];
The above code is what I used, and it's working because in the logging I see the bought in app purchases. Do I miss something?
Could somebody point me to the right direction?
Thanks in advance!
As Merlea Dan mentioned, you can achieve this with notifications. The RMStore documentation states:
Payment transaction notifications are sent after a payment has been
requested or for each restored transaction.
Simply register as an observer and implement:
- (void)storePaymentTransactionFinished:(NSNotification*)notification
{
SKPaymentTransaction *transaction = notification.rm_transaction;
if (transaction.state == SKPaymentTransactionStateRestored)
{
// Do something
}
}
It's worth mentioning that a few others have requested to have restoreTransactionsOnSuccess return the list of restored product ids in the success block. You might want to subscribe to this issue in case it gets added.
I have been testing In App Purchases and can only assume that there are transactions stuck in the queue.
I have called [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; but every time I start the app it asks me to login to iTunes.
I have checked within -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{ but even if I place an NSLog in the first line, this is NOT being called. Thus I don't believe this is being called.
I have also called NSLog(#"PAYMENTQUEUE:%#",[[SKPaymentQueue defaultQueue] transactions]); which shows null.
Can someone advise why I would constantly be asked to login to iTunes every time I start the app all of a sudden? As I say, I can only assume it is a transaction, but how can I track it down?
Your device has been infected with the "endless loop" for failing to have called finishTransaction. It will reoccur every week like clockwork. Check out the IAP Developer's Forum for more info. The queue is empty because you need to log in as the 'infected' user.
Just in case anyone comes across this later on with the same issue. I left the device and stopped trying to sort it out. I deleted the app too.
Around half an hour later I was prompted to logon to the iTunes sandbox store (remember there was no app on anymore).
I logged in, and this scenario occurred another time.
After that all seemed to go quiet.
I have tried installing the app again today (12 hours later) and the issue has gone away.
I can only assume something got jammed up?
I'm developing an ios Application with IAP feature. It works well. But I encountered a strange issue today. It always show me a message to ask me to "Sign In to iTunes Store" for some reasons. Here is the screenshot:
It always shows this every time when I start the application or resume from background. It even still shows this after I delete and reinstall the application. When I setup breakpoints in my source code. There is no any transaction,payment delegate callbacks. Can anybody tell me what the reason is ? Could it be the problem of Apple IAP Sandbox? (I run the same application in other devices without any problem. I can purchase, restore in Sandbox.)
I've had this problem too a couple of times, it turned out I was stopping processing of transactions before calling finishTransaction on all queued transactions. I was forgetting to finish failed transactions in the particular case.
So, you could try letting your application run once and call finishTransaction for each and every one in your observer delegate like this:
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
foreach(SKPaymentTransaction *tr in transactions) [queue finishTransaction:tr];
}
i.e. finish every transaction irrespective of transaction state.
After the test run revert to your original handling code.
Hope this helps.
I tried multiple things :
call finishTransaction for all queued transactions as explained by mpramat (in my case, the array was empty)
Reboot the iDevice
Delete the test account in iTunes Connect.
Finally, the only thing that surprisingly worked for me was to delete the app in the iDevice then re-install it via XCode.
I'm simulating purchases of an Auto-renewing subscription in my app on an iPhone. My issue is that the purchase is considered to be done by the App store while it is not.
Here is what is going on:
The user presses a button to purchase the renewing subscription
The user gives his iTunes password and confirms the purchase
The app submits the receipt received from the app store to my server to check validity
The server returns a "ok" or "not ok" string. The app calls finishTransaction only on "ok"
I have an issue when there is a network failure on step 3. I can't validate the receipt. But if the user tries to purchase a second time, the app store tells him that he has already subscribed, even though I didn't call the finishTransaction method to complete the purchase!
Is this an expected behavior? Shouldn't the app-store treat non-finished transactions as non-finished, or am I missing something?
I would welcome any suggestion to solve this issue.
-(void) userPurchase:(SKProduct*) product{
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
NSLog(#"paymentQueue updatedTransaction");
for (SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
break;
case SKPaymentTransactionStatePurchased:
[self recordSubscription:transaction];
break;
case SKPaymentTransactionStateFailed:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self recordSubscription:transaction];
break;
default: NSLog(#"Default");
break;
}
};
}
-(void) recordSubscription:(SKPaymentTransaction*)transaction{
NSString *jsonObjectString = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];
NSMutableDictionary *params = [[NSMutableDictionary alloc] initWithObjectsAndKeys:jsonObjectString,#"receiptdata", nil];
[[AFNetworkSubClass sharedClient] postPath:#"myserver" params:params
success:^(AFHTTPRequestOperation *operation, id output) {
/* some code */
if([valstring isEqualToString:#"ok"]){
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"validation failed");
}
I think before you call recordSubscription method, you can call finish finishTransaction method to remove payment from payment queue because you have finished the purchase action with apple server.
If network failure make you can't validate app receipt, just record the receipt and validate the receipt to your own server again or validate when network is reachable another time.
I hope it can help you.
Users cannot purchase same subscription until it expires. In your case, the purchasing transaction was finished, but you didn't provide the service because of the network failure.
I used to have similar problem. You may need to ask the users to restore all completed transactions.
Then completeTransaction function will be called. Good luck!
If you haven't called finishTransaction that doesn't meant the transaction has not taken money from the user. All that call does is prevent StoreKit from notifying your App on the transaction every launch. Its just a means for you to tell StoreKit that you have finished with the transaction i.e. unlocked the users content and saved that to your backend etc.
So even with a network error you should be retrying your API call. If you close the App and reopen you should get a transaction update to indicate it has been 'purchased' giving you time to submit to your server again:)
This is the expected behaviour otherwise you would be double/triple billing users.
Just a heads up if your App doesn't fit within the following statement then you will not be able to use Auto-renewing Subscriptions. The following is from the App Review Guidelines;
11.15 Apps may only use auto renewing subscriptions for periodicals (newspapers, magazines), business Apps (enterprise, productivity,
professional creative, cloud storage) and media Apps (video, audio,
voice), or the App will be rejected
In case your app does fit into this bracket then what you could do setup your app so it considers itself to be temporarily "Subscribed" and it keeps trying to authenticate with the server (notify the user if it has been too long since it was connected to the internet).
I used to have issues like this with both the app store purchases and the game center. And I know this is going to sound like a non answer, but it worked for me.
I deleted the app and all its data from my device, restarted xcode and cleaned the project. Turned the device on and setup a fresh deployment. And the app started to receive the correct response from the App Store. I'm not sure if it was because something was cached on the device or if the App Store just needed more time to work correctly but things just fell into place after that.
Although I have never worked with subscription based purchases, nothing in your code stands out as incorrect as far as I can tell.
Good luck.
I have an in-app purchase which is Apple hosted. However I can't figure out how to download the content associated with it.
I could get the downloadable objects and request the download start:
[[SKPaymentQueue defaultQueue] startDownloads:currentTransaction.downloads];
that's when I get lost: what should I do after that?
I have taken a look at Apple doc and didn't find anything that could possibly help me.
Thx
Implement an SKPaymentTransactionObserver. The observer will be notified when the payment queue changes status. For example, paymentQueue:updatedDownloads: will be called with an array of SKDownloads whose downloadState indicates the status change for a download. If you want to know about completed downloads, look for downloadState == SKDownloadStateFinished.
See http://developer.apple.com/library/ios/#documentation/StoreKit/Reference/SKPaymentTransactionObserver_Protocol/Reference/Reference.html
I could solve my problem. Calling the
[[SKPaymentQueue defaultQueue] startDownloads:currentTransaction.downloads];
then
(void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads
is constantly called back. I guess I had forgotten some delegates or something like that. Besides that, I deleted the app from my device and re-compiled it again from Xcode. Then it worked like a charm =) Thanks