In App Purchase - No purchase to restore - ios

I'm trying to figure out if I get a message back when a user tries to restore an In App Purchase but there was no purchase ever made.
Right now, as soon as a user taps the restoreButton, I disable the restoreButton.
- (IBAction)purchaseRestore:(id)sender {
NSLog(#"4 IBAction Purchase Restore Method: start");
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue]restoreCompletedTransactions];
NSLog(#"4 Purchase Restore: SKPayment Queue two lines... log in user");
restoreButton.enabled = NO;
NSLog(#"4 Restore button enabled: No");
}
So if they click the restoreButton, and have nothing to restore then the restoreButton just stays grayed out.
I would like to do something like a UIAlert or change the restoreButton text if that happens to say "You don't have any items to Restore", but I assume I need to get some message back from Apple saying "no items to restore" so I can fire off that code.
Here's my updatedTransactions code if needed:
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSLog(#"5 Finish Transaction");
restoreButton.hidden = YES;
NSLog(#"5 restore button hidden: Yes");
[self showButtonThree];
NSLog(#"5 Show Button Three");
NSLog(#"5 Restored: End");
break;
Any ideas? Thanks!
Update
Tried this:
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
NSLog(#"Completed Transactions Finished");
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Restored succesfully" message:nil delegate:
self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}
But that was popping up whether there was a transaction to restore or not. I had assumed this would get called if there was no transaction to restore maybe, but makes sense that it wouldn't:
-(void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
NSLog(#"Completed Transactions Failed with Error");
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Not restored succesfully" message:nil delegate:
self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}

From the official Apple documentation for restoreCompletedTransactions
After the transactions are delivered, the payment queue calls the
observer’s paymentQueueRestoreCompletedTransactionsFinished: method.
If an error occurred while restoring transactions, the observer will
be notified through its
paymentQueue:restoreCompletedTransactionsFailedWithError: method.

Related

How can i solved in app purchase SKProductsResponse invaild ID

Hey guys i am frustrated to retrieve a product from iTunesConnect but it always return invalid ID. I fulfill all requirements but not getting any response from it. I followed these steps and my answer is Yes..
.Please give me some solution to solve this problem. Thanks in advance..
1.Have you enabled In-App Purchases for your App ID?
2.Have you checked Cleared for Sale for your product?
3.Have you submitted (and optionally rejected) your application binary?
4.Does your project’s .plist Bundle ID match your App ID?
5.Have you generated and installed a new provisioning profile for the new App ID?
6.Have you configured your project to code sign using this new provisioning profile?
7.Are you building for iPhone OS 3.0 or above?
8.Are you using the full product ID when when making an SKProductRequest? Have you waited several hours since adding your product to iTunes Connect?
9.Are your bank details active on iTunes Connect? (via Mark) Have you tried deleting the app from your device and reinstalling? (via Hector, S3B, Alex O, Joe, and Alberto)
Code:
-(void)fetchAvailableProducts{
NSSet *productSet = [NSSet setWithObjects:#"com.magazineapp.books",nil];
productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:productSet];
productsRequest.delegate = self;
[productsRequest start];
}
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
- (void)purchaseMyProduct:(SKProduct*)product{
if ([self canMakePurchases]) {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:product];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// [[SKPaymentQueue defaultQueue] addPayment:payment];
}
else{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Purchases are disabled in your device" message:nil delegate:
self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}
}
-(IBAction)purchase:(id)sender{
[self purchaseMyProduct:[validProducts objectAtIndex:0]];
}
#pragma mark StoreKit Delegate
-(void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(#"Purchasing");
break;
case SKPaymentTransactionStatePurchased:
if ([transaction.payment.productIdentifier
isEqualToString:kMySubscriptionFeature]) {
NSLog(#"Purchased ");
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Purchase is completed succesfully" message:nil delegate:
self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"Restored ");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"Purchase failed ");
break;
default:
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
SKProduct *validProduct = nil;
NSUInteger count = [response.products count];
if (count>0) {
validProducts = response.products;
validProduct = [response.products objectAtIndex:0];
if ([validProduct.productIdentifier
isEqualToString:kMySubscriptionFeature]) {
DLog(#"vaild product");
}
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Not Available"
message:#"No products to purchase"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
[activityIndicatorView stopAnimating];
}

iOS in-app restore purchases nightmare

I've read ALL the threads etc. on the internet and still am getting back zero restored transactions when I try to restore purchased products (sandbox environment).
I have created 3 Non-renewing products and 2 sandbox testers through iTunes connect.
I have had zero issues setting up the ability to actually purchase products; all is well.
I have created a "restore purchases" button and am trying to get it to work. I've been testing the following: install app and make purchase. Purchase again and it asks if I want to renew. Delete app and log out of app store. Reinstall app and press restore purchases ... zero!!!
Here's some code I'm using:
-(void)viewDidLoad
{
[super viewDidLoad];
//in-app purchase setup
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
-(void)viewDidAppear:(BOOL)animated
{
//make request for in-app products
if([SKPaymentQueue canMakePayments])
{
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObjects:#"product1_id",#"product2_id",#"product3_id",nil]];
request.delegate = self;
[request start];
}
else
{
//alert user
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"..." message:#"Please enable In App Purchase in settings" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
-(IBAction)restoreTransactionsPressed
{
_refreshRequest = [[SKReceiptRefreshRequest alloc] init];
_refreshRequest.delegate = self;
[_refreshRequest start];
}
-(void)requestDidFinish:(SKRequest *)request
{
if([request isKindOfClass:[SKReceiptRefreshRequest class]])
{
NSLog(#"Got a new receipt... %#",request.description);
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
}
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"%#",error.description);
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"updatedTransactions");
for(SKPaymentTransaction *transaction in transactions)
{
NSLog(#"updatedTransaction:%# ... state:%#",transaction.originalTransaction.payment.productIdentifier,transaction.transactionState);
switch(transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
NSLog(#"SKPaymentTransactionStatePurchased");
//unlock feature code
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"SKPaymentTransactionStateFailed");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"SKPaymentTransactionStateRestored");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
default:
break;
}
}
}
-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSLog(#"paymentQueueRestoreCompletedTransactionsFinished");
NSLog(#"received restored transactions: %i", (int)queue.transactions.count);
for(SKPaymentTransaction *transaction in queue.transactions)
{
NSLog(#"transaction:%#",transaction);
}
}
-(void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
NSLog(#"restoreCompletedTransactionsFailedWithError %#",error);
}
Why do I always get back zero restored transactions?
it seems that "restoreCompletedTransactions" isn't in turn calling - paymentQueue:updatedTransactions: if that helps.
I'm unsure that SKReceiptRefreshRequest is necessary, but I've tried both refreshing the receipt before restoring and not refreshing the receipt before restoring.
Please don't post links to thread. I've investigated this for a few Days now! Some people claim that Apple's Sandbox had issues in the past ... could this be the reason? tyvm in advanced; all help is appreciated!
According to Apple's documentation, you can't restore non-renewing subscriptions. Also, have a read here for a more detailed explanation.

Does restoring in-app purchases work with test accounts on iTunes Connect?

Purchasing works perfect. But restoring in-app purchase doesn't work from test accounts on iTunes Connect. Is it correct? I use the following code to restore purchase:
... {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
// called when a transaction has been restored and and successfully completed
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
[self recordTransaction:transaction.originalTransaction];
[self provideContent:transaction.originalTransaction.payment.productIdentifier];
[self finishTransaction:transaction wasSuccessful:YES];
}
// saves a record of the transaction by storing the receipt to disk
- (void)recordTransaction:(SKPaymentTransaction *)transaction {
if ([transaction.payment.productIdentifier isEqualToString:[self getProductId:gFullVersion]]) {
// save the transaction receipt to disk
[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:[self getProductId:gFullVersion]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
// removes the transaction from the queue and posts a notification with the transaction result
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful {
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, #"transaction" , nil];
if (wasSuccessful) {
// send out a notification that we’ve finished the transaction
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionSucceededNotification object:self userInfo:userInfo];
} else {
// send out a notification for the failed transaction
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionFailedNotification object:self userInfo:userInfo];
}
}
The application shows dialog to enter AppleID password.
I found the issue. I have two non-consumable in-apps. One is removed. And one is active. The following code returns only removed in-app:
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
So, the issue is on App Store...
P.S. When I buy it again it's free, so two in-apps are really made.
I followed this code for Restore button.
-(void)restoreButtonTapped{
[[RMStore defaultStore] restoreTransactionsOnSuccess:^{
NSLog(#"Restored Successfully");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[apDelegate hideLoading];
BOOL isExpired = [self isPurchaseExpired:[NSDate date]]; // DEV-TODO: again fetching the expireDate from user Defaults in the method, so just sending the current Date.
if (!isExpired)
{
[self moveToLoginScreen];
}
else
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"title" message:#"Product expired, Please buy the product for uninterrupted services." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}
} failure:^(NSError *error) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSLog(#"Failed to restore Completed Transactions");
[apDelegate hideLoading];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"MDPulse" message:#"Failed to restore Transactions. Please try again." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}];
}
}
- (void)restoreCompletedTransactions
{
NSLog(#"Restore Tapped in transaction process");
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSLog(#"Restore completed transactions finished.");
NSLog(#" Number of transactions in queue: %d", [[queue transactions] count]);
for (SKPaymentTransaction *trans in [queue transactions])
{
NSLog(#" transaction id %# for product %#.", [trans transactionIdentifier], [[trans payment] productIdentifier]);
NSLog(#" original transaction id: %# for product %#.", [[trans originalTransaction] transactionIdentifier],
[[[trans originalTransaction] payment]productIdentifier]);
if ([[[trans payment] productIdentifier] isEqual: kMDPulseSubscriptionProductIdentifier]) {
NSLog(#"Purchase Restored");
// Do your stuff to unlock
}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
NSLog(#"%s","User Cancel.");
MedPulseAppDelegate *appdelegate =( MedPulseAppDelegate *)[[UIApplication sharedApplication]delegate];
[appdelegate hideLoading];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
Welcome!
I think this will help you out.

in-app purchase being restored even before purchase

An update of my existing app has been published on the app store today with in-app purchase to remove ads. I had to put a restore button as it is non-consumable in-app purchase. i have noticed that i can restore the in-app purchase without even purchasing it.
The app went for sale about 4 hours ago. Is this normal for a new app? does the app store take some time to hook up the in-app purchase (probably a silly question)? or have i done something wrong in my code?
Please response if you know whats going on.
EDIT:
Here is the code i used
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
default:
break;
}
}
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
[[SKPaymentQueue defaultQueue]addTransactionObserver:self];
}
-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
if (SKPaymentTransactionStatePurchased)
{
//save purchase
NSUserDefaults *savePurchase = [NSUserDefaults standardUserDefaults];
[savePurchase setBool:TRUE forKey:k_Save];
[savePurchase synchronize];
_adBanner.hidden = YES;
// alert after successful restore.
UIAlertView *restoreTransactionAlert = [[UIAlertView alloc] initWithTitle:#"Congratulations!"
message:#"Your purchase is restored."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[restoreTransactionAlert show];
}
else
{
// alert after unsuccessful restore.
UIAlertView *restoreTransactionAlert = [[UIAlertView alloc] initWithTitle:#"Restore failed"
message:#"Restore failed. please try again"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[restoreTransactionAlert show];
}
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
}
Your implementation for restoring transactions is incorrect. Your condition in the -restoreTransactionsCompleted method, i.e.
if (SKPaymentTransactionStatePurchased)
Will always return true, as SKPaymentTransactionStatePurchased is an enum with value 1.
Look up on the documentation on restoring transactions here.

In-app Purchasing

I am trying to get this whole in-app purchasing to work, but I have encountered some problems. So I am able to "buy" the features with my test user account, but I want to test again and again. So I made a delete method that gets rid of the keychain that is generated and quit the app, then build it again and it is back to the "free" state, where I once again try to buy the app, but this time it gives me "you've already purchased this item, tap OK to download it again" so I tap ok, but this time nothing happens and no features get unlock, wtf?
code:
-(void)deleteKeyChain:(id)sender {
NSError *error = nil;
NSLog(#"delete!!!!");
[SFHFKeychainUtils deleteItemForUsername:#"someUser" andServiceName:kStoredData error:&error];
}
-(void)doFeature:(id)sender {
[newPlayer pause];
if ([self IAPItemPurchased]) {
// do the feature 2!
// featureLabel.text = #"Feature: 2";
} else {
// not purchased so show a view to prompt for purchase
askToPurchase = [[UIAlertView alloc]
initWithTitle:#"All Features"
message:#"Tap refresh anytime to read latest 5 emails. To read all emails with no ads and to continue reading in the background, please purchase the full version of this app."
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"OK",#"Later on", nil];
askToPurchase.delegate = self;
[askToPurchase show];
[askToPurchase release];
}
}
#pragma mark StoreKit Delegate
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
// show wait view here
// statusLabel.text = #"Processing...";
break;
case SKPaymentTransactionStatePurchased:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view and unlock feature 2
// statusLabel.text = #"Done!";
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Complete"
message:#"You now have the full version of Emails Aloud!!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[tmp release];
NSError *error = nil;
[SFHFKeychainUtils storeUsername:#"someUser" andPassword:#"pass" forServiceName:kStoredData updateExisting:YES error:&error];
// apply purchase action - hide lock overlay and
// [feature2Btn setBackgroundImage:nil forState:UIControlStateNormal];
// do other thing to enable the features
break;
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view here
// statusLabel.text = #"";
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(#"Error payment cancelled=%d",transaction.error.code);
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view here
// statusLabel.text = #"Purchase Error!";
break;
default:
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
// remove wait view here
// statusLabel.text = #"";
SKProduct *validProduct = nil;
int count = [response.products count];
if (count>0) {
validProduct = [response.products objectAtIndex:0];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
SKPayment *payment = [SKPayment paymentWithProductIdentifier:#"com.myapp.shit"];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Not Available"
message:#"No products to purchase"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[tmp release];
}
}
-(void)requestDidFinish:(SKRequest *)request
{
[request release];
}
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"Failed to connect with error: %#", [error localizedDescription]);
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (alertView==askToPurchase) {
if (buttonIndex==0) {
// user tapped YES, but we need to check if IAP is enabled or not.
if ([SKPaymentQueue canMakePayments]) {
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:#"com.myapp.shit"]];
request.delegate = self;
[request start];
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Prohibited"
message:#"Parental Control is enabled, cannot make a purchase!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[tmp release];
}
}
}
}
note:The someUser/pass is not a username/password of anybody. It is just a text that I chose to register the users in app purchase into their device.
IF you already purchased the application with the user/password from the iTunes Connect user, you need to look for SKPaymentTransactionStateRestored and not SKPaymentTransactionStatePurchased.
SKPaymentTransactionStatePurchased only happens once per user for a non-consumable product. Try creating many more test users on iTunes Connect.

Resources