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