Почему приложение вылетает при попытке покупки в App Store, но все работает в изолированной среде (iOS)?

#ios #swift #in-app-purchase #app-store

#iOS #swift #покупка в приложении #app-store

Вопрос:

Всем доброго дня!

Проблема в следующем: когда я тестирую приложение в изолированной среде, все работает нормально. И приложение появляется в App Store и вылетает при попытке покупки. Покупка не является расходным материалом.

Некоторые факты, которые могут навести на правильную мысль:

  • Сбой происходит после строк: let payment = SKPayment (product: subscription.product); SKPaymentQueue.default().Добавить(платеж)
  • Приложение вылетает не «мгновенно», а через ~ 1 секунду, что означает, что оно вылетает после некоторого ответа от сервера
  • Окно с вводом пароля, логина, подтверждением покупки не появляется на экране. Сбой происходит до этого.
  • Это приложение было размещено в App Store в качестве обновления старого. Оно было создано в новом проекте, в котором идентификатор пакета был изменен на идентификатор пакета старого приложения.
  • Все покупки в itunesconnect одобрены модератором, они отображаются в App Store.
 class BillingManager: NSObject {

    static let sessionIdSetNotification = Notification.Name("SubscriptionServiceSessionIdSetNotification")
    static let optionsLoadedNotification = Notification.Name("SubscriptionServiceOptionsLoadedNotification")
    static let restoreSuccessfulNotification = Notification.Name("SubscriptionServiceRestoreSuccessfulNotification")
    static let purchaseSuccessfulNotification = Notification.Name("SubscriptionServiceRestoreSuccessfulNotification")

    private override init() { }
    private static var instance: BillingManager?

    /** создает 1 объект или возвращает текущий (синглтон) */
    static func shared() -> BillingManager {
        if instance == nil {
            instance = BillingManager()
        }
        return instance!
    }

    var isPro: Bool {

        /** Здесь проверяем дату подписки по UserDefaults */

        var subscriptionAccess = false


        if let purchase = UserDefaults.standard.string(forKey: "purchaseDate") {
            if let expires = UserDefaults.standard.string(forKey: "expiresDate") {
                if let purchaseDate = dateFormatter.date(from: purchase), let expiresDate = dateFormatter.date(from: expires) {

                    subscriptionAccess = (purchaseDate...expiresDate).contains(Date())

                }
            }
        }

        return (currentSubscription != nil) || subscriptionAccess || UserDefaults.standard.bool(forKey: "forever.subscription")
    }

    var hasReceiptData: Bool {
        return loadReceipt() != nil
    }

    var currentSessionId: String? {
        didSet {
            NotificationCenter.default.post(name: BillingManager.sessionIdSetNotification, object: currentSessionId)
        }
    }

    var currentSubscription: PaidSubscription?

    var options: [Subscription]? {
        didSet {
            NotificationCenter.default.post(name: BillingManager.optionsLoadedNotification, object: options)
        }
    }

    func loadSubscriptionOptions() {
        let productIDs = Set([InAppType.month.rawValue,
                              InAppType.halfYear.rawValue,
                              InAppType.year.rawValue,
                              InAppType.forever.rawValue,
                              InAppType.discountedForever.rawValue])

        let request = SKProductsRequest(productIdentifiers: productIDs)
        request.delegate = self
        request.start()
    }

    func purchase(subscription: Subscription) {
        let payment = SKPayment(product: subscription.product)
        SKPaymentQueue.default().add(payment)
    }

    func restorePurchases() {
        SKPaymentQueue.default().restoreCompletedTransactions()
    }

    func uploadReceipt(completion: ((_ success: Bool) -> Void)? = nil) {

        if let receiptData = loadReceipt() {
            Service.shared.upload(receipt: receiptData) { [weak self] (result) in
                guard let strongSelf = self else { return }
                switch result {
                case .success(let result):
                    strongSelf.currentSessionId = result.sessionId
                    strongSelf.currentSubscription = result.currentSubscription
                    completion?(true)
                case .failure(let error):
                    print("🚫 Receipt Upload Failed: (error)")
                    completion?(false)
                }
            }
        }
    }

    private func loadReceipt() -> Data? {
        guard let url = Bundle.main.appStoreReceiptURL else {
            return nil
        }

        do {
            let data = try Data(contentsOf: url)
            return data
        } catch {
            print("Error loading receipt data: (error.localizedDescription)")
            return nil
        }
    }

}


extension BillingManager: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        options = response.products.map { Subscription(product: $0) }.sorted(by: { (left, right) -> Bool in
            left.product.price < right.product.price
        })

    }

    func request(_ request: SKRequest, didFailWithError error: Error) {
        if request is SKProductsRequest {
            print("Subscription Options Failed Loading: (error.localizedDescription)")
        }
    }
}
  

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

1. У вас есть приложение в appstore, в expiresDate котором хранятся пользовательские настройки по умолчанию??!??!?!!!??!!

2. Что произошло? ДА

3. Вы знаете о том, что любой может изменить эти значения?

4. Где добавлен ваш наблюдатель очереди платежей? Можете ли вы показать его код. Можете ли вы запустить приложение на своем собственном устройстве и получить журнал сбоев или сообщение об исключении с консоли?

5. @Paulw11 куда я могу отправить вам код? И теперь у меня есть журнал сбоев, но я не могу понять, где ошибка