Search code examples
in-app-purchaseapp-store-connectsubscriptions

Resolving invalid product id issue with in-app purchases?


This is a follow-up question to "In app purchases with MKStoreKit failing: “Problem in iTunes connect configuration for product: xxx"

I am having a similar issue and have tried to do both with and without MKStoreKit and get a similar message.

Check-list

With thanks to: http://troybrant.net/blog/2010/01/invalid-product-ids/

Have you enabled In-App Purchases for your App ID?

YES

Have you checked Cleared for Sale for your product?

YES

Have you submitted (and optionally rejected) your application binary?

YES

Does your project’s .plist Bundle ID match your App ID?

YES

Have you generated and installed a new provisioning profile for the new App ID?

YES

Have you configured your project to code sign using this new provisioning profile?

YES

Are you building for iPhone OS 3.0 or above?

YES. iOS4.2 and up.

Are you using the full product ID when when making an SKProductRequest?

YES. Also just the product ID itself without the reverse domain

Have you waited several hours since adding your product to iTunes Connect?

YES. It says "Waiting for review" and has done for the past 4-5 days

Are your bank details active on iTunes Connect?

NO. It is a client project and am only testing to see if it is working. Do I need bank details to test it?

EDIT: I have changed this now. But I do not see why I need to do this just to test sandboxing.

Have you tried deleting the app from your device and reinstalling?

YES

Is your device jailbroken?

NO


App notation is correct

Some examples I have seen use this notation: com.domain.APP_ID.PRODUCT_TO_BUY

But others use com.domain.PRODUCT_TO_BUY

Which is right?

In my code I have attempted using the full notation and just the product ID itself but still get the same problem of "Invalid product id".

Developer rejected

App Bundle (Release) is on iTunes Connect and I have "developer rejected" the bundle.

Picture: App developer rejected

Waiting for review?

In my in-app purchases I have the 1 product, an auto-renewable subscription. It is cleared for sale; it is however "Waiting for review" and is not green-lit.

Picture:

waiting for review

I created this almost 4-5 days ago and its still flagged as "Waiting for review"??

I am wondering if my app has been "developer rejected" whether this has an impact on "Waiting for review" on an auto-renewable subscription?

I have not changed anything in this item.

According to the various articles I have to wait 24-48 hours before I can use it?

But do I need to wait for this to be accepted or green-lit before I can use it in my testing?

Bank details

I have not filled in any bank details because this is an app for a client -- Do I need to fill in bank details before I can continue testing it?

According to these docs: http://developer.apple.com/library/ios/#technotes/tn2259/_index.html bank details are required?

Do I need to be logged into iTunes on my device under a test account before running the app?

I am wondering if someone can clarify these issues? Maybe its just as simple as waiting for the in-app purchase to be "green lit", or perhaps I've missed a step?

Note: This happens regardless of whether I use MKStoreKit or use the code that follows.

Thanks.

My code (this is the non MKStoreKit version)

#define kMySubscriptionFeature @"uk.co.samplewebsite.myappproject.sub1"

    - (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    self.title = @"Manage Subscriptions";


    if ([SKPaymentQueue canMakePayments])
    {
        // Display a store to the user.

        //[MKStoreManager sharedManager];
        //NSLog(@"purhcasable = %@", [[MKStoreManager sharedManager] purchasableObjectsDescription] );
        [self requestProUpgradeProductData];

    }
    else
    {
        // Warn the user that purchases are disabled.
        NSString *message = @"In-app purchases are disabled. Please review your settings";
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alertView show];
        [alertView release];
    } // end if
}


#pragma mark - StoreKit Delegate

- (void) requestProductData
{
    SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObject:kMySubscriptionFeature]];
    request.delegate = self;
    [request start];
}

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    NSArray *myProduct = [[NSArray alloc] initWithArray:response.products];

    for(SKProduct *item in myProduct)
    {

        NSLog(@"Product title: %@" , item.localizedTitle);
        NSLog(@"Product description: %@" , item.localizedDescription);
        NSLog(@"Product price: %@" , item.price);
        NSLog(@"Product id: %@" , item.productIdentifier);
    }


    for (NSString *invalidProductId in response.invalidProductIdentifiers)
    {
        NSLog(@"Problem in iTunes connect configuration for product: %@" , invalidProductId);
    }


    [myProduct release];

    // populate UI
    [request autorelease];
}

Edit:

As a failsafe, I've added bank details just in case, but I don't see why this would cause an issue.

I've also made sure I uploaded and rejected the app release bundle and not the adhoc one; though I do not know if this makes any difference.


