EzDevInfo.com

in-app-purchase interview questions

Top in-app-purchase frequently asked interview questions

How do you add an in-app purchase to an iOS application?

How do you add an in-app purchase to an iOS app? What are all the details and is there any sample code? (Question and Answer style, answered by me below)


Source: (StackOverflow)

Clearing purchases from IOS in-app purchase sandbox for a test user

Does anyone have any ideas on how to reset and/or clear the IOS in-app purchase sandbox? I have an app that I'm testing with the sandbox, and I'd like to test new purchases without having to create a new test user every time I purchase something. If I don't do this, then I (of course) always get a message that the in-app purchase item has already been purchased when I click on my app's buy button.


Source: (StackOverflow)

Advertisements

A complete solution to LOCALLY validate an in-app receipts and bundle receipts on iOS 7

I have read a lot of docs and code that in theory will validate an in-app and/or bundle receipt.

Given that my knowledge of SSL, certificates, encryption, etc., is nearly zero, all of the explanations I have read, like this promising one, I have found difficult to understand.

They say the explanations are incomplete because every person has to figure out how to do it, or the hackers will have an easy job creating a cracker app that can recognize and identify patterns and patch the application. OK, I agree with this up to a certain point. I think they could explain completely how to do it and put a warning saying "modify this method", "modify this other method", "obfuscate this variable", "change the name of this and that", etc.

Can some good soul out there be kind enough to explain how to LOCALLY validate, bundle receipts and in-app purchase receipts on iOS 7 as I am five years old (ok, make it 3), from top to bottom, clearly?

Thanks!!!


If you have a version working on your apps and your concerns are that hackers will see how you did it, simply change your sensitive methods before publishing here. Obfuscate strings, change the order of lines, change the way you do loops (from using for to block enumeration and vice-versa) and things like that. Obviously, every person that uses the code that may be posted here, has to do the same thing, not to risk being easily hacked.


Source: (StackOverflow)

Application Error : This version of the application is not configured for Market Billing

Since a couple of days it's possible to use in-app-billing in Android apps. Very cool of course. So, I started working on a update for my application to implement this. But now I run into some problems. This is what I did:

  • I first tried the reserved product IDs for testing static in-app billing responses. That works without problems.
  • I uploaded a draft APK to the market (with production signing, no debug signing), that included the com.android.vending.BILLING permission.
  • When I did that I was able to add a 'In-app Products' to my app in the market and set it status to published.
  • I created a test user and reseted my device to make that test user the primary account on my test device.
  • The reserved product id's still work.

Now comes the problem. When I use my own product ID (the one I added before, see above) a dialog is shown with the following text:

"Application Error : This version of the application is not configured for Market Billing. Check the help center for more information."

There is nothing strange I can find in the log. Anyone knows what I'm doing wrong here?


Source: (StackOverflow)

iPhone Store Kit "Cannot connect to iTunes Store"

I am working on adding In-App purchases to my app.

I am able to receive the productsRequest:didReceiveResponse method, and receive the array of products.

My problem arises when I add a SKPayment to the SKPaymentQueue. After I add the product to the queue, in the paymentQueue:updatedTransactions method the transactions always have the state SKPaymentTransactionStateFailed.

I NSLog the "transaction.error" and this is what it returns: Error Domain=SKErrorDomain Code=0 UserInfo=0x165000 "Cannot connect to iTunes Store"

I have logged out of the Store in the Settings app, but after trying to purchase a product in my app it never asks me to log in with my test account. It just fails with the above error.


Source: (StackOverflow)

Implementing In App purchases in Android?

Edit: Android now supports in-app billing!

Original question:

It looks like Android won't natively support in-app purchases for a while, and when it does there might be a huge user base with devices that don't support them.

What's the best way to implement iPhone-like (additional content or services) in-app purchases in Android using the Android Market if possible?

The solution should consider in particular:

  • For all kinds of in-app purchases: Android Market's 24-hour cancellation policy
  • For consumables/non-consumables: storage of additional content (ie: use precious application memory to avoid piracy, or use SD card to avoid bloating application memory)

Thanks!


Source: (StackOverflow)

iOS 5 does not allow to store downloaded data in Documents directory?

I have made an application for my client by keeping target iOS as 4.
But since the application still not submitted to Apple store, my client is planning to upgrade it for iOS 5.0.

For this I read the guideline from Apple and found that "Only user-generated data or that cannot otherwise be recreated by your application, should be stored in the /Documents directory and rest should be stored to /Library/Caches directory"

In my application, I am using server model of in-app purchase for non-consumable product. For this I am storing all my downloaded data (which are basically books or magazines) to Documents directory. The Database is also present in the same directory which contains the details about the downloaded products.

