Search code examples
iosswiftin-app-purchase

Swift / App Store : Problem with in app purchase


I have a problem with my app. My in app purchase work very well on my side, on ios 12,4 iphone 5s. But when I send the binary at Apple Store I have a return :"We noticed that your app still still does not display the purchase button for the In-App Purchase product, in the app.".The buy button is only displayed when the processing to pay is ready. About 2 to 3 seconds at home. But with them apparently it does not work at all ... Here is the full code for my store that handles everything. And I specify that with my iphone I manage to have the purchase window and to buy the integrated purchase in sandbox.

import UIKit
import StoreKit
import MessageUI

class ShopViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver, MFMailComposeViewControllerDelegate {
    
    @IBOutlet weak var buyBtn: UIButton!
    @IBOutlet weak var restore: UIButton!
    @IBOutlet weak var mail: UIImageView!
    @IBOutlet weak var shopDescription: UILabel!
    
    var productsRequest = SKProductsRequest()
    var validProducts = [SKProduct]()
    var productIndex = 0
   
    override func viewDidLoad() {
        super.viewDidLoad()
         buyBtn.isHidden = true
              shopDescription.numberOfLines = 0
              shopDescription.lineBreakMode = NSLineBreakMode.byWordWrapping
              shopDescription.sizeToFit()
              shopDescription.text = NSLocalizedString("packpro", comment: "")
              //   SKPaymentQueue.default().add(self)
              let tap4 = UITapGestureRecognizer(target: self, action:#selector(tappedMe5))
              mail.addGestureRecognizer(tap4)
              mail.isUserInteractionEnabled = true
              fetchAvailableProducts()
    }
    
     func fetchAvailableProducts()  {
            let productIdentifiers = NSSet(objects:
                "customLifePremium"        // 0
            )
            productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
            productsRequest.delegate = self
            productsRequest.start()
        }
        
        func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {
            if (response.products.count > 0) {
                validProducts = response.products
                let prod100coins = response.products[0] as SKProduct
                print("1st rpoduct: " + prod100coins.localizedDescription)
                buyBtn.isHidden = false
            }
        }
        
    /*    func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
            return true
        }*/
        
        func canMakePurchases() -> Bool {  return SKPaymentQueue.canMakePayments()  }
        
        func purchaseMyProduct(_ product: SKProduct) {
            if self.canMakePurchases() {
                let payment = SKPayment(product: product)
                SKPaymentQueue.default().add(self)
                SKPaymentQueue.default().add(payment)
            } else { print("Purchases are disabled in your device!") }
        }
        
        func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
            for transaction:AnyObject in transactions {
                if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction {
                    switch trans.transactionState {
                        
                    case .purchased:
                        SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                        UserDefaults.standard.set(true, forKey: "premiumUser")
                        UserDefaults.standard.set(false, forKey: "limitedVersion")
                        break
                        
                    case .failed:
                        SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                        print("Payment has failed.")
                        break
                    case .restored:
                        SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                        print("Purchase has been successfully restored!")
                        UserDefaults.standard.set(true, forKey: "premiumUser")
                        UserDefaults.standard.set(false, forKey: "limitedVersion")
                        break
                        
                    default: break
                    }}}
        }
        
        func restorePurchase() {
            SKPaymentQueue.default().add(self as SKPaymentTransactionObserver)
            SKPaymentQueue.default().restoreCompletedTransactions()
        }
        
        func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
            print("The Payment was successfull!")
        }
        
        override func viewWillAppear(_ animated: Bool) {
            setGradientBackground()
            super.viewWillAppear(animated)
        }
        
        @IBAction func restoreCC(_ sender: Any) {
            restorePurchase()
        }
        
        @IBAction func buyCC(_ sender: Any) {
            productIndex = 0
            purchaseMyProduct(validProducts[productIndex])
        }
    
        @objc func tappedMe5()
        {
            if MFMailComposeViewController.canSendMail() {
                let mail = MFMailComposeViewController()
                mail.mailComposeDelegate = self
                mail.setToRecipients(["dfmv.enterprise@gmail.com"])
                mail.setSubject("")
                mail.setMessageBody("", isHTML: true)
                present(mail, animated: true)
            }else{
                let alert = UIAlertController(title: NSLocalizedString("info", comment: ""), message: NSLocalizedString("noClientMail", comment: ""), preferredStyle: UIAlertController.Style.alert)
                alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: UIAlertAction.Style.default, handler: nil))
                self.present(alert, animated: true, completion: nil)
            }
        }
        
        func setGradientBackground() {
            let colorTop =  UIColor(red:1.00, green:0.30, blue:0.30, alpha:1.0).cgColor
            let colorBottom = UIColor(red:1.00, green:0.69, blue:0.25, alpha:1.0).cgColor
            
            let gradientLayer = CAGradientLayer()
            gradientLayer.colors = [colorTop, colorBottom]
            gradientLayer.locations = [0.0, 1.0]
            gradientLayer.frame = self.view.bounds
            
            self.view.layer.insertSublayer(gradientLayer, at:0)
        }
}

Solution

  • Check this Gist that I personally used a week ago and passed the AppStore review for In-App Purchase flow.

    IAP Service in Swift 5:

    https://gist.github.com/egzonpllana/abd6d385bb45b1e329fe85a624ee531f

    You can call IAPService.shared.getProducts() in AppDelegate method: didFinishLaunchingWithOptions to have all the products ready to use from your StoreKit in any viewcontroller, just check IAPService.shared.products.count.

    Or call in your specific viewcontroller inside viewDidLoad and listen to changes with: IAPService.shared.didFinishRetrievingProducts = { [ weak self ] in ... }