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.
Related
I did everything right, here is the iTunesConnect integrated shopping screenshot and my simple code. Only he tells me "no integrated purchase".
I've done it hundreds of times, but I still do not understand it.
My productID fits well on itunes as in the code. So where's the problem ?
Help me please guys :)
Code :
#import ....
#import ....
#define kTutorialPointProductID #"com.mathrack.SocialFame.CoinsPlus"
..........
..........
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self fetchAvailableProducts];
[self.navigationController.navigationBar setBackgroundImage:[UIImage new]
forBarMetrics:UIBarMetricsDefault]; //UIImageNamed:#"transparent.png"
self.navigationController.navigationBar.shadowImage = [UIImage new];////UIImageNamed:#"transparent.png"
self.navigationController.navigationBar.translucent = YES;
self.navigationController.view.backgroundColor = [UIColor clearColor];
}
-(void)fetchAvailableProducts{
NSSet *productIdentifiers = [NSSet
setWithObjects:kTutorialPointProductID,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:
#"Les achats intégrés sont désactiver sur votre appareil" message:nil delegate:
self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
}
}
- (IBAction)buy50coins:(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:kTutorialPointProductID]) {
NSLog(#"Purchased ");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"Restored ");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self purchaseFailed];
NSLog(#"Purchase failed ");
break;
default:
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
SKProduct *validProduct = nil;
int count = [response.products count];
if (count>0) {
validProducts = response.products;
validProduct = [response.products objectAtIndex:0];
if ([validProduct.productIdentifier
isEqualToString:kTutorialPointProductID]) {
}
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Non disponible"
message:#"Aucun produit à acheter"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil];
[tmp show];
}
}
- (void)purchaseFailed {
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Achat impossible" message:#"Un problème est survenue lors de votre achat, veuillez réessayer ulterieurement" delegate:
self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
}
ItunesConnect picture :
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 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 2 files set up with the InAppPurchase code in there which look like this:
InAppPurchaseSS.h
#import <Foundation/Foundation.h>
#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
//
// InAppPurchaseSS.m
// Pharaonia
//
// Created by iMac on 4/16/13.
// Copyright (c) 2013 Apple. All rights reserved.
//
#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
SKProduct has a priceLocale attribute and price attribute you can use to create the text for your label.
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
I have added InApp Purchasing to my app but when trying to complete a test transaction using a Test User account, the app keeps crashing at the following code saying that Unrecognised Selector sent to Instance.
I have read up about this and feel it may be something to do with the fact I am using Auto Renewal Subscription product.
The code that seems to be related to the crash is this line:
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
I have provided my InAppPurchase code incase anyone can please please help me with this!!
InAppPurchaseSS.h
#import <Foundation/Foundation.h>
#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;
}
#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;
- (void)collectProducts;
#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)collectProducts
{
self.request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:#"my.inappads"]];
self.request.delegate = self;
[self.request start];
}
- (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);
}
}
-(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: #"my.inappads"]) {
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 am using the #TransCancel in the view that the user makes the purchase in, the code is like this:
// Purchase
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(TransactionCancel) name:#"TransCancel" object: nil];
[center addObserver: self selector: #selector(TransactionComplete) name:#"successbuy" object: nil];
NSLog(#"%d",[[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"]);
if([[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"])
btn.hidden=YES;
else
btn.hidden=NO;
Error Log
2014-01-06 00:25:40.694 MyApp[2764:60b] transaction status updated
2014-01-06 00:25:40.695 MyApp[2764:60b] failed transaction called
2014-01-06 00:25:40.696 MyApp[2764:60b] Transaction failed called
2014-01-06 00:25:40.696 MyApp[2764:60b] Transaction error: Cannot connect to iTunes Store
2014-01-06 00:25:40.730 MyApp[2764:60b] -[MyViewController TransactionCancel]: unrecognized selector sent to instance 0x1666a350
MyViewController
#import "UpgradeViewController.h"
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#import "InAppPurchaseSS.h"
#define ProductIdentifier #"<my.inappads>"
#interface UpgradeViewController ()
#property (retain, nonatomic) IBOutlet JSAnimatedImagesView *animatedImagesView;
#property (weak, nonatomic) IBOutlet UIButton *installFullAppButton;
#end
#implementation UpgradeViewController
#synthesize menuBtn, animatedImagesView = _animatedImagesView, scrolly, bannerView, labelPrice;
- (NSString *)publisherIdForAdSdkBannerView:(AdSdkBannerView *)banner {
return #"e0616d4190bff65279ed5c20de1b5653";
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Price
SKProduct *product = [[[InAppPurchaseSS sharedHelper] products] lastObject];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
// [self.installFullAppButton setTitle:formattedString forState:UIControlStateNormal];
labelPrice.text = formattedString;
// Purchase
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(TransactionCancel) name:#"TransactionCancel" object: nil];
[center addObserver: self selector: #selector(TransactionComplete) name:#"successbuy" object: nil];
NSLog(#"%d",[[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"]);
if([[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"])
btn.hidden=YES;
else
btn.hidden=NO;
// Do any additional setup after loading the view.
//UIScrollView
self.scrolly.contentSize = CGSizeMake(320, 600);
//Image Transition
// self.animatedImagesView.delegate = self;
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
self.menuBtn = [UIButton buttonWithType:UIButtonTypeCustom];
menuBtn.frame = CGRectMake(8, 30, 34, 24);
[menuBtn setBackgroundImage:[UIImage imageNamed:#"menuButton.png"] forState:UIControlStateNormal];
[menuBtn addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuBtn];
myWebView.opaque = NO;
myWebView.backgroundColor = [UIColor clearColor];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)revealMenu:(id)sender
{
[self.slidingViewController anchorTopViewTo:ECRight];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.screenName = #"Upgrade";
}
#pragma mark - Memory Management
- (void)viewDidUnload
{
// [self setAnimatedImagesView:nil];
[self setScrolly:nil];
// [self setAnimatedImagesView:nil];
[super viewDidUnload];
[self setBannerView:nil];
[super viewDidUnload];
}
- (void)dealloc
{
bannerView.delegate = nil;
}
-(IBAction)but:(id)sender
{
[[InAppPurchaseSS sharedHelper] buyProductIdentifier:ProductIdentifier];
}
-(IBAction)restore:(id)sender
{
[[InAppPurchaseSS sharedHelper] restoreInAppPurchase];
}
- (IBAction)dismissView:(id)sender {
[self dismissViewControllerAnimated:YES completion:NULL];
}
-(void)TransactionComplete
{
btn.hidden=YES;
}
-(void)TransactionCancel
{
btn.hidden=NO;
}
You have this line:
[center addObserver: self selector: #selector(TransactionCancel) name:#"TransCancel" object: nil];
Which means that self must implement the TransactionCancel method. The error indicates that the method doesn't exist on the MyViewController class.
The solution is to add the TransactionCancel method to the MyViewController class.
I am confused about how to and when to tell the user that they completed the purchase successfully. I got my application rejected during the app review process for this reason:
1. Launch app
2. Tap on learn about the benefits of subscription
3. Tap on Subscribe
4. Tap on Confirm and enter iTunes password
5. No further action occurs
And I am not sure when and how to tell the user they entered their info correctly since that is confirmed on the iTunes server.
I have an IAPHelper class which looks like this:
//
// IAPHelper.m
// BusinessPlan
//
// Created by MacOSLion on 8/12/13.
//
//
// 1
#import "IAPHelper.h"
#import <StoreKit/StoreKit.h>
// 2
//#interface IAPHelper () <SKProductsRequestDelegate>
#interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
#end
#implementation IAPHelper
{
// 3
SKProductsRequest * _productsRequest;
// 4
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);
// SET memory to yes and then use that later.
// Get user data.
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
// First time on the app, so set the user cookie.
[standardUserDefaults setBool:YES forKey:#"subscriber"];
// Saving
[[NSUserDefaults standardUserDefaults] synchronize];
}
else
{
NSLog(#"Not purchased: %#", productIdentifier);
}
}
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
// retrieve the product information from iTunes Connect
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler
{
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
#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;
}
- (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];
}
- (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];
// SET memory to yes and then use that later.
// Get user data.
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
// First time on the app, so set the user cookie.
[standardUserDefaults setBool:YES forKey:#"subscriber"];
// Saving
[[NSUserDefaults standardUserDefaults] synchronize];
// Tell user that things are purchased.
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
// UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Success sending purchase request."
// message:#"Just press OK and wait a few moments while iTunes processes the request." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
//
// [message show];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Could not complete your transaction"
message:#"Please try again. If the error persists, please email support at: alex#problemio.com" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
// Add to top of file
NSString *const IAPHelperProductPurchasedNotification = #"IAPHelperProductPurchasedNotification";
// Add new method
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier
{
//NSLog(#"Provifing content for subsciber: ");
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Subscribed successfully!"
message:#"Now you can ask questions right on the app, and get our monthly business content." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
}
#end
And my class from which I start the transaction process:
#import "SubscriptionController.h"
// 1
#import "RageIAPHelper.h"
#import <StoreKit/StoreKit.h>
// 2
#interface SubscriptionController ()
{
NSArray *_products;
// Add new instance variable to class extension
NSNumberFormatter * _priceFormatter;
}
#end
#implementation SubscriptionController
// 3
- (void)viewDidLoad
{
[super viewDidLoad];
//self.refreshControl = [[UIRefreshControl alloc] init];
//[self.refreshControl addTarget:self action:#selector(reload) forControlEvents:UIControlEventValueChanged];
[self reload];
//[self.refreshControl beginRefreshing];
// Add to end of viewDidLoad
_priceFormatter = [[NSNumberFormatter alloc] init];
[_priceFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[_priceFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
self.view.backgroundColor = [UIColor colorWithWhite:0.859 alpha:1.000];
}
// 4
- (void)reload
{
_products = nil;
//[self.tableView reloadData];
[[RageIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products)
{
if (success)
{
_products = products;
//[self.tableView reloadData];
}
//[self.refreshControl endRefreshing];
}];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// 5
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _products.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"a");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
SKProduct * product = (SKProduct *) _products[indexPath.row];
cell.textLabel.text = product.localizedTitle;
// Add to bottom of tableView:cellForRowAtIndexPath (before return cell)
[_priceFormatter setLocale:product.priceLocale];
cell.detailTextLabel.text = [_priceFormatter stringFromNumber:product.price];
if ([[RageIAPHelper sharedInstance] productPurchased:product.productIdentifier])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.accessoryView = nil;
}
else
{
UIButton *buyButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buyButton.frame = CGRectMake(0, 0, 72, 37);
[buyButton setTitle:#"Buy" forState:UIControlStateNormal];
buyButton.tag = indexPath.row;
[buyButton addTarget:self action:#selector(buyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.accessoryView = buyButton;
}
return cell;
}
//- (IBAction)subscribe:(id)sender
//{
// UIButton *buyButton = (UIButton *)sender;
// SKProduct *product = _products[buyButton.tag];
//
// NSLog(#"Buying %#...", product.productIdentifier);
// [[RageIAPHelper sharedInstance] buyProduct:product];
//}
- (void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productPurchased:) name:IAPHelperProductPurchasedNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)productPurchased:(NSNotification *)notification
{
NSLog(#"PURCHASEDDDDDDDDD");
// NSString * productIdentifier = notification.object;
// [_products enumerateObjectsUsingBlock:^(SKProduct * product, NSUInteger idx, BOOL *stop)
// {
// if ([product.productIdentifier isEqualToString:productIdentifier])
// {
// // TODO:
// // Update how the button appears.
//
//
//// [self.table reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:idx inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
// *stop = YES;
// }
// }];
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Purchased successfully"
message:#":)" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
// PUSH TO CONFIRMATION
}
//- (IBAction)subscribe:(id)sender
//{
//
//}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (IBAction)createSub:(id)sender
{
UIButton *buyButton = (UIButton *)sender;
SKProduct *product = _products[buyButton.tag];
if ( product == nil)
{
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Pulling product data from iTunes..."
message:#"Please try again in a few moments." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
}
else
{
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Success sending purchase request."
message:#"Just press OK and wait a few moments while iTunes processes the request." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
NSLog(#"Buying %#...", product.productIdentifier);
[[RageIAPHelper sharedInstance] buyProduct:product];
}
}
#end
Thank you for your help!
You should have some sort of UI update to tell the user that the payment was successful and the feature is now available/unlocked. Typically, this is done either with an update in your views to correspond to the new content, or a UIAlertView if there are no visual changes made.