My question is,
1. Should I have to change my code to store the downloaded data to Library/Caches directory instead of to the Documents directory?
2. Where should my database file be placed (to Documents or Caches)?

If I put it products in the Caches then I have to change the logic of retrieval also, since it is considered that if record is present in database, there is no need change the existence of the file and it directly opens it when user clicks on the magazine.

Kindly guide me on this issue.
Thanks in advance.

UPDATED:
I am updating this for those who are still not sure about this problem.
Using the guideline of accepted answer, I have implemented this in 2 of my applications and submitted them to Apple Store. Both were approved in review.
This may promote that the solution suggested in the accepted answer is correct.


Source: (StackOverflow)

Unable to change the Price tier in the In app Purchase

I'm unable to change the price of an in-app purchase. I added initially for testing purposes, but now that I'm submitting the app, I'd like to change it. Unfortunately, it doesn't seem to accept the change. It is showing me the same price tier not the updated one.what should i do ?


Source: (StackOverflow)

iOS recurring subscription policy for service, not content

Apologies in advance for a policy, rather than a programming question, but given the paucity of information available online I hope I can be forgiven for asking it here.

I would like to use the new recurring subscriptions from Apple in an iOS app. I have coded payments before and have no problems there, however nowhere can I find guidance on what is allowed under the new subscription type. The implication 'seems' to be that there is no special guidance, however all the discussions I can find are talking about 'content' providers rather than service providers.

I would like to use the recurring subscription for a service that people subscribe to. I am not offering any content per se.

Using the old non-renewing subscription type (that is really so broken it isn't worth using) I'm 99% sure the app would be accepted, but all the talk of content providers has me worried that Apple really don't want SAAS providers to use the recurring subscription model and want to restrict it to publishers of content.

Does anyone have experience with using the new payment model for software as a service?

I'd love to get some better idea as to whether it's viable or not before we build a whole payment solution around the concept!


Source: (StackOverflow)

In App Purchase Crashes on [[SKPaymentQueue defaultQueue] addPayment:payment]

My In-App-Purchases work. I present a ModalView with a "Buy" UIButton. You click the button and the In App Purchase goes through the process. You can even do it several times in a row.

The problem occurs if you open the Modal View, then close the Modal View (using a UITabBarButtonItem), then reopen the Modal View and tap the "Buy" button. The app crashes and I get an NSZombie that reads

* -[InAppPurchaseManager respondsToSelector:]: message sent to deallocated instance 0x1c7ad0

The NSZombie points to line 160 in the .m file. I have marked it with comments.

I got the original code from this page: http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/

I have been struggling with this for many days now... any help would be awesome.

Here is the .h

//
//  InAppPurchaseManager.h
//  Copyright 2010 __MyCompanyName__. All rights reserved.


#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>

#define kInAppPurchaseManagerProductsFetchedNotification @"kInAppPurchaseManagerProductsFetchedNotification"
#define kInAppPurchaseManagerTransactionFailedNotification @"kInAppPurchaseManagerTransactionFailedNotification"
#define kInAppPurchaseManagerTransactionSucceededNotification @"kInAppPurchaseManagerTransactionSucceededNotification"

#define kInAppPurchaseCreditProductId @"com.myname.app.iap"

@interface InAppPurchaseManager : UIViewController <SKProductsRequestDelegate, SKPaymentTransactionObserver>
{
    SKProduct *productID;
    SKProductsRequest *productsRequest;

 IBOutlet UIBarButtonItem *closeButton;
 IBOutlet UIButton *buyButton;
 IBOutlet UILabel *testLabel;

}

@property (retain, nonatomic) SKProduct *productID;
@property (retain, nonatomic) SKProductsRequest *productsRequest;

@property (retain, nonatomic) IBOutlet UIBarButtonItem *closeButton;
@property (retain, nonatomic) IBOutlet UIButton *buyButton;
@property (retain, nonatomic) IBOutlet UILabel *testLabel;


// public methods
-(void)loadStore;
-(BOOL)canMakePurchases;
-(void)purchaseCredit;

-(void)requestInAppPurchaseData;
-(void)buyButtonAction:(id)sender;
-(void)closeButtonAction:(id)sender;
-(void)updateButtonStatus:(NSString *)status;

@end

Here is the .m

// InAppPurchaseManager.m

#import "InAppPurchaseManager.h"

@implementation InAppPurchaseManager

@synthesize productID;
@synthesize productsRequest;

@synthesize closeButton;
@synthesize buyButton;
@synthesize testLabel;


- (void)dealloc {

 [productID release];
 //[productsRequest release];

 [closeButton release];
 [buyButton release];
 [testLabel release];

    [super dealloc];
}


- (void)viewDidLoad {
    [super viewDidLoad];

 [closeButton release];
 closeButton = [[UIBarButtonItem alloc] initWithTitle:@"Close" style:UIBarButtonItemStyleBordered target:self action:@selector(closeButtonAction:)];
 self.navigationItem.leftBarButtonItem = closeButton;

 [self loadStore];

 self.navigationItem.title = @"Credits";


}

-(void)closeButtonAction:(id)sender { 
 [self dismissModalViewControllerAnimated:YES];
}


-(void)buyButtonAction:(id)sender {

 if([self canMakePurchases]) {
  [self updateButtonStatus:@"OFF"];

  [self performSelectorOnMainThread:@selector(requestInAppPurchaseData) withObject:nil waitUntilDone:NO];

 } else {
  UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:[NSString stringWithString:@"Your account settings do not allow for In App Purchases."] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
  [alertView show];
  [alertView release];  
 }

}


-(void)updateButtonStatus:(NSString *)status {

 if ([status isEqual:@"OFF"]) {
  closeButton.enabled = NO;
  buyButton.enabled = NO;
  buyButton.titleLabel.textColor = [UIColor grayColor];
 } else {
  closeButton.enabled = YES;
  buyButton.enabled = YES;
  buyButton.titleLabel.textColor = [UIColor blueColor];
 }

}

#pragma mark -
#pragma mark SKProductsRequestDelegate methods


//
// call this method once on startup
//
- (void)loadStore
{

    // restarts any purchases if they were interrupted last time the app was open
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];

}


