IOS In-app purchase does not trigger restoreTransaction on already purchased goods

I am working on buying an application using ios, and I have little doubt that doubts will be useful for a more recent one, like me, so understand about buying an application.

1) I had a problem in my application if the user “installed my application on a new device or the same device if he uninstalls my application earlier”, while the user is trying to buy a purchase already, my code does not call restoreTransaction in the switch updatedTransactions

I get a message that you ve already purchased this tap okay to downlaod it FREE Enviornment sandbox and it causes the SKPaymentTransactionStatePurchased case, but it does not call SKPaymentTransactionStateRestored , which will be a problem in my case ..

So, I have a separate Restore button for restoring the entire video item that the user has already brought in, so you just need to know that he will reject my application in the Apple store?

2) To purchase goods, he asks for a password only once, and after that he does not ask me for a password for the purchase. it directly displays a dialog box with a confirmation button, but my project manager says that he should ask for a password for each purchase of goods.

It asks for a password every time I try to restore Purchase..strange.

3) I am currently testing in the sandbox when I try to buy with a real apple identifier on which it was not possible to display the purchase (I should use a test account to verify the purchase, as stated in the Apple document), but my project manager says that he should ask new ones to check the username if you are testing in the sandbox (the document says that you need to exit the settings manually, but my project manager wants it to run automatically),

So you just need to ask if it is possible to release and display the window using encoding (I know this is not possible, but for the information I ask)

4) My application is currently running in a sandbox environment, but do I need to change something for a real purchase for my application? .. or does the apple automatically change the sandbox to a real purchase when the apple checks my application and signs it up and is available in the app store ?

5) I check the transaction on my own server, so I send the sandbox 1 if I am in the sandbox environment, otherwise I need to send 0 (currently the hard code value for hard code is 1), so is there any method to detect the environment - is it a sandbox or real?

Here is my purchase code and button code Restore any help is welcome

Purchase code

 - (IBAction)PaymentButton:(id)sender { loadingHUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; loadingHUD.labelText = NSLocalizedString(@"Loading", nil); [loadingHUD show:YES]; [self startPurchase];// call the restore Purchase method //[loadingHUD showWhileExecuting:@selector(startPurchase) onTarget:self withObject:nil animated:YES];// call the restore Purchase method } - (void)startPurchase { if([SKPaymentQueue canMakePayments]) { NSLog(@"IN-APP:can make payments"); [self requestProductData]; } else { NSLog(@"IN-APP:can't make payments"); loadingHUD.hidden=YES; } } - (void)requestProductData { NSLog(@"IN-APP:requestProductData"); SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObject:myIdentifier]]; request.delegate = self; [request start]; NSLog(@"IN-APP:requestProductData END"); NSLog(@"Productdata is %@",myIdentifier); } - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; @try { SKProduct *product = [response.products objectAtIndex:0]; SKPayment *newPayment = [SKPayment paymentWithProduct:product]; [[SKPaymentQueue defaultQueue] addPayment:newPayment]; NSLog(@"IN-APP:productsRequest END"); } @catch (NSException *exception) { // Failed to purchase Hide the progress bar and Display Error Dialog loadingHUD.hidden=YES; UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"Error in Product id can not purchase" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } } - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: [self completeTransaction:transaction]; break; case SKPaymentTransactionStateFailed: [self failedTransaction:transaction]; break; case SKPaymentTransactionStateRestored: [self restoreTransaction:transaction]; default: break; } } } - (void) completeTransaction: (SKPaymentTransaction *)transaction { NSLog(@"Transaction Completed"); // Finally, remove the transaction from the payment queue. [self verifyReceipt:transaction]; // Call the verifyReceipt method to send transaction.bytes NSLog(@"Purchase Transaction finish"); [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; } - (void) restoreTransaction: (SKPaymentTransaction *)transaction NSLog(@"Transaction Restored %@",transaction.originalTransaction.payment.productIdentifier); // You can create a method to record the transaction. // [self recordTransaction: transaction]; loadingHUD.hidden=YES; // You should make the update to your app based on what was purchased and inform user. // [self provideContent: transaction.payment.productIdentifier]; // Finally, remove the transaction from the payment queue. [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; } - (void) failedTransaction: (SKPaymentTransaction *)transaction { loadingHUD.hidden=YES;// hide loadingHUD if (transaction.error.code != SKErrorPaymentCancelled) { // Display an error here. UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Purchase Unsuccessful" message:@"Your purchase failed. Please try again." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; } 

It’s easy to recover

 -(void)startRestore { [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; } - (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { if ([queue.transactions count] == 0) { HUD.hidden=YES; UIAlertView *restorealert = [[UIAlertView alloc] initWithTitle:@"Restore" message:@"There is no products purchased by you" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [restorealert show]; } else { NSLog(@"received restored transactions: %i", queue.transactions.count); for (SKPaymentTransaction *transaction in queue.transactions) { NSString *temp = transaction.payment.productIdentifier; NSString *testID = [temp stringByReplacingOccurrencesOfString:projectIdString withString:@""]; NSString *productID = [testID stringByReplacingOccurrencesOfString:@"." withString:@""]; // remove Dot NSLog(@"cutted string is %@",productID); [purchasedItemIDs addObject:productID]; NSLog(@"** Purchased item is %@",purchasedItemIDs); } HUD.hidden=YES; HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; HUD.labelText = NSLocalizedString(@"Restoring", nil); [HUD showWhileExecuting:@selector(restorePurchasedItem) onTarget:self withObject:nil animated:YES];// call the restore Purchase method } } 
+4
source share
2 answers

IMHO, you better outline this question in a certain amount if different questions arise.

In any case, I will try to answer:

1) The way it was supposed to be. You receive transactions with the SKPaymentTransactionStateRestored state only if you manually call restoreCompletedTransactions . AFAIK, that the usual practice of creating a single button (for example, "Restore purchases") for this.

2) I can not say anything about it. Usually, every time your application is going to make a purchase (and take some money for the user), it should ask the user for a password.

3) AFAIK, no. You work with Apple servers through the iTunes / AppStore applications. It is their business to remember the iTunes user account. And I do not think that they will give you a way to make the current user log out. The project manager should understand this :-)

4), 5) There is no difference in the production / sandbox environment until you try to verify receipt. If you are talking about working with a server, I hope you use the server to check receipts.

On the device side, everything you do works with the StoreKit infrastructure. You do not define any URLs for Apple servers, you just use the infrastructure classes and call them methods. AFAIK, you do not need to make any changes to your code for the sandbox and production support at the same time.

But there is a difference on your server side: when your application is ready, you should send HTTP POST requests to https://buy.itunes.apple.com/verifyReceipt . On the other hand, when your application is still in the sandbox, use https://sandbox.itunes.apple.com/verifyReceipt .

How to deal with this? Watch the WWDC 2012 308 video on auto-renewal subscriptions. They offer two ways:

1) Smart server. When your application sends receipts to your server, it also passes some parameter to tell the server which Apple server to use. As I can see, you are already using this method.

