0
votes

In my app i have in app purchase feature. I have added Restore purchase functionality in the app. I have shown a Restore Purchase Button in the app, so user can restore all his previous purchases on tapping the button.

But i am getting this error from resolution center as Guideline 3.1.1 - Business - Payments - In-App Purchase

The error description says we must include a Restore purchase button and functionality. Which I already included in the app.

My code is working perfectly. I tested with sandbox tester accounts. So if I purchase some items and and after that uninstall the app and again if I reinstall the app and after tapping restore purchase button all the previously purchased items for that sandbox test user unlocks.

Here is my code snippet:

-(void)sendreciptToApple{
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];


// Create the JSON object that describes the request
NSError *error;
NSDictionary *requestContents = @{
                                  @"receipt-data": [receipt base64EncodedStringWithOptions:0]
                                  };
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                      options:0
                                                        error:&error];

if (!requestData) { /* ... Handle error ... */ }

// Create a POST request with the receipt data.
// NSURL *storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"]; //for sandbox

NSURL *storeURL = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"]; // for live


// https://sandbox.itunes.apple.com/verifyReceipt
// https://buy.itunes.apple.com/verifyReceipt

NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:requestData];

// Make a connection to the iTunes Store on a background queue.
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                           if (connectionError) {
                               /* ... Handle error ... */
                           } else {
                               NSError *error;
                               NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                               if (!jsonResponse) { /* ... Handle error ...*/ }
                               [self isAppPreviouslyPurchased:jsonResponse];

                               [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                                   // update the UI here
                                   //   [self fireAllProductPurchaseOnetime];
                                   [self showViewsAfterOrBeforePurchase];
                                   [SVProgressHUD dismiss];
                               }];
                           }
                       }];

    }

I am parsing and saving the JSON response from apple and storing locally..

-(void)isAppPreviouslyPurchased:(NSDictionary *)receipt{
NSLog(@"after receive reciept data %@",receipt);


NSMutableArray *purchasedItemArray=[[NSMutableArray alloc]initWithArray:[receipt valueForKeyPath:@"receipt.in_app"]];

NSLog(@"Already purchased item array<<<<>>>>>> %@",purchasedItemArray);
NSString *valueToSave = @"yes";

if (purchasedItemArray.count>0)
{
    for (int i=0; i<purchasedItemArray.count; i++)
    {
        NSDictionary *productItem=[purchasedItemArray objectAtIndex:i];
        NSString *productID=[productItem valueForKey:@"product_id"];

        if ([productID isEqualToString: @"com.iwonderapp.iWonder.EntireApp"])
        {
            [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"IsAllProductPurchased"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
        if ([productID isEqualToString: @"com.iwonderapp.iWonder.Organs"])
        {
            [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"IsOrgansPurchased"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
        if ([productID isEqualToString: @"com.iwonderapp.iWonder.Endocrines"])
        {
            [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"IsEndocrinesPurchased"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
        if ([productID isEqualToString: @"com.iwonderapp.iWonder.Emotions"])
        {
            [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"IsEmotionsPurchased"];
            [[NSUserDefaults standardUserDefaults] synchronize];

        }
        if ([productID isEqualToString: @"com.iwonderapp.iWonder.SexIntInf"])
        {
            [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"IsIntimacyPurchased"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
        if ([productID isEqualToString: @"com.iwonderapp.iWonder.ConOverCon"])
        {
            [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"IsConflictOverwhelmPurchased"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
        if ([productID isEqualToString: @"com.iwonderapp.iWonder.PowConMan"])
        {
            [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"IsPowerControlPurchased"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
        if ([productID isEqualToString: @"com.iwonderapp.iWonder.CreExpRej"])
        {
            [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"IsCreativityPurchased"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
        if ([productID isEqualToString: @"com.iwonderapp.iWonder.Chakras"])
        {
            [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"IsChakrasPurchased"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }

    }
}



 }
1
Have you told Apple where to find the Restore Purchases button?Marcus Adams

1 Answers

2
votes

This is probably your issue:

// NSURL *storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"]; //for sandbox

NSURL *storeURL = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"]; // for live

When Apple tests your IAP flow it happens in the sandbox environment. You are sending the sandbox receipt to the production URL. This will obviously not work, you'll receive status code 21007. When you detect that status code you know you have to send it to the sandbox URL.

That's how Apple recommends to handle this: always send the receipt to the production URL first, and if you receive status code 21007 send it to the sandbox URL as a fallback.

Useful links