Почему мое приложение отклонено для покупок в приложении? - PullRequest
0 голосов
/ 06 октября 2018

Здравствуйте, я публикую свое приложение в App Store, поскольку они настаивают на том, что мои покупки в приложении не настроены правильно.

После того, как я сам проверил их в песочнице, все работало правильно.

Это сообщение, которое они мне прислали, я вставил свой код ниже.

Спасибо, что нашли время помочь мне!

Рекомендация 2.1 - Производительность - Полнота приложения

Мы обнаружили, что в ваших продуктах покупки в приложении обнаружена одна или несколько ошибок при проверке на iPhone и iPad с iOS 12 на Wi-Fi.

В частности, вашкнопки покупки в приложении не работают.

Следующие шаги

При проверке квитанций на вашем сервере ваш сервер должен иметь возможность обрабатывать подписанное в производственных условиях приложение, получающее его квитанции из тестовой среды Apple.Рекомендуется, чтобы ваш производственный сервер всегда сначала проверял поступления в производственном App Store.Если проверка завершилась неудачно с кодом ошибки «Квитанция песочницы, используемая в производственной среде», вы должны выполнить проверку вместо тестовой среды.

class IAPService: NSObject {
    private override init() {}
    static let shared = IAPService()

    var products = [SKProduct]()
    let paymentQueue = SKPaymentQueue.default()

    func getProducts() {
        let products: Set = [IAPProduct.consumable.rawValue,
                             IAPProduct.nonConsumable.rawValue]
        let request = SKProductsRequest(productIdentifiers: products)
        request.delegate = self
        request.start()
        paymentQueue.add(self)
    }

    func purchase(product: IAPProduct) {


        for p in products {
            if p.productIdentifier == product.rawValue {
                let payment = SKPayment(product: p)
                paymentQueue.add(payment)
                print("Adding product to payment queue")
            }
        }
      }

    func restorePurchase() {
        print("Restoring purchases")
        paymentQueue.restoreCompletedTransactions()
    }


    func givePurchasedProduct(productID: String) {

        if productID.range(of: "Zap") != nil {

            NotificationCenter.default.post(name: Notification.Name.init("zapPurchased"), object: nil)

        } else if productID.range(of: "Ads") != nil {

            NotificationCenter.default.post(name: Notification.Name.init("noAdsPurchased"), object: nil)

        }
    }
 }
extension IAPService: SKProductsRequestDelegate {

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        self.products = response.products
        for product in response.products {
            print(product.localizedTitle)
        }
    }

}
    extension IAPService: SKPaymentTransactionObserver {
        func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
            for transaction in transactions {
                print(transaction.transactionState.status(), transaction.payment.productIdentifier)
                switch transaction.transactionState {
                case .purchasing, .deferred: break // do nothing
                case .purchased:
                    queue.finishTransaction(transaction)
                    givePurchasedProduct(productID: transaction.payment.productIdentifier)
                case .restored:
                    self.restorePurchase()
                    queue.finishTransaction(transaction)

               case .failed:
                    queue.finishTransaction(transaction)

                }
            }
        }
    }



    extension SKPaymentTransactionState {
        func status() -> String {
            switch self {
            case .deferred:
                return "deferred"
            case .failed:
                return "failed"
            case .purchased:
                return "purchased"
            case .purchasing:
                return "purchasing"
            case .restored:
                return "restored"
            }
        }
    }

Ответы [ 2 ]

0 голосов
/ 06 октября 2018

Я думаю, что нет проблем с вашим кодом iOS.В ответе Apple говорится, что ваш сервер указывает на производственную среду покупки Apple InApp и проверяет квитанции, полученные из тестовой среды покупки Apple InApp, используемой в приложении.

У Apple есть две среды для покупок InApp -Тестирование и производство.Обе среды ведут себя одинаково.Когда вы запускаете приложение на своем iPhone для тестирования с помощью QA или во время отладки, оно подключается к тестовой среде.Вы не платите в реальном времени при использовании тестовой среды.Но сгенерированные поступления почти такие же, как и в реальной производственной среде

Теперь, когда вы отправляете приложение в магазин, оно автоматически совершает покупки из производственной среды.С пользователей взимается плата и генерируются реальные квитанции.

Ваше приложение отправляет эти квитанции на сервер, я думаю, и ваш сервер использует неправильную среду сервера InApp для проверки квитанций.На своем сервере убедитесь, что URL-адрес среды покупки Apple InApp выбран правильно на основе квитанции о покупке InApp.Если вы или ваша команда тестируете приложение, ваш сервер должен использовать тестовый URL-адрес, а когда приложение отправляется в производство, ваш сервер должен использовать рабочий URL-адрес покупки InApp.

0 голосов
/ 06 октября 2018

Обзор приложений очень строг, когда дело доходит до Apple.Судя по опыту, у меня была эта проблема много раз.Ваш код мне кажется нормальным, пока он не перейдет к функции givePurchasedProduct.

Хорошо, так что я заметил:

  1. Ваше приложение обрабатывает платеж, и мы получаем return "purchased", если ничего не происходит,
  2. Если дело было case .purchased:, тогдаМы вызываем GivePurchasedProduct

На вашей функции.Вы разделяете покупку, чтобы узнать, является ли она Zap покупкой или она должна была удалить ads

Однако.эта строка сбивает меня с толку ... Зачем вам использовать range, когда contains, где недавно появился

if productID.contains("Zap") {
     // No Zapp? it has to be Ads then
     NotificationCenter.default.post(name: Notification.Name.init("zapPurchased"), object: nil)
} else {
     NotificationCenter.default.post(name: Notification.Name.init("noAdsPurchased"), object: nil)   
}

Примечания.Возможно, вы забыли:

  1. К import Foundation
  2. Я не знаю, что скрывается за уведомлениями наблюдателей, так как код не включен.Но.Это не доставляет

Это еще не все.Receipt Validating это головная боль, но когда это необходимо.Это расслабление и безопасность вашего приложения.

Если вы проверяете квитанцию.эти вопросы и ответы мне очень помогли.см.

Бонус.С SwiftyStoreKit.Проверка квитанции аналогична нажатию кнопки:

Используйте этот метод для (необязательно) обновления квитанции и проверки за один шаг.

let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator, forceRefresh: false) { result in
    switch result {
    case .success(let receipt):
        print("Verify receipt success: \(receipt)")
    case .error(let error):
        print("Verify receipt failed: \(error)")
    }
}

Теперь с другой стороны.рецензентам купленный контент не доставляется.Поэтому они думают, что это проверка покупки.

Как вы подтверждаете покупку?доставить контент?пожалуйста, обновите ваш вопрос.Я уверен, что буду полезен

Удачи

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...