iOS In-App Purchases restore previous purcases safely - iphone

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

Related

Issue with transactionreceipt deprecated in iOS 7 warning

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

Changing in app purchase string identifier into an object (deprecated on ios 5)

i need to change this method of string to an SKProduct object, because its' deprecated on my app (ios 5)
(NOTE: this is implemented on the InAppRageIAPHelper class)
- (void)buyProductIdentifier2:(NSString *)productIdentifier {
srtProduct = [productIdentifier copy];
NSLog(#"Buying %#...", productIdentifier);
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
then, when you hit buy:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(alertView.tag == 1)
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Cancel"])
{
if ([delegate respondsToSelector:#selector(purchaseCancelled)]) {
[delegate purchaseCancelled];
}
}
else if([title isEqualToString:#"Buy"])
{
[self buyProductIdentifier2:srtProduct];
}
}
}
Can someone explain me what to change?
You can use this link. It provide the sample code that have what u want.
http://www.raywenderlich.com/23266/in-app-purchases-in-ios-6-tutorial-consumables-and-receipt-validation
Find this method in IAPHelper.m
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
After quick look into documentation: it looks like you have to perform SKProductsRequest with product identifier to fetch SKProduct, then you can proceed with SKPayment

multiple in-app purchase in one app

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.

How can I get the product identifier from a restored transaction?

I have managed to restore in-app transactions and get the transaction identifier from the original transaction, but how do I identify the product which were bought in the transaction?
Is it possible to get the product identifier for a previously purchased in-app product?
if you mean you want to check the purchased items that already user buy it .. yes you can do like this
- (void) checkPurchasedItems {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
} //You Call This Function
//Then this delegate Function Will be fired
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
purchasedItemIDs = [[NSMutableArray alloc] init];
NSLog(#"received restored transactions: %i", queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions) {
NSString *productID = transaction.payment.productIdentifier;
[purchasedItemIDs addObject:productID];
}
}
SWIFT VERSION:
Once you get the delegate
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue!) {
var productIds = [String]()
for transcation in queue.transactions{
if let productID = transcation.payment?.productIdentifier{
productIds.append(productID)
}
}

in app purchase non consumable product

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.