- (void)requestInAppPurchaseData
{
 NSSet *productIdentifiers = [NSSet setWithObject:kInAppPurchaseCreditProductId];

    productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
    productsRequest.delegate = self;
    [productsRequest start];

    // we will release the request object in the delegate callback
}



- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{

    NSArray *products = response.products;


    productID = [products count] == 1 ? [[products objectAtIndex:0] retain] : nil;
    if (productID)
    {
  /*
   NSLog(@"Product title: %@" , productID.localizedTitle);
   NSLog(@"Product description: %@" , productID.localizedDescription);
   NSLog(@"Product price: %@" , productID.price);
   NSLog(@"Product id: %@" , productID.productIdentifier);
   */

  NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
  NSString *currentCredits = ([standardUserDefaults objectForKey:@"currentCredits"]) ? [standardUserDefaults objectForKey:@"currentCredits"] : @"0";

  testLabel.text = [NSString stringWithFormat:@"%@", currentCredits];
    }

    for (NSString *invalidProductId in response.invalidProductIdentifiers)
    {
        //NSLog(@"Invalid product id: %@" , invalidProductId);
  testLabel.text = @"Try Again Later.";
    }

    // finally release the reqest we alloc/init’ed in requestProUpgradeProductData
    [productsRequest release];

    [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];

 [self performSelectorOnMainThread:@selector(purchaseCredit) withObject:nil waitUntilDone:NO];
}


//
// call this before making a purchase
//
- (BOOL)canMakePurchases
{
    return [SKPaymentQueue canMakePayments];
}

//
// kick off the upgrade transaction
//
- (void)purchaseCredit
{

    SKPayment *payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseCreditProductId];

 // *********************************************************************************************************
 [[SKPaymentQueue defaultQueue] addPayment:payment]; // <--- This is where the NSZombie Appears *************
 // *********************************************************************************************************

}

#pragma -
#pragma Purchase helpers

//
// saves a record of the transaction by storing the receipt to disk
//
- (void)recordTransaction:(SKPaymentTransaction *)transaction
{
 if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseCreditProductId])
    {
        // save the transaction receipt to disk
        [[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:@"InAppPurchaseTransactionReceipt" ];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }

}

//
// enable pro features
//
- (void)provideContent:(NSString *)productId
{
 if ([productId isEqualToString:kInAppPurchaseCreditProductId])
    {        
  // Increment currentCredits
  NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
  NSString *currentCredits = [standardUserDefaults objectForKey:@"currentCredits"];
  int newCreditCount = [currentCredits intValue] + 1;
  [standardUserDefaults setObject:[NSString stringWithFormat:@"%d", newCreditCount] forKey:@"currentCredits"];

  testLabel.text = [NSString stringWithFormat:@"%d", newCreditCount];

    }

}

//
// 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];
    }


 [self updateButtonStatus:@"ON"];

}

