Search code examples
iosswiftin-app-purchasein-app-subscription

After canceling the subscription, the user is still allowed access to app


The problem that I am experiencing is that the app keeps working if you try to cancel the subscription.

Here is my code snippet:

override func viewDidLoad() {
    super.viewDidLoad()

    if(SKPaymentQueue.canMakePayments()) {
        print("IAP is enabled, loading")
        let productID: NSSet = NSSet(objects: "Seb.DiPlus.RenewingSubMonthAuto", "Seb.DiPlus.RenewingSubYearAuto")
        let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
        request.delegate = self
        request.start()
    } else {
        print("please enable IAPS")
    }
}

@IBAction func subscribeMonth(_ sender: Any) {
    for product in list {
        let prodID = product.productIdentifier
        if(prodID == "Seb.DiPlus.RenewingSubMonthAuto") {
            p = product
            buyProduct()
        }
    }
}

@IBAction func subscribeYear(_ sender: Any) {
    for product in list {
        let prodID = product.productIdentifier
        if(prodID == "Seb.DiPlus.RenewingSubYearAuto") {
            p = product
            buyProduct()
        }
    }
}

@IBAction func restoreComplete(_ sender: UIButton) {
    restorePurchases()
}

@IBAction func exit(_ sender: Any) {
    _exit(0)
}

func restorePurchases() {
    if SKPaymentQueue.canMakePayments(){
        print("restored complete")
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().restoreCompletedTransactions()
    }
    else{
        print("restored faild, IAP not activ?")
    }
}

func buyProduct() {
    print("buy " + p.productIdentifier)
    let pay = SKPayment(product: p)
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().add(pay as SKPayment)
}

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    print("product request")
    let myProduct = response.products
    for product in myProduct {
        print("product added")
        print(product.productIdentifier)
        print(product.localizedTitle)
        print(product.localizedDescription)
        print(product.price)

        list.append(product)
    }
}

func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
    print("transactions restored")
    for transaction in queue.transactions {
        let t: SKPaymentTransaction = transaction
        let prodID = t.payment.productIdentifier as String

        switch prodID {
        case "Seb.DiPlus.RenewingSubMonthAuto":
            print("Subscribe Month!")
            if abo < 1 {
                readySubscribe()
            }
            abo = 1
            break
        case "Seb.DiPlus.RenewingSubYearAuto":
            print("Subscribe Year!")
            if abo < 1 {
                readySubscribe()
            }
            abo = 1
            break
        default:
            print("IAP not found")
        }
    }
}

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    print("add payment")

    for transaction: AnyObject in transactions {
        let trans = transaction as! SKPaymentTransaction
        print("ERROR: ", trans.error)

        switch trans.transactionState {
        case .purchased:
            print("buy ok, unlock IAP HERE")
            print(p.productIdentifier)

            let prodID = p.productIdentifier
            switch prodID {
            case "Seb.DiPlus.RenewingSubMonthAuto":
                print("Subscribe Month!!")
                if abo < 1 {
                    readySubscribe()
                }
                abo = 1
                break
            case "Seb.DiPlus.RenewingSubYearAuto":
                print("Subscribe Year!!")
                if abo < 1 {
                    readySubscribe()
                }
                abo = 1
                break
            default:
                print("IAP not found")
            }
            queue.finishTransaction(trans)
        case .failed:
            print("buy error")
            alert(title: "ERROR", message: trans.error?.localizedDescription)
            queue.finishTransaction(trans)
            break
        default:
            print("Default")
            break
        }
    }
}

func readySubscribe()  {
    UserDefaults.standard.setValue(checkSubscribe, forKeyPath: "subscribe")
    UserDefaults.standard.synchronize()
    self.performSegue(withIdentifier: "readySubscribe", sender: self)

}

func alert (title:String, message:String?){

    let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)

    alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: { (action) in
        alert.dismiss(animated: true, completion: nil)
    }))

    self.present(alert, animated: true, completion: nil)
}

Once I click on restore purchases, you can continue to use the app even though the subscription is no longer activated.

So the function readySubscribe() is called.


Solution

  • You should not activate any purchases in paymentQueueRestoreCompletedTransactionsFinished. This function is called when the restoration process is complete. You can use it to update your UI or alert the user.

    The actual restoration of products should be handled in your paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) function.

    Restored transactions appear with a state of .restored. You should process them exactly as you process a .purchased state.

    Since you are using auto-renewing subscription IAPs you also need to check expiration dates from the receipt and be prepared for new transactions to be presented when the subscription renews. For this reason one of the first things your app should do in didFinishLaunchingWithOptions is create a payment queue observer.