fs_tigre
fs_tigre

Reputation: 10748

App with in-app purchase crashes when downloaded from the Appstore but not when loaded locally

I just submitted my first app that contains in-app purchases and it was approved last night. I downloaded my app and tried to buy the in-app purchase for testing purposes but to my surprise it crashes when the button that offers the in-app option is tapped. What is more interesting is that it only crashes when downloaded from the Appstore, I deleted the app and re-downloaded it directly from my computer/XCode and the app didn't crash.

Are there any chances that the URL was changed to use the sendbox for testing purposes when the app was in review?

This is the URL I used for production:

let storeURL = NSURL(string: "https://buy.itunes.apple.com/verifyReceipt")

This is the URL I used for testing which was commented out when submitted to the Appstore:

let storeURL = NSURL(string: "https:/sandbox.itunes.apple.com/verifyReceipt")

Again, is there any chance that the URL was changed for testing purposes when the app was in review and left the testing URL?

Is there a way to know what URL is currently in use, in the Appstore?

Thanks

EDITED on 10/18/16:

Here is the code I'm using... Can someone be so kind and give it a quick look to see if something is wrong, especially the accessPremiumFeature method which is the one being called when the crash is happening?

FYI - I still don't have error details because I cannot reproduce it locally and the app was approved hours ago.

AppDelegate.swif

import StoreKit  
class AppDelegate: UIResponder, UIApplicationDelegate {  
    var window: UIWindow?  
    var canPurchase:Bool = false  
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {  
        if SKPaymentQueue.canMakePayments(){  
            self.canPurchase = true  
            IAPManager.sharedInstance.setupInAppPurchases()  
        }  
        return true  
    }  
} 

SettingsViewController.swift - Here is where the crash occurs when accessPremiumFeature is called. Is there anything wrong here?

import StoreKit  

class SettingsViewController: UIViewController {  
    @IBAction func accessPremiumFeature() {  
        if NSUserDefaults.standardUserDefaults().boolForKey("com.domain.appName"){  
            let alert = UIAlertController(title: "PRO-Version", message: "You already have the PRO version.", preferredStyle: .Alert)  
            alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))  
            self.presentViewController(alert, animated: true, completion: nil)  
        }else{  
            var productInfo:SKProduct?  
            for product in IAPManager.sharedInstance.products{  
                productInfo = product as? SKProduct  
            }  
            let alertController = UIAlertController(title: "Premium Features", message: "Unlock all premium features for \(productInfo!.price)." + "This includes... bla, bla, bla...", preferredStyle: .Alert)  
            alertController.view.tintColor = UIColor.myRedColor()  
            let okAction = UIAlertAction(title: "Ok", style: .Default, handler: nil)  
            let buyAction = UIAlertAction(title: "Buy", style: .Default) { (action) -> Void in  
                let vc = self.storyboard?.instantiateViewControllerWithIdentifier("StoreTableView") as! StoreTableViewController  
                self.presentViewController(vc, animated: true, completion: nil)  
            }  
            alertController.addAction(okAction)  
            alertController.addAction(buyAction)  
            self.presentViewController(alertController, animated: true, completion: nil)  
        }  
    }  
}  

IAPManager.swift - This is the main in-app purchase code (Brain).

import StoreKit  

// protocol to notify when user restores purchase  
protocol IAPManagerDelegate {  
    func managerDidRestorePurchases()  
}  


class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver, SKRequestDelegate {  
    static let sharedInstance = IAPManager()  
    var request:SKProductsRequest!  
    var products:NSArray!  

    var delegate:IAPManagerDelegate?  

    //Load product identifiers for store usage  
    func setupInAppPurchases(){  
        self.validateProductIdentifiers(self.getProductIdentifiersFromMainBundle())  
        SKPaymentQueue.defaultQueue().addTransactionObserver(self)  
    }  

    //Get product identifiers  
    func getProductIdentifiersFromMainBundle() -> NSArray {  
        var identifiers = NSArray()  
        if let url = NSBundle.mainBundle().URLForResource("iap_product_ids", withExtension: "plist"){  
            identifiers = NSArray(contentsOfURL: url)!  
        }  
        return identifiers  
    }  

