在 appstoreconnect 中无法选择应用内购买

发布于 2025-01-16 04:37:46 字数 3533 浏览 0 评论 0原文

我的新应用程序有四个应用程序内购买(消耗品),我使用这些 IAP 提交了第一个版本。这些 IAP 被标记为“准备审核”。然而,该应用程序由于另一个原因被拒绝,当我上传新版本时,我无法再在应用程序详细信息页面中选择这些 IAP,即使它们仍然“准备审核”: 应用详细信息页面的屏幕截图

因此,在重新提交新版本的应用进行审核后,我得到了本次拒签信息:

我们发现,在通过 Wi-Fi 运行 iOS 15.4 的 iPad 上审核时,您的应用内购买产品出现了一个或多个错误。

具体来说,我们无法购买应用内购买项目。按钮对点击没有反应

后续步骤

在服务器上验证收据时,您的服务器需要能够处理从 Apple 测试环境获取收据的生产签名应用程序。建议的方法是让您的生产服务器始终首先根据生产 App Store 验证收据。如果验证失败并显示错误代码“生产中使用的沙盒收据”,您应该改为针对测试环境进行验证。

我之前在 Testflight 上测试了所有内容,所有 IAP 都工作正常。我知道在提交带有 IAP 的应用程序之前,必须在应用程序详细信息页面上选择这些购买,所以我很好奇为什么我无法选择它们以及这是否导致了问题。

当应用程序启动时,在 AppDelegate 中,我获取产品:IAPManager.shared.fetchProducts()

,IAPManager 的代码如下:

final class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {

static let shared = IAPManager()
var products = [SKProduct]()
enum Product: String, CaseIterable {
    case firstIdentifier = "com.fahrprueferCreate.tokens_1_1000"
    case secondIdentifier = "com.FahrprueferCreate.tokens_5_4000"
    case thirdIdentifier = "com.FahrprueferCreate.tokens_10_8000"
    case fourthIdentifier = "com.FahrprueferCreate.tokens_20_15000"
    
    var count: Int {
        switch self {
        case .firstIdentifier:
            return 1
        case .secondIdentifier:
            return 5
        case .thirdIdentifier:
            return 10
        case .fourthIdentifier:
            return 20
        }
    }
}
private var completion: ((Int) -> Void)?

// Fetch Product Objects from Apple
func fetchProducts() {
    let request = SKProductsRequest(productIdentifiers: Set(Product.allCases.compactMap({ $0.rawValue})))
    request.delegate = self
    request.start()
}

// Prompt a product payment transaction
public func purchase(product: Product, completion: @escaping ((Int) -> Void)) {
    guard SKPaymentQueue.canMakePayments() else {
        // Show some error here
        return
    }
    
    guard let storeKitProduct = products.first(where: { $0.productIdentifier == product.rawValue }) else {
        return
    }
    self.completion = completion
    
    let payment = SKPayment(product: storeKitProduct)
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().add(payment)
}

// Observe the transaction state
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    transactions.forEach({ transaction in
        switch transaction.transactionState {
        case.purchasing:
            break
        case .purchased:
            if let product = Product(rawValue: transaction.payment.productIdentifier) {
                completion?(product.count)
            }
            SKPaymentQueue.default().finishTransaction(transaction)
            SKPaymentQueue.default().remove(self)
            break
        case .restored:
            break
        case .failed:
            break
        case .deferred:
            break
        @unknown default:
            break
        }
    })
}

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    self.products = response.products
    print("products: ", response.products)
}

func request(_ request: SKRequest, didFailWithError error: Error) {
    guard request is SKProductsRequest else {
        return
    }
    
    print("Product fetch request failed")
}

}

My new app has four in app purchases (consumables) and I submitted the first version with these IAPs. These IAP's were marked as "Ready for Review". However, the app got rejected due to another reason, and when I uploaded a new build, I couldn't select these IAP's anymore in the app details page, even though they're still "Ready for Review":
screenshot of the app details page

So after resubmitting a new version of the app for review, I got this rejection information:

We found that your in-app purchase products exhibited one or more bugs when reviewed on iPad running iOS 15.4 on Wi-Fi.

Specifically, we were not able to buy the in app purchases. The buttons did not react to taps

Next Steps

When validating receipts on your server, your server needs to be able to handle a production-signed app getting its receipts from Apple’s test environment. The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code "Sandbox receipt used in production," you should validate against the test environment instead.

I tested everything on Testflight before and all the IAP's were working fine. I know that prior to submitting an app with IAP's, these purchases have to be selected on the app details page, so I'm curious why I can't select them and if that's causing the issue.

Right when the app launches, in the AppDelegate, I fetch the products: IAPManager.shared.fetchProducts()

and the code for the IAPManager is as follows:

final class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {

static let shared = IAPManager()
var products = [SKProduct]()
enum Product: String, CaseIterable {
    case firstIdentifier = "com.fahrprueferCreate.tokens_1_1000"
    case secondIdentifier = "com.FahrprueferCreate.tokens_5_4000"
    case thirdIdentifier = "com.FahrprueferCreate.tokens_10_8000"
    case fourthIdentifier = "com.FahrprueferCreate.tokens_20_15000"
    
    var count: Int {
        switch self {
        case .firstIdentifier:
            return 1
        case .secondIdentifier:
            return 5
        case .thirdIdentifier:
            return 10
        case .fourthIdentifier:
            return 20
        }
    }
}
private var completion: ((Int) -> Void)?

// Fetch Product Objects from Apple
func fetchProducts() {
    let request = SKProductsRequest(productIdentifiers: Set(Product.allCases.compactMap({ $0.rawValue})))
    request.delegate = self
    request.start()
}

// Prompt a product payment transaction
public func purchase(product: Product, completion: @escaping ((Int) -> Void)) {
    guard SKPaymentQueue.canMakePayments() else {
        // Show some error here
        return
    }
    
    guard let storeKitProduct = products.first(where: { $0.productIdentifier == product.rawValue }) else {
        return
    }
    self.completion = completion
    
    let payment = SKPayment(product: storeKitProduct)
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().add(payment)
}

// Observe the transaction state
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    transactions.forEach({ transaction in
        switch transaction.transactionState {
        case.purchasing:
            break
        case .purchased:
            if let product = Product(rawValue: transaction.payment.productIdentifier) {
                completion?(product.count)
            }
            SKPaymentQueue.default().finishTransaction(transaction)
            SKPaymentQueue.default().remove(self)
            break
        case .restored:
            break
        case .failed:
            break
        case .deferred:
            break
        @unknown default:
            break
        }
    })
}

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    self.products = response.products
    print("products: ", response.products)
}

func request(_ request: SKRequest, didFailWithError error: Error) {
    guard request is SKProductsRequest else {
        return
    }
    
    print("Product fetch request failed")
}

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文