//
// called when the transaction was successful
//
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{

 [self updateButtonStatus:@"OFF"];

 [self recordTransaction:transaction];
    [self provideContent:transaction.payment.productIdentifier];
 [self finishTransaction:transaction wasSuccessful:YES];

}

//
// 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];
}

//
// called when a transaction has failed
//
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{

    if (transaction.error.code != SKErrorPaymentCancelled)
    {
   // error!
        [self finishTransaction:transaction wasSuccessful:NO];
    }
    else
    {
   // this is fine, the user just cancelled, so don’t notify
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }

 [self updateButtonStatus:@"ON"];

}

#pragma mark -
#pragma mark SKPaymentTransactionObserver methods

//
// called when the transaction status is updated
//
- (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];
                break;
            default:
                break;
        }
    }
}


@end

Source: (StackOverflow)

iPhone In-App Purchase Store Kit error -1003 "Cannot connect to iTunes Store"

I've been working on adding in-app purchases and was able to create and test in-app purchases using Store Kit (yay!). During testing, I exercised my app in a way which caused the app to crash mid purchase (so I guess the normal cycle of receiving paymentQueue:updatedTransactions and calling finishTransaction was interrupted).

Now I am unable to successfully complete any transactions and instead am getting only transactions with transactionState SKPaymentTransactionStateFailed when paymentQueue:updatedTransactions is called.

The transaction.error.code is -1003 and the transaction.error.localizedDescription is "Cannot connect to iTunes Store"!

I have tried removing all products from iTunesConnect, and rebuilt them using different identifiers but that did not help. I have also tried using the App Store app to really connect to the real App Store and download some apps so I do have connectivity. Finally, I have visited the Settings:Store app to make sure I am signed out of my normal app store account.


Source: (StackOverflow)

iPhone in-app purchase screen shot

When posting an in-app purchase with Apple they ask for a screenshot to be included before going to review.

What kind of screenshots do they want?

My in-app purchase unlocks some of the lite version, I'm not sure what Apple wants to see here.


Source: (StackOverflow)

Testing in-app billing: "The publisher cannot purchase this item"

My app seems ready to get a 'real life' test for an in-app purchase procedure on my device. However, I receive an "The publisher cannot purchase this item" error message in Play Store. Now, how am I supposed to test this? I don't want to lose my phone's configuration by reinstalling it with a dummy account just for testing. In the Developer Console under "Settings"-"License testing", I have added my email address under "GMail accounts with testing access", but this doesn't change anything... maybe I missed some simple way, but right now it feels very confusing!


Source: (StackOverflow)

Android In-App Billing failure, saying "You already have a pending order for this item."

I've implemented in-app billing and I'm running into an issue with it. Here is what I see.

  1. Place an order for an item
  2. Wait for a little while for the order to go through
  3. If the purchase is taking a while, the user hits the back button to cancel the purchase
  4. My app gets notified that the purchase was canceled and it confirms this
  5. The user and myself both get an email stating that the purchase was canceled
  6. When the user attempts to purchase the item again, the Market throws an error saying "You already have a pending order for this item."
  7. The response code is "Service Unavailable"
  8. Restore transactions yields no transactions
  9. You can't ever purchase this item with this account

I have found some information about this on the web. http://www.google.com/support/forum/p/Android+Market/thread?tid=375490c831e02ab5&hl=en http://code.google.com/p/marketbilling/issues/detail?id=39

I contacted Google and got an autobot response that they are looking into this.

However, I'm wondering if there is anything I could be doing to cause this.

Oh, and I've also made sure my PendingIntent is good. I have had successful orders.

Update: Here is my stock e-mail to customers that see this. It seems the Android Market has been getting better, as I get fewer and fewer of these now anyhow.

Hello,

This "pending order" error is unfortunately a bug in the Android Market that I cannot control.

To help them raise the priority of this issue, please contact Google at the following web form. http://www.google.com/support/androidmarket/bin/request.py?contact_type=market_phone_tablet

You can tell them to reference bug 5126349, which is their internal tracking number for this.


Source: (StackOverflow)

Verify receipt for in App purchase

I have been playing around with in app purchases for a few days, everything works fine up until the point where I try to validate the receipt with the app store, as i am constantly getting back an invalid status.

I am passing the receipt data to my PHP server then forwarding from there to the app store and once I get a valid response I intend to add the receipt data to my database.

The store kit programming guide and the class references are less than useless for this particular area as they don't really give you any sort of example, I did find one useful article which helped me out a bit but something is still wrong.

Basically I am wondering if someone who has receipt validation working would be willing to share their code as I'm getting nowhere.

Thanks


Source: (StackOverflow)