i set my in app purchase creating two bundle id's .i created a test account and checked my in app purchase..it was working perfectly fine...after a week later when i tested i m getting this error *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 0 beyond bounds for empty array'
*** First throw call stack: and its not returning the products...could u guys help me out.what possibly could be wrong with this
-(void)Declarations
{
if ([SKPaymentQueue canMakePayments]) {
NSLog(#"parental functions are disabled");
SKProductsRequest *productRequest = [[SKProductsRequest alloc]initWithProductIdentifiers:[NSSet setWithObjects:#"com.techzone.9xo.onedollar",#"com.techzone.9xo.twodollar",nil]];
productRequest.delegate=self;
[productRequest start];
}else {
NSLog(#"parental functions are enabled");
}
}
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse: (SKProductsResponse *)response
{
SKProduct *validProduct=nil;
int count = [response.products count];
NSLog(#"number of prouducts present:%d",count);
if(_HintValue)
{
validProduct = [response.products objectAtIndex:0];
}
if (!_HintValue) {
validProduct = [response.products objectAtIndex:1];
}
NSLog(#"the product is :%#",validProduct.localizedTitle);
SKPayment *skpayment = [SKPayment paymentWithProduct:validProduct];
[[SKPaymentQueue defaultQueue] addPayment:skpayment];
[[SKPaymentQueue defaultQueue]addTransactionObserver:self];
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"Failed to connect with error: %#", [error localizedDescription]);
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(#"stuff is getting purchased");
break;
case SKPaymentTransactionStatePurchased:
NSLog(#"purchased properly");
NSInteger currentValue=[Util getIntegerValueForKey:#"hintValue"];
if(_HintValue)
[Util setIntegerValue:10+currentValue forKey:#"hintValue"];
if (!_HintValue)
[Util setIntegerValue:25+currentValue forKey:#"hintValue"];
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(#"error happened");
}
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
default:
break;
}
}
}
The problem is that you are getting an empty list of valid product IDs. You are accessing objectAtIndex:0 of an empty array which creates that error.
if you check response.invalidProductIdentifiers you will find your product IDs there.
Unfortunately that can have many reasons. Have a look at this checklist of reasons that can lead to invalid product IDs.
Related
So, when I trying to fetch some data, RACCommand return this error.
I have a picker for example and when user scroll it, app get data from server and show them, but if user scroll fast, (previous operation in progress) RACCommand get this error:
Error Domain=RACCommandErrorDomain Code=1 "The command is disabled and cannot be executed" UserInfo={RACUnderlyingCommandErrorKey=<RACCommand: 0x174280050>, NSLocalizedDescription=The command is disabled and cannot be executed}
I know, its related with some cancel mechanism, but I tried many examples and not working as well.
Its my piece of code:
#weakify(self);
[[[self.viewModel makeCommand] execute:nil]
subscribeError:^(NSError *error) {
#strongify(self);
[self showAlertWithError:error];
}];
and viewModel:
- (RACCommand*)makeCommand {
if (!_makeCommand) {
_makeCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [self getVehicleMake];
}];
}
return _makeCommand;
}
- (RACSignal*)getVehicleMake {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[self.forumService getForumMakesWithYear:#([self.selectedYear integerValue])
category:self.vehicleCategory]
subscribeNext:^(RACTuple *result) {
self.makes = result.first;
[subscriber sendNext:self.makes];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
return [RACDisposable disposableWithBlock:^{
}];
}];
}
RACCommand doesn't allow concurrent execution by default. When it's executing, it becomes disabled. If you try to execute again, it will send that error.
But you can test for that error—RACCommand has RACCommandErrorDomain and RACCommandErrorNotEnabled constants available.
#weakify(self);
[[[self.viewModel makeCommand] execute:nil]
subscribeError:^(NSError *error) {
#strongify(self);
if ([error.domain isEqual:RACCommandErrorDomain] && error.code == RACCommandErrorNotEnabled) {
return;
}
[self showAlertWithError:error];
}];
I want to use TouchID authenticate my own app.
1.I want user can click 'Enter passcode' to invoke system build-in passcode screen to authenticate,if success then enter my own app.
But I don't how know to make it forward to passcode authenticate view like the following screen in 'case LAErrorUserFallback'
Here is my code :
LAContext *context = [[LAContext alloc] init];
__block NSString *msg;
__block BOOL bAuth;
// show the authentication UI with our reason string
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:NSLocalizedString(#"Unlock APP With FingerPrint", nil) reply:
^(BOOL success, NSError *authenticationError) {
if (success) {
bAuth = YES;
msg =[NSString stringWithFormat:NSLocalizedString(#"EVALUATE_POLICY_SUCCESS", nil)];
dispatch_async(dispatch_get_main_queue(), ^{
[[MYAppDelegate theDelegate] initializeAppAfterKeyVaultUnlocked];
});
NSLog(#"%#",msg);
} else {
bAuth = NO;
switch (authenticationError.code) {
case LAErrorAuthenticationFailed:
msg = [NSString stringWithFormat:NSLocalizedString(#"Authentication Failed", nil)];
// ...
break;
case LAErrorUserCancel:
msg = [NSString stringWithFormat:NSLocalizedString(#"User pressed Cancel button", nil)];
dispatch_async(dispatch_get_main_queue(), ^{
[[MYAppDelegate theDelegate] exitAndLock];
});
break;
case LAErrorUserFallback:
msg = [NSString stringWithFormat:NSLocalizedString(#"User pressed \"Enter Password\"", nil)];
//Here I want to fallback to iOS build-in passcode authenticate view, and get the auth result.
break;
default:
msg = [NSString stringWithFormat:NSLocalizedString(#"Touch ID is not configured", nil)];
// ...
break;
}
NSLog(#"%#",authenticationError.localizedDescription);
}
}];
Now in iOS 9 it is pretty simple actually - you just have to use LAPolicyDeviceOwnerAuthentication instead of LAPolicyDeviceOwnerAuthenticationWithBiometrics
So in your code you just replace this:
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:NSLocalizedString(#"Unlock APP With FingerPrint", nil) reply:
^(BOOL success, NSError *authenticationError) {
With this:
[context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:NSLocalizedString(#"Unlock APP With FingerPrint", nil) reply:
^(BOOL success, NSError *authenticationError) {
Thus, when user fails to authenticate with fingerprint, there'd be "enter passcode" option which would invoke system passcode input screen.
In my understanding, you will have to build the passcode screen yourself if you want to use evaluatePolicy.
If you use keychain, the SecItemCopyMatching function automatically falls back to passcode if fingering fails. Here's a reference on how to get it going (see section 3): https://www.secsign.com/fingerprint-validation-as-an-alternative-to-passcodes/
Didn't try that, but this post claim you can use the system as follow here (This works only with iOS 8 or later).
Or (which is what I did) you can build your passcode entry screen (to support older iOS versions), my controller have a passcode entry view, which will get exposed when the user select use passcode. At that point the call back from evaluatePolicy will return with LAErrorUserFallback, which can be the time to open your custom passcode screen.
something like that:
-(void)maybeShowTouchIDMessage {
if (![SettingsManager sharedManager].isUseTouchID || self.createPassCodeMode) {
self.shieldView.hidden = YES;
return;
}
self.shieldView.hidden = NO;
self.shieldView.alpha = 1.0;
LAContext *context = [[LAContext alloc] init];
NSError *evalError = nil;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&evalError] ) {
__weak SecurityWindowViewController *weakSelf = self;
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:#"Use touch id \n or hit \"Cancel\" to enter passcode"
reply:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
SecurityWindowViewController *strongSelf = weakSelf;
if (success) {
[strongSelf hideWithSuccess:YES];
} else if (error){
NSString *errorMessage;
BOOL showError = NO;
switch (error.code) {
case LAErrorAuthenticationFailed:
errorMessage = #"Sorry couldn't autheticate";
showError = YES;
break;
case LAErrorPasscodeNotSet:
errorMessage = #"Sorry couldn't autheticate";
showError = YES;
break;
case LAErrorTouchIDNotEnrolled:
errorMessage = #"Touch ID has no enrolled fingers";
showError = YES;
break;
default:
showError = NO;
break;
}
[UIView animateWithDuration:0.5 animations:^{
strongSelf.shieldView.alpha = 0.0;
} completion:^(BOOL finished) {
strongSelf.shieldView.hidden = YES;
}];
if (showError) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:errorMessage
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
}
}
});
}];
}
For your case :
case LAErrorUserFallback:
[context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:NSLocalizedString(#"Unlock APP With PassCode", nil) reply: ^(BOOL success, NSError *authenticationError) {
if(success){
NSLog(#"PassCode Login successful");
}else{
NSLog(#"%#",authenticationError);
}
}
For device passcode verification you need to use LAPolicyDeviceOwnerAuthentication instead of LAPolicyDeviceOwnerAuthenticationWithBiometrics. Hope this helps !!
You can add another case and call your passcode screen from that.
Here is my code:
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = strMessage;
objFlockr.pVerificationBlock = objResponse;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
if (!isShow) {
myContext.localizedFallbackTitle = #"";
}
else
{
myContext.localizedFallbackTitle = #"Set Up Passcode";
}
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL succes, NSError *error) {
if (!AppDel.firstAttampt && succes && !isShow)
{
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(1);
}
else if (succes) {
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(0);
NSLog(#"User authenticated");
} else {
switch (error.code) {
case LAErrorAuthenticationFailed:
NSLog(#"Authentication Failed");
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(1);
break;
case LAErrorUserCancel:
NSLog(#"User pressed Cancel button");
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(3);
break;
case LAErrorUserFallback:
NSLog(#"User pressed \"Enter Password\"");
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(4);
break;
default:
[self showMessage:#"Touch ID is not configured" withTitle:#"Error"];
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(2);
NSLog(#"Touch ID is not configured");
break;
}
NSLog(#"Authentication Fails");
}
}];
} else {
NSLog(#"Can not evaluate Touch ID");
[self showMessage:#"Can not evaluate TouchID" withTitle:#"Error"];
}
Replace LAPolicy policy enum value deviceOwnerAuthenticationWithBiometrics with deviceOwnerAuthentication
Note: If user has enable biometric (face id or touch id) authentication, then device will ask first for biometric authentication and if user choose fall back authentication, then only deviceOwnerAuthentication will show passcode screen.
Try this and see (swift 4):
func touchIDAuthentication() {
let context = LAContext()
var error:NSError?
// edit line - deviceOwnerAuthentication
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else {
//showAlertViewIfNoBiometricSensorHasBeenDetected()
return
}
// edit line - deviceOwnerAuthentication
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &errorPointer) {
// edit line - deviceOwnerAuthentication
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason, reply: { (success, error) in
if success {
DispatchQueue.main.async {
print("Authentication was successful")
}
}else {
DispatchQueue.main.async {
//self.displayErrorMessage(error: error as! LAError )
print("Authentication was error")
}
}
})
}else {
// self.showAlertWith(title: "Error", message: (errorPointer?.localizedDescription)!)
}
}
AppStore always returns a response 'invalid identifier'.
I wrote following code, and give product identifier as argument, productId.
- (void)purchase:(NSString *)productId callbackURL:(NSString *)callbackURL {
if (![SKPaymentQueue canMakePayments]) {
DDLogWarn(#"couldn't make purchase");
return;
}
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
NSSet *productIds = [NSSet setWithObject:productId];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIds];
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
if (!response) {
DDLogError(#"Product Response is nil");
return;
}
[response.invalidProductIdentifiers bk_each:^(id obj) {
DDLogWarn(#"%# is a invalid identifier", obj);
}];
[response.products bk_each:^(id obj) {
SKProduct *product = obj;
DDLogDebug(#"%# is a valid product", product.localizedTitle);
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}];
}
However, always receive a response, invalid identifier.
I saw http://troybrant.net/blog/2010/01/invalid-product-ids/ , and check my code, device and iTC and provisioning profiles.
My application's state is waiting for upload, inapp items are yellow signals.
Registered application bundle identifier and Xcode project's are same string.
Why..!? Thanks.
well this time mi problem is, I did Create an in app purchase method, for buy 2 products,is my first time so, I did learn from the tutorials of raywenderlich [1]:http://www.raywenderlich.com/21081/introduction-to-in-app-purchases-in-ios-6-tutorial, [2]:http://xcodenoobies.blogspot.com/2012/04/implementing-inapp-purchase-in-xcode.html and [3]:http://www.techotopia.com/index.php/An_iPhone_iOS_6_In-App_Purchase_Tutorial, the problem is when developing my application, I create the account in the simulator tester and runs perfectly without any error I did test in real device & was run perfectly, but when my application went up to the appstore and approved, I did download the app,I did install it to my iphone and both method not works, not let me buy or restore, not even show me the message dialogs of "use apple id", "create new apple id", "cancel", etc., following the tutorials the code to make it are:
//--- in viewcontroller---//
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray * productIdentifiers = [[NSArray alloc] initWithObjects:INAPP1_ID,INAPP2_ID, nil];
NSMutableArray * inAppPurchases = [InAppPurchase initWithProductIdentifiers:productIdentifiers delegate:self];
NSString * msg = nil;
if (inAppPurchases != nil)
{
NSLog(#"created %d in-app-purchase objects...", inAppPurchases.count);
inAppPurchase_ProductPRIMERO = [inAppPurchases objectAtIndex:0];
[inAppPurchase_ProductPRIMERO setVerbose:FALSE];
inAppPurchase_ProductPRIMERO.progressUploadPurchase = [[MBProgressHUD alloc]initWithView:self.view];
[self.view addSubview: inAppPurchase_ProductPRIMERO.progressUploadPurchase];
inAppPurchase_ProductPRIMERO.progressUploadPurchase.mode = MBProgressHUDModeIndeterminate;
inAppPurchase_ProductPRIMERO.progressUploadPurchase.labelText = #"Buying...";
msg = [NSString stringWithFormat:#"Product One productId: %#", inAppPurchase_ProductPRIMERO.productId];
NSLog(#"%#", msg);
inAppPurchase_ProductSEGUNDO = [inAppPurchases objectAtIndex:1];
[inAppPurchase_ProductSEGUNDO setVerbose:FALSE];
inAppPurchase_ProductSEGUNDO.progressUploadPurchase = [[MBProgressHUD alloc]initWithView:self.view];
[self.view addSubview: inAppPurchase_ProductSEGUNDO.progressUploadPurchase];
inAppPurchase_ProductSEGUNDO.progressUploadPurchase.mode = MBProgressHUDModeIndeterminate;
inAppPurchase_ProductSEGUNDO.progressUploadPurchase.labelText = #"Buying...";
msg = [NSString stringWithFormat:#"Product Two productId %#", inAppPurchase_ProductSEGUNDO.productId];
NSLog(#"%#", msg);
} else {
msg = #"Error creating in-app purchases objects!";
}
NSLog(#"in ViewDidLoad doYouDidBuyPRIMERO %s", doYouDidBuyPRIMERO ? "true" : "false");
NSLog(#"in ViewDidLoad doYouDidBuySEGUNDO %s", doYouDidBuySEGUNDO ? "true" : "false");
}
-(IBAction)BuyPRIMERO:(id)sender
{
alreadyPurchased = [inAppPurchase_ProductPRIMERO alreadyPurchased];
NSLog(#"%c",alreadyPurchased);
if (!alreadyPurchased)
[inAppPurchase_ProductPRIMERO purchaseProduct];
}
-(IBAction)RestoreBuyPRIMERO:(id)sender
{
[inAppPurchase_ProductPRIMERO restorePurchase];
}
-(IBAction)BuySEGUNDO:(id)sender
{
alreadyPurchased = [inAppPurchase_ProductSEGUNDO alreadyPurchased];
NSLog(#"%c",alreadyPurchased);
if (!alreadyPurchased)
[inAppPurchase_ProductSEGUNDO purchaseProduct];
}
-(IBAction)RestoreBuySEGUNDO:(id)sender
{
[inAppPurchase_ProductSEGUNDO restorePurchase];
}
- (void)requestedProduct:(InAppPurchase *)iap identifier:(NSString*)productId name:(NSString*)productName price:(NSString*)productPrice description:(NSString*)productDescription {
NSString * _msg = nil;
if (iap == inAppPurchase_ProductPRIMERO)
{
_msg = [NSString stringWithFormat:#"Product One is valid %# price: %#", productName, [iap getValidProductLocalizedPrice]];
NSLog(#"localized price is %# (id is %#)", [iap getValidProductLocalizedPrice], [iap getValidProductId]);
NSString *PRIMERO =[iap getValidProductLocalizedTitle];
NSLog(#"Title: %#",PRIMERO);
NSString *pricePRIMERO = [iap getValidProductLocalizedPrice];
NSString *titlePRIMERO = [NSString stringWithFormat:#"Buy %# %#",PRIMERO,pricePRIMERO];
PRIMEROFunctionButton.titleLabel.text = titlePRIMERO;
} else if (iap == inAppPurchase_ProductSEGUNDO)
{
_msg = [NSString stringWithFormat:#"Product Two is valid %# price: %#", productName, [iap getValidProductLocalizedPrice]];
NSLog(#"localized price is %# (id is %#)", [iap getValidProductLocalizedPrice], [iap getValidProductId]);
NSString *SEGUNDO =[iap getValidProductLocalizedTitle];
NSLog(#"Title: %#",SEGUNDO);
NSString *priceIcons = [iap getValidProductLocalizedPrice];
NSString *titleIcons = [NSString stringWithFormat:#"Buy %# %#",SEGUNDO,priceSEGUNDO];
SEGUNDOButton.titleLabel.text = titleSEGUNDO
;
} else {
}
}
- (void)successfulPurchase:(InAppPurchase*)iap restored:(bool)isRestore identifier:(NSString*)productId receipt:(NSData*)transactionReceipt
{
NSString * statusMsg = nil;
if (iap == inAppPurchase_ProductPRIMERO)
{
if (isRestore)
{
statusMsg = [NSString stringWithFormat:#"Restored purchase of Product One productId: %#", productId];
UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:#"Purchase Restore Completed" message:statusMsg delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
if ([iap isPurchased])
{
doYouDidBuyPRIMERO = YES;
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"DO_YOU_DID_BUY_PRIMERO"];
NSLog(#"doYouDidBuyPRIMERO %s", doYouDidBuyPRIMERO ? "true" : "false");
[self HideCancelBuyPRIMERO];
}
[inAppPurchase_ProductPRIMERO.progressUploadPurchase hide:YES];
} else {
statusMsg = [NSString stringWithFormat:#"Purchased Product One productId: %#", productId];
UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:#"Purchase Completed" message:statusMsg delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
if ([iap isPurchased])
{
doYouDidBuyPRIMERO = YES;
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"DO_YOU_DID_BUY_PRIMERO"];
NSLog(#"doYouDidBuyPRIMERO %s", doYouDidBuyPRIMERO ? "true" : "false");
[self HideCancelBuyPRIMERO];
}
[inAppPurchase_ProductPRIMERO.progressUploadPurchase hide:YES];
}
} else if (iap == inAppPurchase_ProductSEGUNDO) {
if (isRestore)
{
statusMsg = [NSString stringWithFormat:#"Restored purchase of Product Two productId: %#", productId];
UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:#"Purchase Restore Completed" message:statusMsg delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
if ([iap isPurchased])
{
doYouDidBuySEGUNDO = YES;
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"DO_YOU_DID_BUY_SEGUNDO"];
NSLog(#"doYouDidBuySEGUNDO %s", doYouDidBuySEGUNDO ? "true" : "false");
[self HideCancelBuySEGUNDO];
}
[inAppPurchase_ProductSEGUNDO.progressUploadPurchase hide:YES];
} else {
statusMsg = [NSString stringWithFormat:#"Purchased Product Two productId: %#", productId];
UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:#"Purchase Completed" message:statusMsg delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
if ([iap isPurchased])
{
doYouDidBuySEGUNDO = YES;
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"DO_YOU_DID_BUY_SEGUNDO"];
NSLog(#"doYouDidBuySEGUNDO %s", doYouDidBuySEGUNDO ? "true" : "false");
[self HideCancelBuySEGUNDO];
}
[inAppPurchase_ProductSEGUNDO.progressUploadPurchase hide:YES];
}
} else {
}
}
- (void)failedPurchase:(InAppPurchase*)iap error:(NSInteger)errorCode message:(NSString*)errorMessage {
NSString * statusMsg = nil;
if (iap == inAppPurchase_ProductPRIMERO)
{
statusMsg = [NSString stringWithFormat:#"Failed purchased for Product One!\Error code: %d Error message: %#", errorCode, errorMessage];
NSLog(#"%#",statusMsg);
}else if (iap == inAppPurchase_ProductSEGUNDO) {
statusMsg = [NSString stringWithFormat:#"Failed purchased for Product Two!\nError code: %d Error message: %#", errorCode, errorMessage];
NSLog(#"%#",statusMsg);
} else {
}
}
- (void)incompleteRestore:(InAppPurchase*)iap {
NSString * statusMsg = nil;
if (iap == inAppPurchase_ProductPRIMERO) {
statusMsg = #"Incomplete restore for Product One!";
NSLog(#"%#",statusMsg);
} else if (iap == inAppPurchase_ProductSEGUNDO) {
statusMsg = #"Incomplete restore for Product Two!";
NSLog(#"%#",statusMsg);
} else {
}
}
- (void)failedRestore:(InAppPurchase*)iap error:(NSInteger)errorCode message:(NSString*)errorMessage {
NSString * statusMsg = nil;
if (iap == inAppPurchase_ProductPRIMERO) {
statusMsg = #"Failed restore for Product One";
NSLog(#"%#",statusMsg);
} else if (iap == inAppPurchase_ProductSEGUNDO) {
statusMsg = #"Failed restored for Product Two!";
NSLog(#"%#",statusMsg);
} else {
}
}
// AND IN APP PURCHASE HELPER .m
#import "InAppPurchase.h"
#implementation InAppPurchase
#synthesize delegate;
#synthesize validProduct;
#synthesize productId;
#synthesize progressUploadPurchase;
+ (NSMutableArray *)initWithProductIdentifiers:(NSArray *)productIdentifiers delegate:(id<InAppPurchaseDelegate>)_delegate {
NSMutableArray * iapPurchaseArray = nil;
if (productIdentifiers != nil) {
InAppPurchase * purchase = nil;
NSString * productIdentifier = nil;
iapPurchaseArray = [[NSMutableArray alloc] initWithCapacity:productIdentifiers.count];
for (int i = 0; i < productIdentifiers.count; i++) {
productIdentifier = [productIdentifiers objectAtIndex:i];
purchase = [[InAppPurchase alloc] initWithProductId:productIdentifier delegate:_delegate];
[iapPurchaseArray addObject:purchase];
}
}
return iapPurchaseArray;
}
+ (InAppPurchase *)initWithProductIdentifier:(NSString *)productIdentifier delegate:(id<InAppPurchaseDelegate>)_delegate {
InAppPurchase * purchase = nil;
if (productIdentifier != nil)
purchase = [[InAppPurchase alloc] initWithProductId:productIdentifier delegate:_delegate];
return purchase;
}
+ (InAppPopoverView *)showPurchasePopover:(NSArray *)purchases atPoint:(CGPoint)point inView:(UIView *)view withTitle:(NSString *)title includeRestore:(BOOL)includeRestore delegate:(id<InAppPopoverViewDelegate>)delegate {
InAppPopoverView * inAppPopoverView = nil;
if (purchases != nil && purchases.count > 0) {
NSMutableArray * stringArray = [[NSMutableArray alloc] init];
InAppPurchase * inAppPurchase = nil;
NSString * _productTitle = nil;
NSString * localizedPrice = nil;
BOOL wrongClassObject = FALSE;
BOOL alreadyPurchased = FALSE;
for (int i = 0; i < purchases.count; i++) {
inAppPurchase = [purchases objectAtIndex:i];
if ([inAppPurchase isKindOfClass:InAppPurchase.class])
{
if ([inAppPurchase isValidProduct] == FALSE) {
NSLog(#"Your purchases NSArray contains some non InAppPurchase that has not been validated! Please check!");
_productTitle = [NSString stringWithFormat:#"%# not validated yet!", inAppPurchase.productId];
} else {
_productTitle = [inAppPurchase getValidProductLocalizedTitle];
alreadyPurchased = [inAppPurchase alreadyPurchased];
if (alreadyPurchased) {
localizedPrice = [inAppPurchase getValidProductLocalizedPrice];
if ([InAppPurchase isRunningIPad])
_productTitle = [_productTitle stringByAppendingFormat:#" (already purchased for %#)", localizedPrice];
else
_productTitle = [_productTitle stringByAppendingFormat:#" (%#)", localizedPrice];
} else
_productTitle = [NSString stringWithFormat:#"Buy %#", [inAppPurchase getValidProductLocalizedTitle]];
}
[stringArray addObject:_productTitle];
} else
wrongClassObject = TRUE;
}
if (wrongClassObject)
NSLog(#"Your purchases NSArray contains some non InAppPurchase class objects! Please check!");
else {
if (includeRestore) {
// last add 'Restore Purchase' which is required by Apple AppStore so good idea to add or have available elsewhere
[stringArray addObject:#"Restore Purchases"];
}
inAppPopoverView = [InAppPopoverView showPopoverAtPoint:point inView:view withTitle:title withStringArray:stringArray delegate:delegate];
}
} else {
NSLog(#"nil InAppPurchases array provided!");
}
return inAppPopoverView;
}
+ (BOOL)hasAlreadyPurchased:(NSString *)productId {
BOOL result = FALSE;
if (productId != nil)
result = [[NSUserDefaults standardUserDefaults] boolForKey:productId];
return result;
}
- (id)initWithProductId:(NSString *)_productId delegate:(id<InAppPurchaseDelegate>)_delegate {
if ((self = [super init])) {
self.productId = _productId;
self.delegate = _delegate;
purchased = FALSE;
restored = FALSE;
verbose = FALSE;
NSLog(#"purchased %s", purchased? "true" : "false");
[self requestProduct:self.productId];
purchased = [[NSUserDefaults standardUserDefaults] boolForKey:self.productId];
NSLog(#"purchased %s", purchased? "true" : "false");
if (purchased)
NSLog(#"The product %# is already purchased.", self.productId);
NSLog(#"purchased %s", purchased? "true" : "false");
}
return self;
}
- (void)setVerbose:(BOOL)_verbose {
verbose = _verbose;
}
- (BOOL)canMakePayments {
return [SKPaymentQueue canMakePayments];
}
- (BOOL)alreadyPurchased {
return [self isPurchased];
NSLog(#"purchased %s", purchased? "true alreadyPurchased" : "false alreadyPurchased");
}
- (BOOL)isPurchased
{
return purchased;
NSLog(#"purchased %s", purchased? "true" : "false");
}
- (BOOL)isRestored {
return restored;
}
- (BOOL)isValidProduct {
BOOL result = FALSE;
if (self.validProduct != nil)
result = TRUE;
return result;
}
- (NSString *)getProductId {
return self.productId;
}
- (SKProduct *)getValidProduct {
return self.validProduct;
}
- (NSString *)getValidProductId {
NSString * _id = nil;
if ([self isValidProduct])
_id = [self.validProduct productIdentifier];
return _id;
}
- (NSString *)getValidProductLocalizedDescription {
NSString * description = nil;
if ([self isValidProduct])
description = [self.validProduct localizedDescription];
return description;
}
- (NSString *)getValidProductLocalizedTitle {
NSString * title = nil;
if ([self isValidProduct])
title = [self.validProduct localizedTitle];
return title;
}
- (NSString *)getValidProductLocalizedPrice {
NSString * priceStr = nil;
if ([self isValidProduct]) {
NSNumberFormatter * numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:self.validProduct.priceLocale];
priceStr = [numberFormatter stringFromNumber:self.validProduct.price];
}
return priceStr;
}
- (BOOL)requestProduct:(NSString *)_id {
if (_id != nil)
{
if (verbose)
NSLog(#"InAppPurchase requestProduct: %#", _id);
if ([SKPaymentQueue canMakePayments])
{
SKProductsRequest * prodRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:_id]];
prodRequest.delegate = self;
[prodRequest start];
return YES;
} else {
if (verbose)
NSLog(#"InAppPurchase requestProduct: IAP Disabled");
return NO;
}
} else {
if (verbose)
NSLog(#"InAppPurchase requestProduct: productId = NIL");
return NO;
}
}
- (BOOL)purchaseProduct {
return [self purchaseProduct:self.validProduct];
}
- (BOOL)purchaseProduct:(SKProduct*)requestedProduct
{
NSLog(#"InAppPurchase purchaseProduct: %#", requestedProduct.productIdentifier);
if (requestedProduct != nil)
{
if (verbose)
NSLog(#"InAppPurchase purchaseProduct: %#", requestedProduct.productIdentifier);
if ([SKPaymentQueue canMakePayments])
{
SKPayment *paymentRequest = [SKPayment paymentWithProduct:requestedProduct];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:paymentRequest];
return YES;
} else {
if (verbose)
NSLog(#"InAppPurchase purchaseProduct: IAP Disabled");
return NO;
}
} else {
if (verbose)
NSLog(#"InAppPurchase purchaseProduct: SKProductis nil");
return NO;
}
}
- (BOOL)restorePurchase {
if (verbose)
NSLog(#"InAppPurchase restorePurchase");
if ([SKPaymentQueue canMakePayments])
{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
return YES;
} else {
return NO;
}
}
- (void)clearUserDefaultsSetting {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:self.productId];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
self.validProduct = nil;
int count = [response.products count];
if (count > 0) {
self.validProduct = [response.products objectAtIndex:0];
if (verbose)
NSLog(#"got valid product %# for product id %#", self.validProduct, productId);
}
if (self.validProduct) {
if ([delegate respondsToSelector:#selector(requestedProduct:identifier:name:price:description:)])
[delegate requestedProduct:self identifier:self.validProduct.productIdentifier name:self.validProduct.localizedTitle price:[self.validProduct.price stringValue] description:self.validProduct.localizedDescription];
} else {
if ([delegate respondsToSelector:#selector(requestedProduct:identifier:name:price:description:)])
[delegate requestedProduct:self identifier:nil name:nil price:nil description:nil];
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(#"1");
[self.progressUploadPurchase show:YES];
break;
case SKPaymentTransactionStatePurchased:
purchased = TRUE;
NSLog(#"2");
NSLog(#"purchased %s", purchased? "true" : "false");
purchased product.
if ([delegate respondsToSelector:#selector(successfulPurchase:restored:identifier:receipt:)])
[delegate successfulPurchase:self restored:NO identifier:transaction.payment.productIdentifier receipt:transaction.transactionReceipt];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:transaction.payment.productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
break;
case SKPaymentTransactionStateRestored:
purchased = TRUE;
NSLog(#"3");
NSLog(#"purchased %s", purchased? "true" : "false");
if (!restored && [delegate respondsToSelector:#selector(successfulPurchase:restored:identifier:receipt:)])
[delegate successfulPurchase:self restored:YES identifier:transaction.payment.productIdentifier receipt:transaction.transactionReceipt];
restored = TRUE;
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:transaction.payment.productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"4");
[self.progressUploadPurchase hide:YES];
if (transaction.error.code != SKErrorPaymentCancelled)
{
if ([delegate respondsToSelector:#selector(failedPurchase:error:message:)])
[delegate failedPurchase:self error:transaction.error.code message:transaction.error.localizedDescription];
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
break;
}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions {
if (verbose)
NSLog(#"InAppPurchase removedTransactions");
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
if (verbose)
NSLog(#"InAppurchase paymentQueueRestoreCompletedTransactionsFinished");
if ([queue.transactions count] == 0) {
if (verbose)
NSLog(#"InAppPurchase restore queue.transactions count == 0");
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
if ([delegate respondsToSelector:#selector(incompleteRestore:)])
[delegate incompleteRestore:self];
} else {
if (verbose)
NSLog(#"InAppPurchase restore queue.transactions available");
for (SKPaymentTransaction *transaction in queue.transactions) {
if (verbose)
NSLog(#"InAppPurchase restore queue.transactions - transaction data found");
if (!restored && [delegate respondsToSelector:#selector(successfulPurchase:restored:identifier:receipt:)])
[delegate successfulPurchase:self restored:YES identifier:transaction.payment.productIdentifier receipt:transaction.transactionReceipt];
restored = TRUE;
}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
if (verbose)
NSLog(#"InAppPurchase restoreCompletedTransactionsFailedWithError");
if ([delegate respondsToSelector:#selector(failedRestore:error:message:)])
[delegate failedRestore:self error:error.code message:error.localizedDescription];
}
#pragma mark - Internal Methods & Events
+ (BOOL)isRunningIPad {
if ([[UIDevice currentDevice] respondsToSelector:#selector(userInterfaceIdiom)]) {
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
}
return NO;
}
#end
please, help me guys, if my code is wrong or incomplete, tell me where please!!, i did see another answers from stackoverflow, but, waiting 48 hours, or reset my device, I not find the error, or can not find any explanation to the problem, it seems weird that works the same project before boarding the apsstore and the difference you notice when I download the application if it is the same work project THANKS AGAIN GUYS AND GRETTINGS FROM BOLIVIA ROCK ON!!!
Well guys.., after waiting two weeks ago I dont Know how but works normally, maybe is like tell us in another questions & just we need wait with more patience, any way, best regards from BOLIVIA!! ROCK ON!! XD
I'm trying to implement In App Purchase in my app. The type is Non-Consumable.
The produtct request reuturns fine..with correct title, description and price. But when I click to buy, open the alertView saying "confirm....Environment:Sandbox" when I click in confirm, should appears the apple box requesting the password from user test..but It doesn't appears and the tansaction failed without message error... My app isn't in AppStore yet and the In App Purchase Product in Itunes Connect has the status Ready to Submit It's correct?
Here is My code: (Sorry for longer code, but I mean that its relevant and important for my question)
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *products = response.products;
proUpgradeProduct = [products count] == 1 ? [[products 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);
// lblTitulo.text = proUpgradeProduct.productIdentifier;
// lblDescricao.text = proUpgradeProduct.localizedDescription;
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
// finally release the reqest we alloc/init’ed in requestProUpgradeProductData
[productsRequest release];
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
}
- (void)requestProUpgradeProductData
{
NSSet *productIdentifiers = [NSSet setWithObject:#"versaopro" ];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
// we will release the request object in the delegate callback
}
- (void)loadStore
{
// restarts any purchases if they were interrupted last time the app was open
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// get the product description (defined in early sections)
[self requestProUpgradeProductData];
}
- (void)purchaseProUpgrade
{
SKPayment *payment = [SKPayment paymentWithProduct:proUpgradeProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
-(IBAction) buyClick: (id) sender {
[self purchaseProUpgrade];
}