Solution

  • I think I've done it now. I'm going to run some tests just to make sure.

    This is not going to be accepted just yet; I am running some tests with different storekit frameworks.

    My output:

    2011-10-27 15:17:49.297 My Simple App[7376:707] productsRequest
    2011-10-27 15:17:49.298 My Simple App[7376:707] Product title: Simple subscription
    2011-10-27 15:17:49.299 My Simple App[7376:707] Product description: Subscribe and get the latest content to your iPhone or iPod Touch device
    2011-10-27 15:17:49.299 My Simple App[7376:707] Product price: 2.99
    2011-10-27 15:17:49.300 My Simple App[7376:707] Product id: sub1
    

    Here's what I did.

    1. I added my bank details. I still do not think this has anything to do with it though.

    2. Note. The app's in-app purchase is still "Waiting for review" and I got the above output.

    3. I replaced:

      #define kMySubscriptionFeature @"uk.co.somesite.someapp.sub1"

    with:

    #define kMySubscriptionFeature @"sub1"
    

    I am going to run some tests with MKStoreKit and with other frameworks to see if it is okay.

    The code I used is below, edited for security reasons:

    .h file

    //  ManageSubscriptionsVC.h
    //  This doesn't have visual output, just NSLog at the moment
    //  This doesn't use MKStoreKit yet
    
    #import <UIKit/UIKit.h>
    #import "StoreKit/StoreKit.h"
    
    #define kMySubscriptionFeature @"sub1"
    
    /*
     Shared Secret
     
     A shared secret is a unique code that you should use when you make the 
     call to our servers for your In-App Purchase receipts. 
     Without a shared secret, you will not be able to test auto-renewable 
     In-App Purchase subscriptions in the sandbox mode. 
     
     Also note that you will not be able to make them available 
     on the App Store.
     
     Note: Regardless of what app they are associated with, 
     all of your auto-renewable subscriptions will use this 
     same shared secret.
     */
    #define sharedSecret @"PUTSHAREDSECRETHERE"
    
    
    @interface ManageSubscriptionsVC : UIViewController
    <SKProductsRequestDelegate, SKProductsRequestDelegate, SKPaymentTransactionObserver>
    {
        SKProduct *proUpgradeProduct;
        SKProductsRequest *productsRequest;
    }
    
    - (void)requestProUpgradeProductData;
    
    @end
    

    .m file

    //
    //  ManageSubscriptionsVC.m
    
    #import "ManageSubscriptionsVC.h"
    
    @implementation ManageSubscriptionsVC
    
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self) {
            // Custom initialization
        }
        return self;
    }
    
    - (void)didReceiveMemoryWarning
    {
        // Releases the view if it doesn't have a superview.
        [super didReceiveMemoryWarning];
        
        // Release any cached data, images, etc that aren't in use.
    }
    
    #pragma mark - View lifecycle
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view from its nib.
        self.title = @"Manage Subscriptions";
        
        
        if ([SKPaymentQueue canMakePayments])
        {
            // Display a store to the user.
            
            //[MKStoreManager sharedManager];
            //NSLog(@"purhcasable = %@", [[MKStoreManager sharedManager] purchasableObjectsDescription] );
            [self requestProUpgradeProductData];
            
        }
        else
        {
            // Warn the user that purchases are disabled.
            NSString *message = @"In-app purchases are disabled. Please review your settings";
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
            [alertView show];
            [alertView release];
        } // end if
    }
    
    - (void)viewDidUnload
    {
        [super viewDidUnload];
        // Release any retained subviews of the main view.
        // e.g. self.myOutlet = nil;
    }
    
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        // Return YES for supported orientations
        return (interfaceOrientation == UIInterfaceOrientationPortrait);
    }
    
    #pragma mark - StoreKit Delegate
    
    - (void) requestProductData
    {
        NSLog(@"requestProductData");
        SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObject:kMySubscriptionFeature]];
        request.delegate = self;
        [request start];
    }
    
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
    {
        NSLog(@"productsRequest");
        
        NSArray *myProduct = [[NSArray alloc] initWithArray:response.products];
        
        for(SKProduct *item in myProduct)
        {
            
            NSLog(@"Product title: %@" , item.localizedTitle);
            NSLog(@"Product description: %@" , item.localizedDescription);
            NSLog(@"Product price: %@" , item.price);
            NSLog(@"Product id: %@" , item.productIdentifier);
            
            
        }
    
        /*
        for(NSString *invalidProduct in response.invalidProductIdentifiers)
            NSLog(@"Problem in iTunes connect configuration for product: %@", invalidProduct);
        */
        
        for (NSString *invalidProductId in response.invalidProductIdentifiers)
        {
            NSLog(@"Problem in iTunes connect configuration for product: %@" , invalidProductId);
        }
        
        
        [myProduct release];
        
        // populate UI
        [request autorelease];
    }
    
    #pragma mark - PaymentQueue
    
    -(void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions
    {
    }
    
    -(void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
    {
    }
    
    -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
    {
    }
    
    -(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
    {
    }
    
    #pragma mark - Other
    
    
    - (void)requestProUpgradeProductData
    {
        NSSet *productIdentifiers = [NSSet setWithObject:kMySubscriptionFeature];
        productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
        productsRequest.delegate = self;
        [productsRequest start];
        
        // we will release the request object in the delegate callback
    }
    
    
    
    
    
    @end