My app has been rejected by itunes.
Their reason is:
After we tap the buy button the app does not initiate the password
window to finish the In App Purchase process and only displays a
message "waiting" then nothing happens.
They mean there is purchase dialog,but when tap the buy button,their is no the password window.But it is OK when I test it.
The password window initialization is not under my control, what should I do ?
Here is my code:
-(void)buy:(NSString*)type
{
buyType = type;
//judge whether can buy product
if ([SKPaymentQueue canMakePayments]) {
NSLog(#"can buy");
[self RequestProductData];
}else{
UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:#"Alert"
message:#"You can‘t purchase in app store(不允许应用程序内购买)"
delegate:nil cancelButtonTitle:NSLocalizedString(#"Close(关闭)",nil) otherButtonTitles:nil];
[alerView show];
[alerView release];
}
}
-(bool)CanMakePay
{
return [SKPaymentQueue canMakePayments];
}
-(void)RequestProductData
{
NSSet *nsset = [NSSet setWithObject:buyType];
SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];
request.delegate=self;
[request start];
}
//<SKProductsRequestDelegate>
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSArray *myProduct = response.products;
// populate UI
for(SKProduct *product in myProduct){
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
[request autorelease];
}
//alert error
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Alert",NULL) message:[error localizedDescription]
delegate:nil cancelButtonTitle:NSLocalizedString(#"Close",nil) otherButtonTitles:nil];
[alerView show];
[alerView release];
}
-(void) requestDidFinish:(SKRequest *)request
{
}
-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction{
NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];
[self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];
[transactions release];
}
//<SKPaymentTransactionObserver>
//----listen to the purchase result
//
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased://purchase ok
[self completeTransaction:transaction];
[[transaction payment] quantity];
NSLog(#"%#",[[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding ]);
NSLog(#"transaction id:%#",[transaction transactionIdentifier]);
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
NSLog(#"-----交易失败 --------");
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
case SKPaymentTransactionStatePurchasing:
break;
default:
break;
}
}
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
-(void)recordTransaction:(NSString *)product{
}
//
-(void)provideContent:(NSString *)product{
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction{
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
-(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
}
-(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{
}
Related
I have been receiving from some of my clients that even they do in app purchase, the product is not loaded. But most of my clients do not have this issue. I was instructing them to do restore operation but that didn't help. The thing is they can restore correctly in other device with same Apple id.
There is no specific device or IOS version and App is not crashing. Just it says restored correctly or purchased correctly, but its not doing it.
It makes me crazy because I tried in many devices and it works correctly for me.
I want to share the code I use for that which may help you to understand issue which I can't.
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
SKProduct *validProduct = nil;
NSUInteger count = [response.products count];
if( count > 0 ) {
validProduct = [response.products objectAtIndex:0];
NSLog(#"Products Available!");
[self purchase:validProduct];
}
else if(!validProduct){
NSLog(#"No products available");
[self hideProgressHud];
isProgress = NO;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:LocalizedString(#"Notice") message:LocalizedString(#"Product is not available now.") preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelBtn = [UIAlertAction actionWithTitle:LocalizedString(#"OK") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}];
[alert addAction:cancelBtn];
[self presentViewController:alert animated:true completion:nil];
}
}
- (void)purchase:(SKProduct *)product {
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
NSLog(#"received restored transactions: %lu", (unsigned long)queue.transactions.count);
for(SKPaymentTransaction *transaction in queue.transactions){
if(transaction.transactionState == SKPaymentTransactionStateRestored || transaction.transactionState == SKPaymentTransactionStateRestored){
NSLog(#"Transaction state -> Restored");
NSString *productID = transaction.payment.productIdentifier;
[[InAppPurchaseManager sharedManager] savePurchaseItem:productID];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
[self hideProgressHud];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
[self hideProgressHud];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
static bool isOnce = false;
for(SKPaymentTransaction *transaction in transactions){
if (transaction.transactionState == SKPaymentTransactionStatePurchasing) {
//called when the user is in the process of purchasing, do not add any of your own code here.
NSLog(#"Transaction state -> Purchasing");
}
else if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[self refresh_purchaseBtn];
return;
}
else if (transaction.transactionState == SKPaymentTransactionStateRestored) {
NSLog(#"Transaction state -> Restored");
[self hideProgressHud];
isProgress = NO;
//add the same code as you did from SKPaymentTransactionStatePurchased here
NSString *productID = transaction.payment.productIdentifier;
if ([productID isEqualToString:kPURCHASE_ITEM_TYPE_A] || [productID isEqualToString:kPURCHASE_ITEM_TYPE_B] || [productID isEqualToString:kPURCHASE_ITEM_TYPE_C]) {
[[InAppPurchaseManager sharedManager] savePurchaseItem:productID];
}
else {
NSArray *arr = [Workout MR_findByAttribute:#"productId" withValue:productID inContext:ApplicationDelegate.managedObjectContext];
if (arr.count > 0) {
Workout *workout = (Workout *)[arr firstObject];
workout.state = #"purchased";
[ApplicationDelegate.managedObjectContext MR_saveOnlySelfAndWait];
}
[[InAppPurchaseManager sharedManager] savePurchasedReadyProgram:productID];
}
if (!isOnce) {
// UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:LocalizedString(#"Notice") message:LocalizedString(#"Purchased Item restored succesfully") delegate:self cancelButtonTitle:LocalizedString(#"Ok") otherButtonTitles: nil];
// [alertView show];
isOnce = true;
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[self refresh_purchaseBtn];
}
else if (transaction.transactionState == SKPaymentTransactionStateFailed) {
//called when the transaction does not finish
if(transaction.error.code == SKErrorPaymentCancelled){
NSLog(#"Transaction state -> Cancelled");
//the user cancelled the payment
}
[self hideProgressHud];
isProgress = NO;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
}
I have implemented in-app purchase and it used to work till now. I have added a few new in-app purchases and tested. It always switches to SKPaymentTransactionStateFailed and app crashes at that stage and no error message is given. Here is code for in app-purchase:
-(void)fetchAvailableProducts{
NSSet *productIdentifiers = [NSSet
setWithObjects:#"xxx.template8",nil];
productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
}
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
- (void)purchaseMyProduct:(SKProduct*)product{
if ([self canMakePurchases]) {
SKPayment *payment = [SKPayment paymentWithProduct: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]];
purchaseButton.enabled = NO;
}
#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:#"xxx.template8"]) {
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 ");
//[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
//break;
default:
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
SKProduct *validProduct = nil;
NSInteger count = [response.products count];
if (count>0) {
validProducts = response.products;
validProduct = [response.products objectAtIndex:0];
if ([validProduct.productIdentifier
isEqualToString:#"xxx.template8"]) {
[productTitleLabel setText:[NSString stringWithFormat:
#"Product Title: %#",validProduct.localizedTitle]];
[productDescriptionLabel setText:[NSString stringWithFormat:
#"Product Desc: %#",validProduct.localizedDescription]];
[productPriceLabel setText:[NSString stringWithFormat:
#"Product Price: %#",validProduct.price]];
}
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Not Available"
message:#"No products to purchase"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
[activityIndicatorView stopAnimating];
purchaseButton.hidden = NO;
}
there is a way to check which error occurred on SKPaymentTransaction, you can do:
...
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
...
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
apply this code and tell us what the log says
EDIT:
since the log says that the product identifier is null check in method
- (void)purchaseMyProduct:(SKProduct*)product{
NSLog(#"product id = %#",product.productIdentifier);
i am really confused, I dont know how can add In-app purchase to my existing app. When in 'Capabilities' i choose on In-app purchase it show me only error add the in app purchase entitlement to your app id. I added app id in dev center and in-app purchase in itunesconnect. My Bundle ID is sk.freetech.zatracenacestina.intro and in-app purchase i set up to sk.freetech.zatracenacestina.intro.package but I dont know where can I fill it?
Thank you
you can follow these tutorial
1. http://www.raywenderlich.com/21081/introduction-to-in-app-purchases-in-ios-6-tutorial
2. http://www.tutorialspoint.com/ios/ios_in_app_purchase.htm
3. http://code4app.net/ios/In-App-Purchase/4fc85be56803fa4a49000000
Create a class name it IAPHelper subclass NSObject
#import <StoreKit/StoreKit.h>
UIKIT_EXTERN NSString *const IAPHelperProductPurchasedNotification;
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
#protocol inAppPurchaseDelegate <NSObject>
#optional
-(void)transactionSucsess:(NSString *)transactionId;
-(void)transactionOnRestore:(NSString *)transactionId;
-(void)transactionOnFail;
#end
#interface IAPHelper : NSObject
#property(nonatomic,readwrite)id<inAppPurchaseDelegate> delegate;
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers;
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler;
- (void)buyProduct:(SKProduct *)product;
- (BOOL)productPurchased:(NSString *)productIdentifier;
- (void)restoreCompletedTransactions;
#end
in IAPHelper.m
import "IAPHelper.h"
#import <StoreKit/StoreKit.h>
NSString *const IAPHelperProductPurchasedNotification = #"IAPHelperProductPurchasedNotification";
#interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
#end
#implementation IAPHelper {
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
NSSet * _productIdentifiers;
NSMutableSet * _purchasedProductIdentifiers;
}
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = productIdentifiers;
// Check for previously purchased products
_purchasedProductIdentifiers = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[_purchasedProductIdentifiers addObject:productIdentifier];
NSLog(#"Previously purchased: %#", productIdentifier);
} else {
NSLog(#"Not purchased: %#", productIdentifier);
}
}
// Add self as transaction observer
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
_completionHandler = [completionHandler copy];
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
- (BOOL)productPurchased:(NSString *)productIdentifier {
return [_purchasedProductIdentifiers containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Failed to load list of products.");
_productsRequest = nil;
_completionHandler(NO, nil);
_completionHandler = nil;
}
#pragma mark SKPaymentTransactionOBserver
- (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(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if(_delegate)
[_delegate transactionSucsess:transaction.payment.productIdentifier];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if(_delegate)
[_delegate transactionOnRestore:transaction.payment.productIdentifier];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
if(_delegate)
[_delegate transactionOnFail];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)restoreCompletedTransactions {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
#end
Create a class RageIAPHelper subclass of IAPHelper
#import "IAPHelper.h"
#interface RageIAPHelper : IAPHelper
+ (RageIAPHelper *)sharedInstance;
#end
In RageIAPHelper.m
#import "RageIAPHelper.h"
#implementation RageIAPHelper
+ (RageIAPHelper *)sharedInstance {
static dispatch_once_t once;
static RageIAPHelper * sharedInstance;
dispatch_once(&once, ^{
NSSet * productIdentifiers = [NSSet setWithObjects:
#"your identifier",#"your identifier",
nil];
sharedInstance = [[self alloc] initWithProductIdentifiers:productIdentifiers];
});
return sharedInstance;
}
#end
[[RageIAPHelper sharedInstance] setDelegate:(id)self];
[[RageIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
if (success) {
totalProduct_Arr =products;
for (SKProduct *s in totalProduct_Arr) {
NSLog(#"product Identifier %#",[s productIdentifier]);
if ([[s productIdentifier]isEqualToString:FULLVERSION]) {
[self.btnFullVersion setEnabled:YES];
}
else if ([[s productIdentifier]isEqualToString:REMOVE_ADS]) {
[self.btnRemoveAd setEnabled:YES];
}
}
}
else{
UIAlertView *alertFail=[[UIAlertView alloc] initWithTitle:#"Product Not Found" message:#"Fails to load product.." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alertFail show];
}
}];
and to buy product
[[RageIAPHelper sharedInstance] buyProduct:product];
This link will help you. Follow this link step by step.
You can use this code also.
Add the StoreKit.framework, Accounts.framework, MobileCoreServices.framework, QuartzCore.framework, CFNetwork.framework, SystemConfiguration.framework.
In .h file import 'StoreKit.h'
#import
Add these delegate on .h class
Declare a property for SKProductsRequest
#property(nonatomic,strong)SKProductsRequest *request;
Now In .m class copy this code for in-app purchase
-(void)startpurching
{
NSArray *arrPackageproductIdentifier=[NSArray arrayWithObjects:#"Your Product Id",nil];
NSString *strPrductIdentifier=[arrPackageproductIdentifier objectAtIndex:0];
request= [[SKProductsRequest alloc]
initWithProductIdentifiers: [NSSet setWithObject:strPrductIdentifier]];
NSLog(#"%#",request);
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
NSArray *myProduct = response.products;
if ([myProduct count]==0) {
UIAlertView *alt=[[UIAlertView alloc]initWithTitle:nil message:#"Cannot connect !! Please check your internet connection." delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alt show];
} else {
NSLog(#"pro...%#",[[myProduct objectAtIndex:0] productIdentifier]);
SKPayment *newPayment = [SKPayment paymentWithProduct:[myProduct objectAtIndex:0]];
[[SKPaymentQueue defaultQueue] addPayment:newPayment];
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"Transaction....%#",transactions);
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
NSLog(#"%d",transaction.transactionState);
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
}
NSLog(#"=======%#",transactions);
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
NSLog(#"Transaction Completed");
NSUserDefaults *remvAddSttr = [NSUserDefaults standardUserDefaults];
// saving an NSString
NSString *chkRemVal=#"10";
[remvAddSttr setObject:chkRemVal forKey:#"revAdd"];
UIAlertView *alrt=[[UIAlertView alloc]initWithTitle:#"" message:#"Transaction completed" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alrt show];
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
NSLog(#"Transaction Restored");
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled) {
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];
}
Now need to design a Restore button for non-consumable product.
For Restore button clicked copy this code.
//*****************************************************************************
//*****************************************************************************
-(void)restoreBtnClicked
{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
if (error.code == SKErrorPaymentCancelled) {
NSLog(#"error.code");
}
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
// NSMutableArray* purchasableObjects = [[[NSMutableArray alloc] init] autorelease];
for (SKPaymentTransaction *transaction in queue.transactions) {
NSString *productID = transaction.payment.productIdentifier;
NSLog(#"productID...%#",productID);
if ([productID isEqualToString:#"Your Product Id"]) {
UIAlertView *alt12=[[UIAlertView alloc]initWithTitle:nil message:#"Product Restored" delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alt12 show];
}
}
//Block Call to access the products
}
For me this error appears because of having NO registered device in member center.
Here is the case that help for me.
Register device in member center.
Set your development team in "Your target" -> General -> "Team"
After that this error disappear for me.
I know this is really old, but I just had the same issue (I was following a Ray W. tutorial https://www.raywenderlich.com/122144/in-app-purchase-tutorial)...
When I added my signing team (Targets > General > Signing > Team) it was resolved.
This is not mentioned in the tutorial (no slight on Ray W. - they're generally great)
I having problem to get the previus purchases from app store. I can buy products and validate the receipt but when I try to restore them, the list of purchased transactions is empty. :(
Any idea? Something i forgoten or dosen't it work in sandbox?
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray*)transactions
{
//Is only fired when buying and not restoring.
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)restoreCompletedTransactions {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue]restoreCompletedTransactions];
}
-(void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self validateReceiptForTransaction:transaction];
_isPurchased = YES;
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
define kMDPulseSubscriptionProductIdentifier #"Your Product ID here...."
SKPaymentTransactionOBserver:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(#"Prossing.............");
break;
case SKPaymentTransactionStatePurchased:
{
[self completeTransaction:transaction];
NSError* error;
NSDictionary* jsonDict = [NSJSONSerialization
JSONObjectWithData:transaction.transactionReceipt
options:kNilOptions
error:&error];
NSLog(#"JSON Receipt: %#",jsonDict);
[[NSUserDefaults standardUserDefaults] setObject:jsonDict forKey:#"A"];
NSLog(#"Purchase was a Success.....");
}
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
NSLog(#"Purchase cancelled");
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqualToString:kMDPulseSubscriptionProductIdentifier]) {
[self purchaseSubscriptionWithMonths:1];
}
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
NSLog(#"%s","User Cancel.");
}
- (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)restoreCompletedTransactions
{
NSLog(#"Restore Tapped in transaction process");
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
When you call this method for restoring
-(void)restoreCompletedTransactions
{
NSLog(#"Restore Tapped in transaction process");
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
//Then this delegate Function Will be fired
-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
alert = [[UIAlertView alloc] initWithTitle:#"IN APP PURCHASE" message:#"Processing" delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
UIActivityIndicatorView *progress= [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(125, 50, 30, 30)];
progress.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[alert addSubview:progress];
[progress startAnimating];
[alert show];
[progress release];
for (SKPaymentTransaction *transaction in queue.transactions)
{
// Use this Commented code to Log and check details about your Products
/**
NSLog(#"%d",queue.transactions.count);
NSMutableArray * purchasedItemIDs = [[NSMutableArray alloc] init];
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *productID = transaction.payment.productIdentifier;
[purchasedItemIDs addObject:productID];
NSLog(#"%#",purchasedItemIDs);
}*/
if( queue.transactions.count == 0 ) {
// User hav't purchased yet.
NSLog(#"User hav't purchased yet.");
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Restore" message:#"You have not purchased yet"
delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:Nil, nil];
[alertView show];
[alertView release];
}else {
// Upgrade to Full version.
NSLog(#"Upgrade to Full version.");
// Do stuffs here
//finally finish transaction
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
}
[alert dismissWithClickedButtonIndex:0 animated:YES];
}
Hope this helps!!
I'm implementing the in-app purchase. And I got the problem while executing the delegate methods of store kit.
Here is my code as following:
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Received products results...");
NSLog(#"response.products.......%#",response.products);
self.products = response.products;
self.request = nil;
proUpgradeProduct = [_products count] == 1 ? [[_products objectAtIndex:0] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
}
NSLog(#"products.....%#",_products);
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:_products];
[self buyProductIdentifier:proUpgradeProduct.productIdentifier];
}
- (void)buyProductIdentifier:(NSString *)productIdentifier {
if ([SKPaymentQueue canMakePayments])
{
NSLog(#"Buying %#...", productIdentifier);
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"StoreKit" message:#"You are not authorised to purchase from AppStore"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
}
- (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;
}
}
}
Control is not shifting to - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions in (void)buyProductIdentifier:(NSString *)productIdentifier - method.
What should I do?
You should set SKPaymentTransactionObserver of defaultQueue:
[[SKPaymentQueue defaultQueue] addTransactionObserver:manager];
Method paymentQueue:updatedTransactions: will be called only after StoreKit did end working with transaction(s) in your defaultQueue : when user canceled or purchased (or transaction was restored) the product.