Vote count:
0
I have an app with in app purchases and I just pushed an update. The IAPs worked in the previous version and work just fine in the sandbox mode, in the new live version, however,when my users click an IAP it asks for their ID, confirms the purchase and says "thank you for your purchase" and charges the user but doesn't actually download the file! The Restore feature also no longer works in the live version (works fine in sandbox mode), the IAPs sort of flicker for a 1/2 second but nothing starts to download.
Has anyone run into an issue like this before? Or have any idea how to fix it? Or how I can debug bug that only shows up in the live version. I'm a relative novice so the more detailed your response the better. - Restore and IAPs don't work in live version - IAPs worked in previous version, I haven't updated the IAPs - IAPS work fine in sandbox mode - I have waited 48 hours for the any issues withs servers to sync up and nothing has changed - The IAPs are hosted by Apple - My Apple ID allows IAPS - All of my contracts are active and up to date
#import "IAPHelper.h"
#import <StoreKit/StoreKit.h>
#import "IAPProduct.h"
#import "VerificationController.h"
#import "IAPProductInfo.h"
#import "IAPProductPurchase.h"
#import "AFNetworking.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#import "NSData+Base64.h"
#import "HMAppDelegate.h"
static NSString *const IAPServerBaseURL =
@"http://ift.tt/1quse2T";
static NSString *const IAPServerProductsURL =
@"/GCC7/IAP/productInfos.plist";
//static NSString *const IAPServerVerifyURL =
//@"/clients/hangman3/"; Needed if I want to verify receipts off my own server
static NSString *const IAPHelperPurchasesPlist =
@"purchases.plist";
@interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
@end
@implementation IAPHelper {
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
BOOL _productsLoaded;
}
- (id)init {
if ((self = [super init])) {
_products = [NSMutableDictionary dictionary];
[self loadPurchases];
[self loadProductsWithCompletionHandler:^(BOOL success,
NSError *error) {
}];
}
return self;
}
- (IAPProduct *)addProductForProductIdentifier:
(NSString *)productIdentifier {
IAPProduct * product = _products[productIdentifier];
if (product == nil) {
product = [[IAPProduct alloc]
initWithProductIdentifier:productIdentifier];
_products[productIdentifier] = product;
}
return product;
}
- (void)addInfo:(IAPProductInfo *)info
forProductIdentifier:(NSString *)productIdentifier {
IAPProduct * product = [self
addProductForProductIdentifier:productIdentifier];
product.info = info;
// NSLog(@"product info : %@",product.info);
}
- (void)requestProductsWithCompletionHandler:
(RequestProductsCompletionHandler)completionHandler {
_completionHandler = [completionHandler copy];
[self loadProductsWithCompletionHandler:^(BOOL success,
NSError *error) {
NSMutableSet * productIdentifiers = [NSMutableSet
setWithCapacity:_products.count];
for (IAPProduct * product in _products.allValues) {
if (product.info) {
product.availableForPurchase = NO;
[productIdentifiers
addObject:product.productIdentifier];
}
}
_productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response {
NSArray *invalidproductIdentifiers = response.invalidProductIdentifiers;
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
// NSLog(@"Invalid product id: %@" , invalidProductId);
}
NSLog(@"Loaded list of products...");
_productsRequest = nil;
// 1
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
IAPProduct * product =
_products[skProduct.productIdentifier];
product.skProduct = skProduct;
product.availableForPurchase = YES;
}
// 2
for (NSString * invalidProductIdentifier in
response.invalidProductIdentifiers) {
IAPProduct * product =
_products[invalidProductIdentifier];
product.availableForPurchase = NO;
// NSLog(@"Invalid product identifier, removing: %@",invalidProductIdentifier);
}
// 3
NSMutableArray * availableProducts = [NSMutableArray array];
for (IAPProduct * product in _products.allValues) {
if (product.availableForPurchase) {
[availableProducts addObject:product];
}
}
_completionHandler(YES, availableProducts);
_completionHandler = nil;
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
// NSLog(@"Failed to load list of products.");
_productsRequest = nil;
// 5
_completionHandler(FALSE, nil);
_completionHandler = nil;
}
- (void)buyProduct:(IAPProduct *)product {
HMAppDelegate *appDel = (HMAppDelegate*)[[UIApplication sharedApplication]delegate];
appDel.plistDeleteProduct = product.productIdentifier;
// NSAssert(product.allowedToPurchase, @"This product isn't allowed to be purchased!");
// NSLog(@"Buying %@...", product.productIdentifier);
product.purchaseInProgress = YES ;
SKPayment * payment = [SKPayment
paymentWithProduct:product.skProduct];
[[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 validateReceiptForTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(@"restoreTransaction...");
[self validateReceiptForTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(@"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(@"Transaction error: %@",
transaction.error.localizedDescription);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"GCC App Error"
message:transaction.error.localizedDescription
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
IAPProduct * product =
_products[transaction.payment.productIdentifier];
[self notifyStatusForProductIdentifier:
transaction.payment.productIdentifier
string:@"Purchase failed."];
product.purchaseInProgress = NO;
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)notifyStatusForProductIdentifier:
(NSString *)productIdentifier string:(NSString *)string {
IAPProduct * product = _products[productIdentifier];
[self notifyStatusForProduct:product string:string];
}
- (void)notifyStatusForProduct:(IAPProduct *)product
string:(NSString *)string {
}
- (void)provideContentForTransaction:
(SKPaymentTransaction *)transaction
productIdentifier:(NSString *)productIdentifier {
IAPProduct * product = _products[productIdentifier];
if (transaction.downloads) {
product.skDownload = transaction.downloads[0];
if (transaction.downloads.count > 1) {
NSLog(@"Unexpected number of downloads!");
}
[[SKPaymentQueue defaultQueue]
startDownloads:transaction.downloads];
} else {
// Put the code from before here
IAPProduct * product = _products[productIdentifier];
if (product.info.consumable) {
[self
purchaseConsumable:product.info.consumableIdentifier
forProductIdentifier:productIdentifier
amount:product.info.consumableAmount];
} else {
NSURL * bundleURL = [[NSBundle mainBundle].resourceURL
URLByAppendingPathComponent:product.info.bundleDir];
[self purchaseNonconsumableAtURL:bundleURL
forProductIdentifier:productIdentifier];
}
[self notifyStatusForProductIdentifier:productIdentifier
string:@"Purchase complete!"];
product.purchaseInProgress = NO;
[[SKPaymentQueue defaultQueue] finishTransaction:
transaction];
}
}
- (void)purchaseConsumable:(NSString *)consumableIdentifier
forProductIdentifier:(NSString *)productIdentifier
amount:(int)consumableAmount {
int previousAmount = [[NSUserDefaults standardUserDefaults]
integerForKey:consumableIdentifier];
int newAmount = previousAmount + consumableAmount;
[[NSUserDefaults standardUserDefaults] setInteger:newAmount
forKey:consumableIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
IAPProductPurchase * previousPurchase = [self
purchaseForProductIdentifier:productIdentifier];
if (previousPurchase) {
previousPurchase.timesPurchased++;
} else {
IAPProductPurchase * purchase = [[IAPProductPurchase alloc]
initWithProductIdentifier:productIdentifier consumable:YES
timesPurchased:1 libraryRelativePath:@""
contentVersion:@""];
[self addPurchase:purchase
forProductIdentifier:productIdentifier];
}
[self savePurchases];
}
- (void)provideContentWithURL:(NSURL *)URL {
}
- (void)purchaseNonconsumableAtURL:(NSURL *)nonLocalURL
forProductIdentifier:(NSString *)productIdentifier {
NSError * error = nil;
BOOL success = FALSE;
BOOL exists = FALSE;
BOOL isDirectory = FALSE;
// 1
NSString * libraryRelativePath =
nonLocalURL.lastPathComponent;
NSString * localPath = [[self libraryPath]
stringByAppendingPathComponent:libraryRelativePath];
NSURL * localURL = [NSURL fileURLWithPath:localPath
isDirectory:YES];
exists = [[NSFileManager defaultManager]
fileExistsAtPath:localPath isDirectory:&isDirectory];
// 2
if (exists) {
BOOL success = [[NSFileManager defaultManager]
removeItemAtURL:localURL error:&error];
if (!success) {
NSLog(@"Couldn't delete directory at %@: %@",
localURL, error.localizedDescription);
}
}
// 3
NSLog(@"Copying directory from %@ to %@", nonLocalURL,
localURL);
success = [[NSFileManager defaultManager]
copyItemAtURL:nonLocalURL toURL:localURL error:&error];
if (!success) {
NSLog(@"Failed to copy directory: %@",
error.localizedDescription);
[self notifyStatusForProductIdentifier:productIdentifier
string:@"Copying failed."];
return;
}
// 1
NSString * contentVersion = @"";
NSString *bundle = [[NSBundle mainBundle] pathForResource:@"ContentInfo" ofType:@"plist"];
// NSLog(@"contents of plist :%@",bundle);
NSURL * contentInfoURL = [localURL
URLByAppendingPathComponent:@"ContentInfo.plist"];
exists = [[NSFileManager defaultManager]
fileExistsAtPath:contentInfoURL.path
isDirectory:&isDirectory];
if (exists) {
// 2
NSDictionary * contentInfo = [NSDictionary
dictionaryWithContentsOfURL:contentInfoURL];
contentVersion = contentInfo[@"ContentVersion"];
NSString * contentsPath = [libraryRelativePath
stringByAppendingPathComponent:@"Contents"];
// 3
NSString * fullContentsPath = [[self libraryPath]
stringByAppendingPathComponent:contentsPath];
if ([[NSFileManager defaultManager]
fileExistsAtPath:fullContentsPath]) {
libraryRelativePath = contentsPath;
localPath = [[self libraryPath]
stringByAppendingPathComponent:libraryRelativePath];
localURL = [NSURL fileURLWithPath:localPath
isDirectory:YES];
}
}
// 4
[self provideContentWithURL:localURL];
// 5
IAPProductPurchase * previousPurchase = [self
purchaseForProductIdentifier:productIdentifier];
if (previousPurchase) {
previousPurchase.timesPurchased++;
// 6
NSString * oldPath = [[self libraryPath]
stringByAppendingPathComponent:
previousPurchase.libraryRelativePath];
// NSLog(@"path : %@",oldPath);
success = [[NSFileManager defaultManager]
removeItemAtPath:oldPath error:&error];
if (!success) {
// NSLog(@"Could not remove old purchase at %@",oldPath);
} else {
// NSLog(@"Removed old purchase at %@", oldPath);
}
// 7
previousPurchase.libraryRelativePath =
libraryRelativePath;
previousPurchase.contentVersion = contentVersion;
} else {
IAPProductPurchase * purchase =
[[IAPProductPurchase alloc]
initWithProductIdentifier:productIdentifier
consumable:NO timesPurchased:1
libraryRelativePath:libraryRelativePath
contentVersion:contentVersion];
[self addPurchase:purchase
forProductIdentifier:productIdentifier];
}
[self notifyStatusForProductIdentifier:productIdentifier
string:@"Purchase complete!"];
// 8
[self savePurchases];
}
- (void)restoreCompletedTransactions {
[[SKPaymentQueue defaultQueue]
restoreCompletedTransactions];
}
- (void)validateReceiptForTransaction:
(SKPaymentTransaction *)transaction {
HMAppDelegate *del = (HMAppDelegate *)[UIApplication sharedApplication].delegate;
IAPProduct * product =
_products[transaction.payment.productIdentifier];
VerificationController * verifier =
[VerificationController sharedInstance];
[verifier verifyPurchase:transaction
completionHandler:^(BOOL success) {
if (success) {
//NSLog(@"Successfully verified receipt!");
[self provideContentForTransaction:transaction
productIdentifier:
transaction.payment.productIdentifier];
del.previouslyVerified = YES;
}
if (del.previouslyVerified == YES) {
// NSLog(@"skipping verification");
}
else {
NSLog(@"Failed to validate receipt.");
product.purchaseInProgress = NO;
[[SKPaymentQueue defaultQueue]
finishTransaction: transaction];
NSLog(@"ferification failed.....");
}
}];
}
// 1
- (NSString *)libraryPath {
NSArray * libraryPaths =
NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask, YES);
return libraryPaths[0];
}
// 2
- (NSString *)purchasesPath {
return [[self libraryPath]
stringByAppendingPathComponent:IAPHelperPurchasesPlist];
}
// 3
- (void)addPurchase:(IAPProductPurchase *)purchase
forProductIdentifier:(NSString *)productIdentifier {
IAPProduct * product = [self
addProductForProductIdentifier:productIdentifier];
product.purchase = purchase;
}
// 4
- (IAPProductPurchase *)purchaseForProductIdentifier:
(NSString *)productIdentifier {
IAPProduct * product = _products[productIdentifier];
if (!product) return nil;
return product.purchase;
}
- (void)loadPurchases {
// 1
NSString * purchasesPath = [self purchasesPath];
// NSLog(@"purchase path : %@",purchasesPath);
NSDictionary *names = [[NSDictionary alloc]
initWithContentsOfFile:purchasesPath];
NSArray *keys = [names allKeys];
NSString *key = [keys objectAtIndex:0];
names = [names objectForKey:key];
// NSLog(@"keys = %@ names = %@",keys,names);
NSArray * purchasesArray = [NSKeyedUnarchiver
unarchiveObjectWithFile:purchasesPath];
for (IAPProductPurchase * purchase in purchasesArray) {
// 2
if (purchase.libraryRelativePath) {
NSString * localPath = [[self libraryPath]
stringByAppendingPathComponent:
purchase.libraryRelativePath];
NSURL * localURL = [NSURL fileURLWithPath:localPath
isDirectory:YES];
[self provideContentWithURL:localURL];
}
// 3
[self addPurchase:purchase
forProductIdentifier:purchase.productIdentifier];
// NSLog(@"Loaded purchase for %@ (%@)",purchase.productIdentifier, purchase.contentVersion);
}
}
- (void)savePurchases {
// 1
NSString * purchasesPath = [self purchasesPath];
NSMutableArray * purchasesArray = [NSMutableArray array];
for (IAPProduct * product in _products.allValues) {
if (product.purchase) {
[purchasesArray addObject:product.purchase];
}
}
// 2
BOOL success = [NSKeyedArchiver
archiveRootObject:purchasesArray toFile:purchasesPath];
// NSLog(@"purchase path : %@",purchasesPath);
if (!success) {
// NSLog(@"Failed to save purchases to %@", purchasesPath);
}
}
- (void)loadProductsWithCompletionHandler:(void (^)
(BOOL success, NSError * error))completionHandler {
for (IAPProduct * product in _products.allValues) {
product.info = nil;
product.availableForPurchase = NO;
}
NSURL * baseUrl = [NSURL URLWithString:IAPServerBaseURL];
AFHTTPClient * httpClient = [[AFHTTPClient alloc]
initWithBaseURL:baseUrl];
NSURL * url = [NSURL URLWithString:IAPServerProductsURL
relativeToURL:baseUrl];
NSMutableURLRequest * request = [NSURLRequest
requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60];
AFHTTPRequestOperation *operation = [httpClient
HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id
responseObject)
{
NSData * productInfosData = [operation responseData];
NSError * error = nil;
NSArray * productInfosArray =
[NSPropertyListSerialization
propertyListWithData:productInfosData
options:NSPropertyListImmutable format:NULL
error:&error];
if (productInfosArray == nil) {
completionHandler(FALSE, error);
} else {
for (NSDictionary * productInfoDict in
productInfosArray) {
IAPProductInfo * info = [[IAPProductInfo alloc]
initFromDict:productInfoDict];
[self addInfo:info
forProductIdentifier:info.productIdentifier];
};
if (!_productsLoaded) {
_productsLoaded = YES;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
completionHandler(TRUE, nil);
}
} failure:^(AFHTTPRequestOperation *operation,
NSError *error) {
completionHandler(FALSE, error);
}];
[httpClient enqueueHTTPRequestOperation:operation];
}
- (void)paymentQueue:(SKPaymentQueue *)queue
updatedDownloads:(NSArray *)downloads {
SKDownload * download = [downloads objectAtIndex:0];
SKPaymentTransaction * transaction = download.transaction;
SKPayment * payment = transaction.payment;
NSString * productIdentifier = payment.productIdentifier;
IAPProduct * product = _products[productIdentifier];
product.progress = download.progress;
NSLog(@"Download state: %d", download.downloadState);
if (download.downloadState == SKDownloadStateFinished) {
[self purchaseNonconsumableAtURL:download.contentURL
forProductIdentifier:productIdentifier];
product.purchaseInProgress = NO;
[[SKPaymentQueue defaultQueue] finishTransaction:
transaction];
} else if (download.downloadState ==
SKDownloadStateFailed) {
NSLog(@"Download failed.");
[self notifyStatusForProductIdentifier:productIdentifier
string:@"Download failed."];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Download Failed"
message:@"Make sure you have enough space on your device. If problems persist uninstall and reinstall the app. You will not be charged for restoring previously purchased classes."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
alertView.tag = 1;
[alertView show];
product.purchaseInProgress = NO;
[[SKPaymentQueue defaultQueue] finishTransaction:
transaction];
} else if (download.downloadState ==
SKDownloadStateCancelled) {
NSLog(@"Download cancelled.");
[self notifyStatusForProductIdentifier:productIdentifier
string:@"Download cancelled."];
product.purchaseInProgress = NO;
[[SKPaymentQueue defaultQueue] finishTransaction:
transaction];
} else {
NSLog(@"Download for %@: %0.2f complete",
productIdentifier, product.progress); }
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
NSLog(@"Clicked button index 0");}
}
- (void)pauseDownloads:(NSArray *)downloads {
[[SKPaymentQueue defaultQueue] pauseDownloads:downloads];}
- (void)resumeDownloads:(NSArray *)downloads {
[[SKPaymentQueue defaultQueue] resumeDownloads:downloads];}
- (void)cancelDownloads:(NSArray *)downloads { [[SKPaymentQueue defaultQueue] cancelDownloads:downloads];}
@end
In App Purchases not downloading in live app but working in sandbox mode
Aucun commentaire:
Enregistrer un commentaire