    //Retrieve product information  
    func validateProductIdentifiers(identifiers:NSArray) {  
        let productIdentifiers = NSSet(array: identifiers as [AnyObject])  
        let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)  
        self.request = productRequest  
        productRequest.delegate = self  
        productRequest.start()  
    }  

    func createPaymentRequestForProduct(product:SKProduct){  
        let payment = SKMutablePayment(product: product)  
        payment.quantity = 1  
        SKPaymentQueue.defaultQueue().addPayment(payment)  
    }  

    func verifyReceipt(transaction:SKPaymentTransaction?){  
        let receiptURL = NSBundle.mainBundle().appStoreReceiptURL!  
        if let receipt = NSData(contentsOfURL: receiptURL){  
            //Receipt exists  
            let requestContents = ["receipt-data" : receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))]  

            //Perform request  
            do {  
                let requestData = try NSJSONSerialization.dataWithJSONObject(requestContents, options: NSJSONWritingOptions(rawValue: 0))  

                //Build URL Request  
                let storeURL = NSURL(string: "https://buy.itunes.apple.com/verifyReceipt")// production URL  
                //let storeURL = NSURL(string: "https:/sandbox.itunes.apple.com/verifyReceipt") // Testing URL  
                let request = NSMutableURLRequest(URL: storeURL!)  
                request.HTTPMethod = "Post"  
                request.HTTPBody = requestData  

                let session = NSURLSession.sharedSession()  
                let task = session.dataTaskWithRequest(request, completionHandler: { (responseData:NSData?, response:NSURLResponse?, error:NSError?) -> Void in  
                    do {  
                        let json = try NSJSONSerialization.JSONObjectWithData(responseData!, options: .MutableLeaves) as! NSDictionary  

                        print(json)  

                        if (json.objectForKey("status") as! NSNumber) == 0 {  
                            if let latest_receipt = json["latest_receipt_info"]{  
                                self.validatePurchaseArray(latest_receipt as! NSArray)  
                            } else {  
                                let receipt_dict = json["receipt"] as! NSDictionary  
                                if let purchases = receipt_dict["in_app"] as? NSArray{  
                                    self.validatePurchaseArray(purchases)  
                                }  
                            }  

                            if transaction != nil {  
                                SKPaymentQueue.defaultQueue().finishTransaction(transaction!)  
                            }  

                            dispatch_sync(dispatch_get_main_queue(), { () -> Void in  
                                self.delegate?.managerDidRestorePurchases()  
                            })  

                        } else {  
                            //Debug the receipt  
                            print(json.objectForKey("status") as! NSNumber)  
                        }  
                    } catch {  
                        print(error)  
                    }  
                })  
                task.resume()  
            } catch {  
                print(error)  
            }  
        } else {  
            //Receipt does not exist  
            print("No Receipt")  
        }  
    }  

    func validatePurchaseArray(purchases:NSArray){  
        for purchase in purchases as! [NSDictionary]{  
            self.unlockPurchasedFunctionalityForProductIdentifier(purchase["product_id"] as! String)  
        }  
    }  


    func unlockPurchasedFunctionalityForProductIdentifier(productIdentifier:String){  
        NSUserDefaults.standardUserDefaults().setBool(true, forKey: productIdentifier)  
        NSUserDefaults.standardUserDefaults().synchronize()  
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false  
    }  

    func lockPurchasedFunctionalityForProductIdentifier(productIdentifier:String){  
        NSUserDefaults.standardUserDefaults().setBool(false, forKey: productIdentifier)  
        NSUserDefaults.standardUserDefaults().synchronize()  
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false  
    }  

    //MARK: SKProductsRequestDelegate  
    func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {  
        self.products = response.products  
        print(self.products)  
    }  

    // MARK: SKPaymentTransactionObserver Protocol  
    func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {  
        for transaction in transactions as [SKPaymentTransaction]{  
            switch transaction.transactionState{  
            case .Purchasing:  
                print("Purchasing")  
                UIApplication.sharedApplication().networkActivityIndicatorVisible = true  
            case .Deferred:  
                print("Deferrred")  
                UIApplication.sharedApplication().networkActivityIndicatorVisible = false  
            case .Failed:  
                print("Failed")  
                print(transaction.error?.localizedDescription)  
                UIApplication.sharedApplication().networkActivityIndicatorVisible = false  
                SKPaymentQueue.defaultQueue().finishTransaction(transaction)  
            case.Purchased:  
                print("Purchased")  
                self.verifyReceipt(transaction)  
            case .Restored:  
                print("Restored")  
            }  
        }  
    }  

    func restorePurchases(){  
        let request = SKReceiptRefreshRequest()  
        request.delegate = self  
        request.start()  
    }  

    func requestDidFinish(request: SKRequest) {  
        self.verifyReceipt(nil)  
    }  
}  

Upvotes: 0

Views: 462

Answers (1)

Fluchaa
Fluchaa

Reputation: 116

You could try Charles HTTP Proxy and watch which URL is requested or you open the App with a hex-viewer and search for the string.

Upvotes: 1

Related Questions