2) Jet server. Your server always sends receipts to an Apple-made server. You check the response, and if the status is 21007 (according to the documentation “This receipt is a sandbox receipt, but it was sent to the production service for verification.”), You send the same request to the Apple Sandbox server.

+2
source

Use this code, it can help you ....

  //To Show Alert of InAppPurchase - (void)showAlert { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"Click buy to purchase full tracks and other functionalities of the application." delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil]; [alert addButtonWithTitle:@"Buy"]; [alert addButtonWithTitle:@"Restore Transaction"]; [alert addButtonWithTitle:@"Cancel"]; [alert show]; } -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex: (NSInteger)buttonIndex{ if(buttonIndex==0) { // For buy an item [self Declarations]; } else if(buttonIndex == 1) { //For Restore Previous Transaction [self restorePreviousTransaction:nil]; } else { //Do something here.. } } #pragma mark In App Purchase -(void)Declarations { if ([SKPaymentQueue canMakePayments]) { NSLog(@"parental functions are disabled"); SKProductsRequest *productRequest = [[SKProductsRequest alloc]initWithProductIdentifiers:[NSSet setWithObjects:@"com.tapmobi.careerandsuccess.inapp",nil]]; productRequest.delegate=self; [productRequest start]; [MBProgressHUD showHUDAddedTo:self.view animated:YES]; } else { NSLog(@"parental functions are enabled"); } } - (IBAction)restorePreviousTransaction:(id)sender { [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; } -(void)productsRequest:(SKProductsRequest *)request didReceiveResponse: (SKProductsResponse *)response { SKProduct *validProduct=nil; int count = [response.products count]; NSLog(@"number of prouducts present:%d",count); if(count==0){ [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; return; } validProduct = [response.products objectAtIndex:0]; NSLog(@"the product is :%@",validProduct.localizedTitle); SKPayment *skpayment = [SKPayment paymentWithProduct:validProduct]; [[SKPaymentQueue defaultQueue] addPayment:skpayment]; [[SKPaymentQueue defaultQueue]addTransactionObserver:self]; } -(void)requestDidFinish:(SKRequest *)request { [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; } -(void)request:(SKRequest *)request didFailWithError:(NSError *)error { [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; NSLog(@"Failed to connect with error: %@", [error localizedDescription]); } -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; NSString *message = [[NSString alloc]init]; BOOL bSuccess = NO; for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchasing: NSLog(@"stuff is getting purchased"); break; case SKPaymentTransactionStatePurchased: NSLog(@"purchased properly"); message = @"Thank you."; [[NSUserDefaults standardUserDefaults] setValue:@"Full Version" forKey:PURCHASED_KEY]; [[SKPaymentQueue defaultQueue]finishTransaction:transaction]; bSuccess = YES; break; case SKPaymentTransactionStateRestored: [[SKPaymentQueue defaultQueue]finishTransaction:transaction]; break; case SKPaymentTransactionStateFailed: if (transaction.error.code != SKErrorPaymentCancelled) { NSLog(@"error happened"); message = @"Purchase is not successfull. Try again later"; } [[SKPaymentQueue defaultQueue]finishTransaction:transaction]; break; default: break; } } if (bSuccess){ UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"" message:message delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; [alert show]; } } 

Happy coding.

0
source

Source: https://habr.com/ru/post/1496133/


All Articles