I am going to update my application for iOS 7 and I am using IAP , but xcode 5 give me this this error :
transactionreceipt is deprecated : first deprecated in iOS 7
here is my code :
// saves a record of the transaction by storing the receipt to disk
- (void)recordTransaction:(SKPaymentTransaction *)transaction
{
if ([transaction.payment.productIdentifier isEqualToString:kProductIdentifier])
{
// save the transaction receipt to disk
[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"proUpgradeTransactionReceipt"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
how can I fix this issue ?
//EDITED :
I used my AndreyMan's answer but nothing happened and compiler gave me APP:requestProductData END message
- (void)requestProductData
{
[NSThread sleepForTimeInterval:2];
NSLog(#"IN-APP:requestProductData");
SKProductsRequest *request= [[SKProductsRequest alloc]
initWithProductIdentifiers: [NSSet setWithObject: #"com.compony.product"]];
request.delegate = self;
[request start];
NSLog(#"IN-APP:requestProductData END");
}
and then gives me these messages :
2013-10-08 14:26:21.400 Arta[1138:60b] Purchasing... 2013-10-08 14:26:28.380 Arta[1138:60b] Unknown Reason. 2013-10-08 14:26:28.383
Arta[1138:60b] Purchase faild...
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
NSLog(#"Purchase compelete...");
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
NSLog(#"Purchase faild...");
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
NSLog(#"Restore compelete...");
break;
case SKPaymentTransactionStatePurchasing:
NSLog(#"Purchasing...");
break;
default:
break;
}
}
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
// error!
[self finishTransaction:transaction wasSuccessful:NO];
if (transaction.error.code == SKErrorClientInvalid) {
}
else if (transaction.error.code == SKErrorPaymentInvalid) {
}
else if (transaction.error.code == SKErrorPaymentNotAllowed) {
}
else if (transaction.error.code == SKErrorPaymentCancelled) {
// [self showAlert:#"In-App Purchase" withMessage:#"This device is not allowed to make the payment."];
NSLog(#"User Cancellation.");
}
else {
// SKErrorUnknown
NSLog(#"Unknown Reason.");
}
}
else {
// this is fine, the user just cancelled, so don’t notify
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
Something like:
NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt;
receipt = [NSData dataWithContentsOfURL:receiptUrl];
Related
I am trying to show MBProgressHud to MPMoviePlayerController, for that I am observing notifications for load states of MPMoviePlayer, but somehow Method observing notification never observes notifications for load states other then MPMovieLoadStatePlayable. I show MBProgressHud when video starts streaming but it does not work after it plays and then to pause to download video, Due to this I am unable to engage user while video is loading, If anyone has a better method please mention it or if there is any problem in the following code then let me know.
-(void)movieLoadStateDidChange:(NSNotification*)notification{
MPMoviePlayerController *player = [notification object];
if ((player.loadState & MPMovieLoadStatePlayable) == MPMovieLoadStatePlayable) {
NSLog(#"Load state Playable");
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}else if ((player.loadState & MPMovieLoadStatePlaythroughOK) == MPMovieLoadStatePlaythroughOK){
NSLog(#"Load state Playing");
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}else if ((player.loadState & MPMovieLoadStateStalled) == MPMovieLoadStateStalled){
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
NSLog(#"Load state stalled");
}else if ((player.loadState & MPMovieLoadStateUnknown) == MPMovieLoadStateUnknown){
NSLog(#"Load State unknown");
}
}
Ok .. i got your problem and the problem is that you are not getting notification for load states except MPMovieLoadStatePlayable. so here what you can do is...like below...
write down below notifiations in viewdidload
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerLoadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
After defining it in ViewDidLoad , implement those functions like below....
- (void) moviePlayerPlaybackDidFinish:(NSNotification *)notification
{
//your code....
MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue];
NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey];
NSString *reason = #"Unknown";
switch (finishReason)
{
case MPMovieFinishReasonPlaybackEnded:
reason = #"Playback Ended";
break;
case MPMovieFinishReasonPlaybackError:
reason = #"Playback Error";
break;
case MPMovieFinishReasonUserExited:
reason = #"User Exited";
break;
}
NSLog(#"Finish Reason: %#%#", reason, error ? [#"\n" stringByAppendingString:[error description]] : #"");
}
- (void) moviePlayerPlaybackStateDidChange:(NSNotification *)notification
{
MPMoviePlayerController *moviePlayerController = notification.object;
NSString *playbackState = #"Unknown";
switch (moviePlayerController.playbackState)
{
case MPMoviePlaybackStateStopped:
playbackState = #"Stopped";
break;
case MPMoviePlaybackStatePlaying:
playbackState = #"Playing";
break;
case MPMoviePlaybackStatePaused:
playbackState = #"Paused";
break;
case MPMoviePlaybackStateInterrupted:
playbackState = #"Interrupted";
break;
case MPMoviePlaybackStateSeekingForward:
playbackState = #"Seeking Forward";
break;
case MPMoviePlaybackStateSeekingBackward:
playbackState = #"Seeking Backward";
break;
}
NSLog(#"Playback State: %#", playbackState);
}
- (void) moviePlayerLoadStateDidChange:(NSNotification *)notification
{
MPMoviePlayerController *moviePlayerController = notification.object;
NSMutableString *loadState = [NSMutableString new];
MPMovieLoadState state = moviePlayerController.loadState;
if (state & MPMovieLoadStatePlayable)
[loadState appendString:#" | Playable"];
if (state & MPMovieLoadStatePlaythroughOK)
[loadState appendString:#" | Playthrough OK"];
if (state & MPMovieLoadStateStalled)
[loadState appendString:#" | Stalled"];
NSLog(#"Load State: %#", loadState.length > 0 ? [loadState substringFromIndex:3] : #"N/A");
}
let me know it is working or not!!!
Happy Coding!!!!
I have an app with multiple in-app contents on same page. when i buy one content it shows already purchased even if i don't buy all of them.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"[transactions count] : - %d ", [transactions count]);
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;
}
}
}
you need to create multiple buttons for in App purchase ,then assign them different tags to identify them.
here is sample code:
- (void)purchaseProUpgrade{
SKPayment *payment;
if(btnTag ==0){
payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseProUpgradeProductId1];
}
else if(btnTag ==1){
payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseProUpgradeProductId2];
}
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
// saves a record of the transaction by storing the receipt to disk
-(void)recordTransaction:(SKPaymentTransaction *)transaction{
if(btnTag == 0){
if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseProUpgradeProductId1]){
[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"proUpgradeTransactionReceipt" ];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
else if(btnTag == 1){
if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseProUpgradeProductId2])
{
// save the transaction receipt to disk
[[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:#"proUpgradeTransactionReceipt" ];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
}
-(void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful{
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, #"transaction" , nil];
if (wasSuccessful) {
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionSucceededNotification object:self userInfo:userInfo];
if(btnTag == 0){
//write ur code
}
else if(btnTag == 1){
//write ur code
}
}
else
{
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionFailedNotification object:self userInfo:userInfo];
}
}
this will solve your problem...
please check this best tutorial of Rey Wenderlich here describing full implementation of in-app Purchase with multiple products.
I have implemented an In-App Purchase in my app and I have two questions about it. This is the code I use:
- (IBAction)buyProduct1:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:product1];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (IBAction)restorePurchases:(id)sender {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
break;
case SKPaymentTransactionStatePurchased:
//Can I be 100% sure that this method is only called when the product is bought?
[self product1Bought:transaction];
break;
case SKPaymentTransactionStateRestored:
//What code should I have here to safely restore product1 if the product is bought?
break;
case SKPaymentTransactionStateFailed:
break;
default:
break;
}
}
}
- (void)product1Bought:(SKPaymentTransaction *)transaction {
NSString *string = #"Product1";
[[NSUserDefaults standardUserDefaults] setObject:string forKey:#"Product1"];
NSLog(#"Product1 is bought");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
1.I was just wondering if I can be 100% sure that the productBought method is only called after a successfully purchase and not if anything strange happens and the product wasn't really purchased?
2.How can I check if product1 is purchased earlier so I can call the product1Bought method?
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
if ([queue.transactions count] == 0)
{
UIAlertView *restorealert = [[UIAlertView alloc]
initWithTitle:#"Restore"
message:#"There is no products purchased by you"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[restorealert show];
activityIndicator.hidden = YES;
}
else
{
NSString *productID;
for(SKPaymentTransaction *transaction in queue.transactions)
{
productID = transaction.payment.productIdentifier;
NSLog(#"the product identifier is %#",productID);
}
here after completing your restore you can get the productId which you bought
1) is working fine and the data will be available when user purchases it
2) for the early purchased items you can call this method under SKPaymentTransactionStateRestored:
[self RestorePurchases];
-(void)RestorePurchases{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
I am using Social framework for Facebook & Twitter integration. For this I followed this link
Everything is working fine except when I app receives ACAccountStoreDidChangeNotification , it crashes.
App crashes when
1) I installed app for first time & user don't have Facebook / Twitter account configured in Settings.
2) When I call any method of Facebook / Twitter , it gives me error code 6 as I don't have account configured which is right.
3) I click on HOME button so my app enters background & add account in Settigs.
4) Once I configured account & come back to app , my app launches from background & receives ACAccountStoreDidChangeNotification.
5) In selector of ACAccountStoreDidChangeNotification my account object is nil so my app crashes. So I checked for the nil
Here is my code
#pragma mark - Request Facebook Account Access
-(void)requestFacebookAccountAccess
{
requestType = kNoOpRequest;
// Register for Account Change notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(accountChanged:) name:ACAccountStoreDidChangeNotification object:nil];
self.accountStore = [[ACAccountStore alloc]init];
ACAccountType *FBaccountType= [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
// Set facebook permissions
NSArray *permissions = nil;
// We need to ask for Basic permission first
if (![kUserDefaults boolForKey:FACEBOOK_BASIC_PERMISSION]) {
permissions = #[#"email",#"user_about_me",#"user_location",#"friends_about_me"];
self.isBasicFacebookPermissionsRequested = YES;
}
else {
permissions = #[#"email",#"user_about_me",#"user_location",#"friends_about_me",#"publish_stream"];
}
//use ACAccountStore to help create your dictionary
NSDictionary *dictFacebook = #{
ACFacebookAppIdKey: kFacebookAppIDKey,
ACFacebookPermissionsKey: permissions,
ACFacebookAudienceKey: ACFacebookAudienceFriends
};
[self.accountStore requestAccessToAccountsWithType:FBaccountType options:dictFacebook completion:^(BOOL granted, NSError *error) {
if (granted) {
if (self.isBasicFacebookPermissionsRequested) {
self.isBasicFacebookPermissionsRequested = NO;
[kUserDefaults saveBasicFacebookPermissionsGrantedStatus:YES];
//[self requestFacebookAccountAccess];
}
// User granted permission to Accout
NSArray *accountsArray = [self.accountStore accountsWithAccountType:FBaccountType];
if ([accountsArray count]>0) {
//It will always be the last object with SSO
self.facebookAccount = [accountsArray lastObject];
NSLog(#"In %s::IS MAIN THREAD:- %#",__PRETTY_FUNCTION__,[NSThread isMainThread]?#"YES":#"NO");
if ([self.delegate respondsToSelector:#selector(didFacebookAccountAccessGranted)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didFacebookAccountAccessGranted];
});
}
}
else {
[self showAlertWithTitle:#"Error" andMessage:#"You don't have any Facebook accounts set up yet.Please set a Facebbok account and try again."];
}
}
else {
DLog(#"User denied permission to Accout::Localized Error:- %#",error.localizedDescription);
if([error code]==6) {
[self showAlertWithTitle:#"Error" andMessage:#"Please setup facebook account from Settings"];
if ([self.delegate respondsToSelector:#selector(didFacebookAccountAccessDenied:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didFacebookAccountAccessDenied:error];
});
}
}
else if ([self.delegate respondsToSelector:#selector(didFacebookAccountAccessDenied:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didFacebookAccountAccessDenied:error];
});
}
else {
// User denied permission to Account
[self showAlertWithTitle:#"Error" andMessage:[NSString stringWithFormat:#"User denied permission to Accout::Error:- %#",error.localizedDescription]];
}
}
}];
}
#pragma mark - Get Logged In User Info
-(void)getLoggedInUserInfo
{
NSURL *requestURL = [NSURL URLWithString:#"https://graph.facebook.com/fql"];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"SELECT current_location,uid,name,sex,birthday,email,pic FROM user WHERE uid=me()",#"q", nil];
requestType = kGetLoggedInUserInfoRequest;
[self requestDataUsingURL:requestURL parameters:dict requestMethod:SLRequestMethodGET];
}
#pragma mark - Request Data From Facebook
-(void)requestDataUsingURL:(NSURL*)requestURL parameters:(NSDictionary*)params requestMethod:(SLRequestMethod)reuqestMethod
{
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeFacebook
requestMethod:reuqestMethod
URL:requestURL
parameters:params];
request.account = self.facebookAccount;
[request performRequestWithHandler:^(NSData *data,
NSHTTPURLResponse *response,
NSError *error)
{
NSDictionary *dictData =[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if(!error)
{
if([dictData objectForKey:#"error"]!=nil)
{
DLog(#"Request Data From Facebook Errro:- %#",dictData);
[self attemptRenewCredentials];
if ([self.delegate respondsToSelector:#selector(didGetFacebookError:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didGetFacebookError:dictData];
});
}
}
else {
// Process the response
//NSLog(#"Response Dictionary contains: %#", dictData );
if ([self.delegate respondsToSelector:#selector(didPostToFacebook:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didPostToFacebook:dictData];
});
}
}
}
else{
if ([self.delegate respondsToSelector:#selector(didGetFacebookError:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didGetFacebookError:dictData];
});
}
else {
[self showAlertWithTitle:#"Error" andMessage:#"Some error occured while processing Facebook request"];
}
}
}];
}
-(void)accountChanged:(NSNotification *)notif
{
if (self.facebookAccount!=nil) {
[self attemptRenewCredentials];
}
else {
[self requestFacebookAccountAccess];
}
}
#pragma mark - Attempt to Renew Credentials
-(void)attemptRenewCredentials
{
[self.accountStore renewCredentialsForAccount:(ACAccount *)self.facebookAccount completion:^(ACAccountCredentialRenewResult renewResult, NSError *error){
if(!error)
{
switch (renewResult) {
case ACAccountCredentialRenewResultRenewed:
NSLog(#"Good to go");
[self againRequestFacebook];
break;
case ACAccountCredentialRenewResultRejected:
NSLog(#"User declined permission");
break;
case ACAccountCredentialRenewResultFailed:
NSLog(#"non-user-initiated cancel, you may attempt to retry");
break;
default:
break;
}
}
else{
//handle error gracefully
NSLog(#"error from renew credentials%#",error);
}
}];
}
-(void)againRequestFacebook
{
switch (requestType) {
case kGetLoggedInUserInfoRequest:
[self getLoggedInUserInfo];
break;
case kGetFriendsInfoRequest:
[self getFacebookFriendsInfo];
break;
case kPostToWallRequest:
[self postToFacebookWallInBackgroundForUserId:fbUserIdForPost withParams:paramsToPost];
break;
default:
break;
}
}
#pragma mark - Remove Observer for Notification
-(void)dealloc
{
requestType = kNoOpRequest;
[[NSNotificationCenter defaultCenter] removeObserver:ACAccountStoreDidChangeNotification];
}
But accountChanged method gets called multiple times.
What is wrong in this ? How can I handle this situation ?
Any kind of help is highly appreciated.
Thanks in advance.
I am facing the same problem...so i used a work around by having a BOOL value in NSUserDefaults to execute accountChanged method only once
-(void)accountChanged:(NSNotification *)notif
{
BOOL calledAccountChanged =[[[NSUserDefaults standardUserDefaults] objectForKey:DidAccountChanged] boolValue];
if(!calledAccountChanged)
{
if (self.facebookAccount!=nil) {
[self attemptRenewCredentials];
}
else {
[self requestFacebookAccountAccess];
}
}
}
Then we can change DidAccountChanged value later in the code...
If you have better solution do post it...
I have written the following code.
But when i execute this code, i got log, which shows "no products available".
I am unable to find the reason.
-(void)viewDidLoad {
[super viewDidLoad];
if ([SKPaymentQueue canMakePayments])
{
SKProductsRequest *productsRequest=[[SKProductsRequest alloc]initWithProductIdentifiers:[NSSet setWithObject:#"com.cmp.name.prdt"]];
productsRequest.delegate=self;
[productsRequest start];
}
else {
NSLog(#"Parental Control are enabled");
}
}
-(IBAction)btnpurchase
{
NSString* isPurchased = [[NSUserDefaults standardUserDefaults] stringForKey:#"com.cmp.name.prdt"];
if ([#"purchased" compare:isPurchased]==NSOrderedSame)
{
///do some task
}
else
{
SKPayment * payment=[SKPayment paymentWithProductIdentifier:#"com.cmp.name.prdt"];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
}
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
SKProduct *validProduct=nil;
int count=[response.products count];
if (count>0) {
validProduct=[response.products objectAtIndex:0];
}
else if(!validProduct)
{
NSLog(#"no products available");
}
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaaction in transactions)
{
switch (transaaction.transactionState)
{
case SKPaymentTransactionStatePurchasing:
break;
case SKPaymentTransactionStatePurchased:
[[SKPaymentQueue defaultQueue]finishTransaction:transaaction];
[[NSUserDefaults standardUserDefaults] setObject:#"purchased" forKey:#"com.cmp.name.prdt"];
[[NSUserDefaults standardUserDefaults] synchronize];
break;
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue]finishTransaction:transaaction];
break;
case SKPaymentTransactionStateFailed:
if (transaaction.error.code!=SKErrorPaymentCancelled) {
NSLog(#"Error encountered");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaaction];
break;
default:
break;
}
}
}
Could anyone help me out in this?
You can try out with following piece of code
//PRODUCT REQUEST DELEGATE METHOD
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSLog(#"The product request didReceiveResponse :%#",[response description]);
NSLog(#"The products are :%#",[response.products description]);
NSLog(#"The invalidProductIdentifiers are:%#",[response.invalidProductIdentifiers description]);
NSArray *products=response.products;
for(SKProduct *currentProduct in products){
NSLog(#"THE Product price is :%#",currentProduct.price);
NSLog(#"THE Product description is :%#",currentProduct.localizedDescription);
NSLog(#"THE Product title is :%#",currentProduct.localizedTitle);
NSLog(#"THE Product's product identifier is :%#",currentProduct.productIdentifier);
}
}
THIS WILL LOG THE PRODUCT DETAILS REGISTERED ON iTUNES CONNECT IN CONSOLE WINDOW
Unfortunately, Apple does not respond with any useful information about why your purchases do not show up. I would go through this checklist. In my apps, things didn't work for a few days, then the suddenly started working. Be sure your financial info is correct.