Приложение с покупкой в приложении вылетает при загрузке из Appstore, но не при локальной загрузке

#ios #swift #in-app-purchase #nsurl

#iOS #swift #покупка в приложении #nsurl

Вопрос:

Я только что отправил свое первое приложение, содержащее покупки в приложении, и оно было одобрено прошлой ночью. Я загрузил свое приложение и попытался купить покупку в приложении в целях тестирования, но, к моему удивлению, оно вылетает при нажатии кнопки, предлагающей опцию в приложении. Что более интересно, оно вылетает только при загрузке из Appstore, я удалил приложение и повторно загрузил его прямо со своего компьютера / XCode, и приложение не вылетело.

Есть ли вероятность, что URL-адрес был изменен, чтобы использовать отправитель для целей тестирования, когда приложение находилось на рассмотрении?

Это URL-адрес, который я использовал для производства:

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

Это URL-адрес, который я использовал для тестирования, который был закомментирован при отправке в Appstore:

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

Опять же, есть ли вероятность, что URL-адрес был изменен в целях тестирования, когда приложение было проверено и оставило URL-адрес для тестирования?

Есть ли способ узнать, какой URL-адрес используется в настоящее время в Appstore?

Спасибо

ОТРЕДАКТИРОВАНО 18.10.16:

Вот код, который я использую… Может кто-нибудь быть таким добрым и быстро взглянуть на него, чтобы увидеть, что что-то не так, особенно accessPremiumFeature метод, который вызывается при сбое происходит?

К вашему сведению — у меня все еще нет сведений об ошибке, потому что я не могу воспроизвести ее локально, а приложение было одобрено несколько часов назад.

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 — вот где происходит сбой при accessPremiumFeature вызове. Здесь что-то не так?

 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 — это основной код покупки в приложении (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)  
    }  
}  
  

Комментарии:

1. У вас есть журнал сбоев? Вы обозначили это символом? Знаете ли вы ошибку сбоя и строку, вызывающую сбой? Укажите всю эту информацию в своем вопросе.

2. У меня НЕТ ошибки сбоя, поскольку приложение было одобрено 2 часа назад. Что вы подразумеваете под symbolicate ?

3. Посмотрите в XCode, Organizer, сбой или подключите устройство к Mac и посмотрите в Devices, ваше устройство, «Просмотр журналов устройств»

Ответ №1:

Вы можете попробовать Charles HTTP Proxy и посмотреть, какой URL запрашивается, или открыть приложение с помощью шестнадцатеричного просмотра и выполнить поиск строки.

Комментарии:

1. Извините, но я запутался в двух ваших предложениях. Как использовать прокси? Что такое hex-viewer и как им пользоваться? Спасибо

2. HTTP-прокси только что установлен на вашем компьютере и активирован с пробной лицензией. Затем вы открываете сетевые настройки своего iPhone / iPad и вводите локальный IP-адрес в разделе HTTP Proxy. После этого вы видите каждый трафик своего телефона на компьютере и можете увидеть, какой URL-адрес используется для вашей покупки.

3. Шестнадцатеричный редактор используется для просмотра скомпилированных приложений. Просто откройте загруженное приложение на своем компьютере и попробуйте выполнить поиск по используемому URL.

4. @Fluchaa это будет работать так просто, как вы говорите в наши дни? Учитывая это, и все это SSL, поэтому ему нужно будет добавить неподписанный сертификат в свое хранилище ключей.

5. @toast Charles Proxy предлагает пользовательский профиль, который можно установить на ваш iPhone / iPad. Затем вы можете клонировать SSL-сертификат и отслеживать каждый HTTPS-трафик.