I am trying to use iap's in my app. These are to buy in game gold and can be used more then once. I code the code from a noob tutorial and it works on there project can someone tell me what I am doing wrong?
#import "IAP.h"
#import "Money.h"
#interface IAP ()
#end
#implementation IAP;
#define kStoredData #"com.AlexApps.TinyTrucks"
+(void)myIAPWithItem:(NSString *)Item{
if ([SKPaymentQueue canMakePayments]) {
NSString *PurchaseAddress = [[NSString alloc] initWithString:[NSString stringWithFormat:#"TTE_%#kGold" , Item]];
//PurchaseAddress is the appId for this in app purchase
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:PurchaseAddress]];
request.delegate = self;
[request start];
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Prohibited"
message:#"Parental Control is enabled, cannot make a purchase!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
}
#pragma mark StoreKit Delegate
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:{
// show wait view here
break;
}
case SKPaymentTransactionStatePurchased:{
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view and unlock feature 2
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Complete"
message:#"You have unlocked Feature 2!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
// apply purchase action - hide lock overlay and
NSLog(#"befvsda");
// do other thing to enable the features
break;
}
case SKPaymentTransactionStateRestored:{
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view here
break;
}
case SKPaymentTransactionStateFailed:{
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(#"Error payment cancelled");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view here
break;
}
default:{
break;
}
}
}
}
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
// remove wait view here
SKProduct *validProduct = nil;
int count = [response.products count];
if (count>0) {
validProduct = [response.products objectAtIndex:0];
SKPayment *payment = [SKPayment paymentWithProductIdentifier:#"com.emirbytes.IAPNoob.01"];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Not Available"
message:#"No products to purchase"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
}
-(void)requestDidFinish:(SKRequest *)request
{
}
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"Failed to connect with error: %#", [error localizedDescription]);
}
#pragma mark AlertView Delegate
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
The program gets to this line:
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:PurchaseAddress]];
request.delegate = self;
[request start];
Then it just stops and doesn't get anything back from apple.
Ensure that your delegate is set, you have request.delegate = self; but you're using self in a class method. I don't see how that's working.
Change +(void)myIAPWithItem:(NSString *)Item to an instance method -(void)myIAPWithItem:(NSString *)Item then instantiate the Money class and call the method. See if that helps.
Are you sure that you created the set up for product ID com.emirbytes.IAPNoob.01 in your iTunesConnect.
If you are created, then check for spelling of your productID com.emirbytes.IAPNoob.01 which is one you created in iTunesConnect
UPDATE 1: No Bundle ID is different, Product ID is different.
Bundle ID is the unique identifier which is used fo App. Product ID is the unique identifier which used to buy the Product List.
Let say com.emirbytes.IAPNoob is bundleID and
com.emirbytes.IAPNoob.swords - is product ID to buy swords
com.emirbytes.IAPNoob.soldiers - is product ID to buy soldiers
To create Product ID, you've to go to iTunesConnect and Create one app, and have to setup Product ID in Manage In-App Purchases.
Related
Hey guys i am frustrated to retrieve a product from iTunesConnect but it always return invalid ID. I fulfill all requirements but not getting any response from it. I followed these steps and my answer is Yes..
.Please give me some solution to solve this problem. Thanks in advance..
1.Have you enabled In-App Purchases for your App ID?
2.Have you checked Cleared for Sale for your product?
3.Have you submitted (and optionally rejected) your application binary?
4.Does your project’s .plist Bundle ID match your App ID?
5.Have you generated and installed a new provisioning profile for the new App ID?
6.Have you configured your project to code sign using this new provisioning profile?
7.Are you building for iPhone OS 3.0 or above?
8.Are you using the full product ID when when making an SKProductRequest? Have you waited several hours since adding your product to iTunes Connect?
9.Are your bank details active on iTunes Connect? (via Mark) Have you tried deleting the app from your device and reinstalling? (via Hector, S3B, Alex O, Joe, and Alberto)
Code:
-(void)fetchAvailableProducts{
NSSet *productSet = [NSSet setWithObjects:#"com.magazineapp.books",nil];
productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:productSet];
productsRequest.delegate = self;
[productsRequest start];
}
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
- (void)purchaseMyProduct:(SKProduct*)product{
if ([self canMakePurchases]) {
SKPayment *payment = [SKPayment paymentWithProductIdentifier: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]];
}
#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:kMySubscriptionFeature]) {
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 ");
break;
default:
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
SKProduct *validProduct = nil;
NSUInteger count = [response.products count];
if (count>0) {
validProducts = response.products;
validProduct = [response.products objectAtIndex:0];
if ([validProduct.productIdentifier
isEqualToString:kMySubscriptionFeature]) {
DLog(#"vaild product");
}
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Not Available"
message:#"No products to purchase"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
[activityIndicatorView stopAnimating];
}
I've been trying to make in-app purchase work. Currently I've an application (Version 1.1) in app store. I want to release another version (V 1.2) in that version I want to integrate in app purchase. I've created a product for that. But when I try to load all product then it shows there is no product.
V1.2 is in the form of ready to upload binary. I've associated in app purchase. I just want to test in App Purchase. I've deleted all the provisioning profile from my devices (MAC + iPhone). Currently I've only one profile installed.
Here is my code:
-(void)fetchAvailableProducts{
NSSet *productIdentifiers = [NSSet
setWithObjects:bundle_identifier,nil];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
}
- (void)requestDidFinish:(SKRequest *)request {
NSLog(#"purchase request finished");
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"%#", [error description]);
}
- (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];
}
}
#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:bundle_identifier]) {
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 ");
break;
default:
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
SKProduct *validProduct = nil;
int count = [response.products count];
NSLog(#"%#",response.products);
if (count>0) {
validProducts = response.products;
validProduct = [response.products objectAtIndex:0];
if ([validProduct.productIdentifier
isEqualToString:bundle_identifier]) {
}
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Not Available"
message:#"No products to purchase"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
}
I wanted to know what is the correct procedure for enabling in-app purchase and what I am doing wrong? And what should be done?
I solved the problem, so I am adding this as answer.
I mixed up with bundle identifier and product identifier. My code was correct, I provided bundle identifier instead of product identifier, this is the reason that I was not getting any products.
NSSet *productIdentifiers = [NSSet
setWithObjects:product_identifier,nil];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
In the above code use product_identifier(s). as I've only one product created so I am using single element to construct the set.
Your code looks correct. Have you confirmed that your in-app product id matches exactly the value in the bundle_id variable?
+ (RageIAPHelper *)sharedInstance {
static dispatch_once_t once;
static RageIAPHelper * sharedInstance;
dispatch_once(&once, ^{
NSSet * productIdentifiers = [NSSet setWithObject:kMDPulseSubscriptionProductIdentifier];
sharedInstance = [[self alloc] initWithProductIdentifiers:productIdentifiers];
});
return sharedInstance;
}
You get the products list,
but you have to test it on real device.
I have in app purchases set up in my app which work fine, however I want to call the 'Product Price' with local currency onto a viewController. I have posted this question before and have got the feedback:
"proUpgradeProduct is a private variable. Make it public by adding is as a method, or a property/sythesize pair. Your problems are not related to in-app purchasing, but rather a lack of basic objective-c knowledge."
I am quite new to this so would appreciate some help. basically, the productPrice variable is in InAppPurchaseSS.m file and I want to call it as a UILabel into a DIFFERENT viewController.
I have these 2 files:
InAppPurchaseSS.h
import
#import "StoreKit/StoreKit.h"
#define kProductPurchasedNotification #"ProductPurchased"
#define kProductPurchaseFailedNotification #"ProductPurchaseFailed"
#define kProductPurchaseCancelledNotification #"ProductPurchaseCancelled"
#interface InAppPurchaseSS : NSObject <SKProductsRequestDelegate,SKPaymentTransactionObserver,UIAlertViewDelegate>
{
SKProductsRequest* productsRequest;
SKProduct *proUpgradeProduct;
UIAlertView* waitingAlert;
BOOL isTransactionOngoing;
IBOutlet UILabel *productPriceLabel;
}
#property (retain) SKProductsRequest* productsRequest;
#property (retain) NSArray * products;
#property (retain) SKProductsRequest *request;
#property (assign) BOOL isTransactionOngoing;
+ (InAppPurchaseSS *) sharedHelper;
-(id)init;
- (void)buyProductIdentifier:(NSString *)productIdentifier;
- (BOOL)canMakePurchases;
-(void)restoreInAppPurchase;
#end
InAppPurchaseSS.m
//
#import "InAppPurchaseSS.h"
#import "Reachability.h"
#implementation InAppPurchaseSS
#synthesize products;
#synthesize request;
#synthesize productsRequest;
#synthesize isTransactionOngoing;
static InAppPurchaseSS * _sharedHelper;
+ (InAppPurchaseSS *) sharedHelper {
if (_sharedHelper != nil) {
return _sharedHelper;
}
_sharedHelper = [[InAppPurchaseSS alloc] init];
return _sharedHelper;
}
-(id)init {
if( (self=[super init]))
{
isTransactionOngoing=NO;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Received products results...");
self.products = response.products;
self.request = nil;
NSLog(#"Number of product:%i : %#",[response.products count],response.products);
NSArray *product = response.products;
proUpgradeProduct = [product count] == 1 ? [[product firstObject] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
[productPriceLabel setText:[NSString stringWithFormat:
#"Product Price: %#",proUpgradeProduct.price]];
}
-(void)restoreInAppPurchase
{
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
[[[UIAlertView alloc] initWithTitle:#"No Internet" message:#"Sorry, no internet connection found" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
return;
}
waitingAlert = [[UIAlertView alloc] initWithTitle:#"Restoring..." message:#"Please Wait...\n\n" delegate:nil cancelButtonTitle:nil otherButtonTitles: nil];
[waitingAlert show];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)buyProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqual: #""]) {
NSLog(#"No IAP Product ID specified");
return;
}
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
[[[UIAlertView alloc] initWithTitle:#"No Internet" message:#"Sorry, no internet connection found" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
return;
}
isTransactionOngoing=YES;
waitingAlert = [[UIAlertView alloc] initWithTitle:#"Purchasing..." message:#"Please Wait...\n\n" delegate:nil cancelButtonTitle:nil otherButtonTitles: nil];
[waitingAlert show];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0) {
SKMutablePayment *payment = [[SKMutablePayment alloc] init];
payment.productIdentifier = productIdentifier;
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
}
-(void)enableFeature
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"PurchaseSuccess"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
NSLog(#"Restore completed transaction failed");
}
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
//
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
{
isTransactionOngoing=NO;
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if (wasSuccessful)
{
[self enableFeature];
[[[UIAlertView alloc] initWithTitle:#"Congratulations!!" message:#"You have succesfully Purchases." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
[[NSNotificationCenter defaultCenter] postNotificationName:#"successbuy" object:self];
}
else
{
[[[UIAlertView alloc] initWithTitle:#"Error!" message:transaction.error.localizedDescription delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"succesfull transaction");
[self finishTransaction:transaction wasSuccessful:YES];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"transaction is restored");
[self finishTransaction:transaction wasSuccessful:YES];
}
// called when a transaction has failed
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
isTransactionOngoing=NO;
NSLog(#"failed transaction called");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction failed called");
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
[self finishTransaction:transaction wasSuccessful:NO];
}
else
{
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
NSLog(#"user cancel transaction");
// this is fine, the user just cancelled, so don’t notify
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
#pragma mark -
#pragma mark SKPaymentTransactionObserver methods
// called when the transaction status is updated
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"transaction status updated");
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;
}
}
}
- (void) dealloc
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[super dealloc];
}
#end
I have another viewController set up where I want to call the Local Product Price with UILabel. Let's assume this is called SecondViewController. I know that in InAppPurchaseSS.m I have a field for proUpgradeProduct.price but I need to call this as a UI label with the local currency
It may be something obvious Im overlooking but I'd appreciate some help with this
Thank you
In the method where you load the products each product should have a variable which contains the price as well as the label. Unfortunately, I don't know which one but I think it's the SKProduct. See if it contains any variables.
EDIT:
It's the proProduct.localizedLabel and proProduct.localizedPrice that you are looking for!
EDIT AGAIN:
I looked again and saw that you were more precise about your question. I would recommend you to save these as NSString.
For instance:
in your .h file you create a property.
#interface SomethingController : UIViewController {
//random stuff
}
#property NSString price;
#property NSString title;
#end
Now set the
self.price = proProduct.localizedPrice
and
self.title = proProduct.localizedLabel
somewhere in the .m file.
Now in your other viewcontroller where you want to show these you create an instance somewhere in the viewcontroller.m file.
First you import the SomethingController.h which contains the variables you are looking for.
At the top of your viewcontroller.m file of the viewcontroller you want to display the data in you write: #import SomethingController.h
Then you create an instance of it to get the data.
SomethingController * something = [SomethingController alloc];
Now you can access the data in your other viewcontroller by writing:
something.price
to access the price and
something.title
to access the label.
I use this code to process my in-app purchase:
#pragma mark StoreKit Delegate
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
{
// show wait view here
// statusLabel.text = #"Processing...";
}
break;
case SKPaymentTransactionStatePurchased:
{
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view and unlock feature 2
// statusLabel.text = #"Done!";
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Complete"
message:#"You have unlocked Feature 2!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
NSError *error = nil;
[SFHFKeychainUtils storeUsername:#"IAPStoreSave" andPassword:#"whatever" forServiceName:kStoredData updateExisting:YES error:&error];
// apply purchase action - hide lock overlay and
[self.lockImage removeFromSuperview];
// do other thing to enable the features
}
break;
case SKPaymentTransactionStateRestored:
{
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view here
// statusLabel.text = #"";
}
break;
case SKPaymentTransactionStateFailed:
{
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(#"Error payment cancelled");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view here
// statusLabel.text = #"Purchase Error!";
}
break;
default:
{
}
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
// remove wait view here
// statusLabel.text = #"";
SKProduct *validProduct = nil;
int count = [response.products count];
if (count>0) {
validProduct = [response.products objectAtIndex:0];
SKPayment *payment = [SKPayment paymentWithProductIdentifier:#"com.app.ID"];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Not Available"
message:#"No products to purchase"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
}
-(void)requestDidFinish:(SKRequest *)request
{
}
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"Failed to connect with error: %#", [error localizedDescription]);
}
And start it in this way:
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:#"com.app.ID"]];
request.delegate = self;
[request start];
In my itunes connect I set up the in-app with the product ID I have entered, and the status is "ready to submit", and it also says: "Your first In-App Purchase(s) must be submitted with a new app version. Select them from the In-App Purchases section of the Version Details page and then click Ready to Upload Binary." When i start the request, the alert NO PRODUCT AVAILABLE always show up. What am I doing wrong??
Are you sure that bundle ID is the same as the app on Itunes Connect? Second to check is app provisioning profile (if it handles IAP) - I think this could be the reason. Let me know.
I am trying to get this whole in-app purchasing to work, but I have encountered some problems. So I am able to "buy" the features with my test user account, but I want to test again and again. So I made a delete method that gets rid of the keychain that is generated and quit the app, then build it again and it is back to the "free" state, where I once again try to buy the app, but this time it gives me "you've already purchased this item, tap OK to download it again" so I tap ok, but this time nothing happens and no features get unlock, wtf?
code:
-(void)deleteKeyChain:(id)sender {
NSError *error = nil;
NSLog(#"delete!!!!");
[SFHFKeychainUtils deleteItemForUsername:#"someUser" andServiceName:kStoredData error:&error];
}
-(void)doFeature:(id)sender {
[newPlayer pause];
if ([self IAPItemPurchased]) {
// do the feature 2!
// featureLabel.text = #"Feature: 2";
} else {
// not purchased so show a view to prompt for purchase
askToPurchase = [[UIAlertView alloc]
initWithTitle:#"All Features"
message:#"Tap refresh anytime to read latest 5 emails. To read all emails with no ads and to continue reading in the background, please purchase the full version of this app."
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"OK",#"Later on", nil];
askToPurchase.delegate = self;
[askToPurchase show];
[askToPurchase release];
}
}
#pragma mark StoreKit Delegate
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
// show wait view here
// statusLabel.text = #"Processing...";
break;
case SKPaymentTransactionStatePurchased:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view and unlock feature 2
// statusLabel.text = #"Done!";
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Complete"
message:#"You now have the full version of Emails Aloud!!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[tmp release];
NSError *error = nil;
[SFHFKeychainUtils storeUsername:#"someUser" andPassword:#"pass" forServiceName:kStoredData updateExisting:YES error:&error];
// apply purchase action - hide lock overlay and
// [feature2Btn setBackgroundImage:nil forState:UIControlStateNormal];
// do other thing to enable the features
break;
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view here
// statusLabel.text = #"";
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(#"Error payment cancelled=%d",transaction.error.code);
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// remove wait view here
// statusLabel.text = #"Purchase Error!";
break;
default:
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
// remove wait view here
// statusLabel.text = #"";
SKProduct *validProduct = nil;
int count = [response.products count];
if (count>0) {
validProduct = [response.products objectAtIndex:0];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
SKPayment *payment = [SKPayment paymentWithProductIdentifier:#"com.myapp.shit"];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Not Available"
message:#"No products to purchase"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[tmp release];
}
}
-(void)requestDidFinish:(SKRequest *)request
{
[request release];
}
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"Failed to connect with error: %#", [error localizedDescription]);
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (alertView==askToPurchase) {
if (buttonIndex==0) {
// user tapped YES, but we need to check if IAP is enabled or not.
if ([SKPaymentQueue canMakePayments]) {
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:#"com.myapp.shit"]];
request.delegate = self;
[request start];
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Prohibited"
message:#"Parental Control is enabled, cannot make a purchase!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[tmp release];
}
}
}
}
note:The someUser/pass is not a username/password of anybody. It is just a text that I chose to register the users in app purchase into their device.
IF you already purchased the application with the user/password from the iTunes Connect user, you need to look for SKPaymentTransactionStateRestored and not SKPaymentTransactionStatePurchased.
SKPaymentTransactionStatePurchased only happens once per user for a non-consumable product. Try creating many more test users on iTunes Connect.