Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my App Rejected for In-App Purchases?

Hello I am having getting my App published on the App Store as they keep insisting my in-app purchases are not set up correctly.

Upon testing them myself in the sandbox, everything worked correctly.

This is the message they sent me, I pasted my code below.

Thank you for taking the time to help me out!

Guideline 2.1 - Performance - App Completeness

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

Specifically, your in app purchase buttons do not work.

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.

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"
            }
        }
    }
like image 768
Pat Trudel Avatar asked Feb 04 '26 17:02

Pat Trudel


1 Answers

App review is very strict when it comes to Apple. Speaking from experience, I have had this problem many times. Your code seems fine to me till it goes to the givePurchasedProduct function.

Okay so things i noticed:

  1. Your app processes the payment and we get return "purchased" if nothing goes wrong
  2. If the case was case .purchased: then we invoke the givePurchasedProduct

On your function. you separate the purchase to see if it's either a Zap purchase or it was to remove the ads

However. this line is confusing me-- Why would you use range when contains where introduced recently.

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)   
}

Side notes. You might have forgot:

  1. To import Foundation
  2. I don't know what goes behind the notification observers since the code is not included. But. It's not delivering

There's more to it. Receipt Validating is a headache, but when it's needed. It's relaxation and more security to your app.

If you're validating the receipt. these question and it's answers helped me a lot. please see:

  • Implementing Receipt Validation in Swift 3
  • Lightweight In App Purchases Swift framework for iOS 8.0+, tvOS 9.0+ and macOS 10.10+

Bonus. With SwiftyStoreKit. Receipt validating is just like tapping a button:

Use this method to (optionally) refresh the receipt and perform validation in one step.

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)")
    }
}

Now on the other hand. to the reviewers the purchased content is not delivering. So they think it's purchase validating.

How do you validate the purchase? deliver the content? please update your question. I'm sure i'll be helpful

Good Luck

like image 98
excitedmicrobe Avatar answered Feb 06 '26 06:02

excitedmicrobe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!