在应用程序购买中永远不会完成

时间:2017-11-16 16:26:57

标签: swift in-app-purchase storekit

我有一个用户在进行应用内购买时遇到了困难。我已经有60-65个成功的应用程序购买,但这个用户经历过几次问题。应用内购买设置为非续订订阅。我相信他两次都有问题,他一直试图在他的订阅到期的同一天购买。知道为什么他的应用程序购买过程永远不会完成?

以下是他如何描述会发生什么:

一步一步。 1.订阅用完了 2.单击续订 3.我在添加应用程序和支付应用程序时都有密码保护,因此我通过密码并接受收费。 4.应用程序中接受的收费状态,但是,当我点击使用应用程序时,它会不断要求我续订。 (作者注:我认为他指的是我在应用程序中提供的警报控制器,当他们的订阅日期早于今天的日期时)然后,当我再次尝试付款时,它说已经在app中购买了。尝试至少5次。 5.每次尝试重新尝试失败后,只显示应用程序处于重新加载模式。在app中间有一个思维旋转轮是最好的描述方式。

以下是相关代码:

@IBAction func buyOneMonthTapped(_ sender: Any) {

        activity.startAnimating()
        activity.isHidden = false


        product_ID = **(removed)**
        purchaseLength = "onemonth"

        print("About to fetch the products")

        // We check that we are allowed to make the purchase.


        if (SKPaymentQueue.canMakePayments())
        {
            let productID:NSSet = NSSet(object: self.product_ID!);
            let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
            productsRequest.delegate = self;
            productsRequest.start();
            print("Fetching Products");

        }  else { print("can't make purchases")

            let cantPurchaseAlert = UIAlertController(title: "Can't Make Purchases", message: "You do not have the ability to make purchases on this device. Please check your settings and payment info and try again.", preferredStyle: .alert)
            cantPurchaseAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                self.activity.isHidden = true
                self.activity.stopAnimating()
            }))


        }

    }


    @IBAction func buyOneYearTapped(_ sender: Any) {

        activity.startAnimating()
        activity.isHidden = false

        product_ID = **(removed)**
        purchaseLength = "oneyear"

        print("About to fetch the products")

        // We check that we are allowed to make the purchase.


        if (SKPaymentQueue.canMakePayments())
        {
            let productID:NSSet = NSSet(object: self.product_ID!);
            let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
            productsRequest.delegate = self;
            productsRequest.start();
            print("Fetching Products");

        }  else { print("can't make purchases")

            let cantPurchaseAlert = UIAlertController(title: "Can't Make Purchases", message: "You do not have the ability to make purchases on this device. Please check your settings and payment info and try again.", preferredStyle: .alert)
            cantPurchaseAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                self.activity.isHidden = true
                self.activity.stopAnimating()
            }))

        }

    }

    func buyProduct(_ product: SKProduct){
        print("Sending the Payment Request to Apple");
        let payment = SKPayment(product: product)
        SKPaymentQueue.default().add(payment);

    }

    func productsRequest (_ request: SKProductsRequest, didReceive response: SKProductsResponse) {

        let count : Int = response.products.count
        if (count>0) {
            let validProducts = response.products
            let validProduct: SKProduct = response.products[0] as SKProduct
            if (validProduct.productIdentifier == self.product_ID as String!) {
                print(validProduct.localizedTitle)
                print(validProduct.localizedDescription)

                print(validProduct.price)
                buyProduct(validProduct);
            } else {
                print(validProduct.productIdentifier)
            }
        } else {
            print("nothing")
        }
    }

    func request(_ request: SKRequest, didFailWithError error: Error) {
        print("Error Fetching product information")

        let failedPurchaseAlert = UIAlertController(title: "We're sorry. There was an error with the purchase.", message: "Please quit the app and try again. If the error continues please email support@*****.com", preferredStyle: .alert)
        failedPurchaseAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
            self.activity.isHidden = true
            self.activity.stopAnimating()
        }))

        ;
    }

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])    {
        print("Received Payment Transaction Response from Apple");

        for transaction:AnyObject in transactions {
            if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{


                switch trans.transactionState {
                case .purchased:
                    print("Product Purchased");
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)

                    subscriptionEndDate = userSettings.object(forKey: "subEnd") as? Date

                    let calendar = Calendar.current

                    let today = Date()

                    if purchaseLength == "onemonth" {

                        if (subscriptionEndDate as NSDate?)?.earlierDate(today) == today {

                            subscriptionEndDate = (calendar as NSCalendar).date(byAdding: [.month], value: 1, to: subscriptionEndDate!, options: [])!

                        } else {

                            subscriptionEndDate = (calendar as NSCalendar).date(byAdding: [.month], value: 1, to: today, options: [])!

                        } }

                    else  if purchaseLength == "oneyear" {

                        if (subscriptionEndDate as NSDate?)?.earlierDate(today) == today {

                            subscriptionEndDate = (calendar as NSCalendar).date(byAdding: [.year], value: 1, to: subscriptionEndDate!, options: [])!

                        } else {

                            subscriptionEndDate = (calendar as NSCalendar).date(byAdding: [.year], value: 1, to: today, options: [])!

                        }


                    }

                    print("added time")

                    let components = (calendar as NSCalendar).components([.day, .month, .year], from: subscriptionEndDate!)

                    let year = components.year
                    let month = components.month
                    let day = components.day

                    let testDate = "\(month!)/\(day!)/\(year!)"


                    if (subscriptionEndDate! as NSDate).laterDate(today) == subscriptionEndDate {

                        subscriptionMessageLabel.text = "Your subscription expires on:"
                        subscriptionLabel.text = "\(testDate)" }


                    else {

                        subscriptionMessageLabel.text = "There was an error in the purchase."
                        subscriptionLabel.text = "Please contact Support@UndauntedAthlete.com" }

                    userSettings.set(subscriptionEndDate, forKey: "subEnd")


                    ref.updateChildValues(["users/\(userID!)/subscriptionDate": testDate])

                    activity.isHidden = true
                    activity.stopAnimating()

                    break;

                case .failed:
                    print("Purchased Failed");
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)

                    let failedPurchaseAlert = UIAlertController(title: "We're sorry. There was an error with the purchase.", message: "Please quit the app and try again. If the error continues please email support@****.com", preferredStyle: .alert)
                    failedPurchaseAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                        self.activity.isHidden = true
                        self.activity.stopAnimating()
                    }))

                    break

                case .restored:
                    print("Already Purchased");
                    SKPaymentQueue.default().restoreCompletedTransactions()

                    let failedPurchaseAlert = UIAlertController(title: "We're sorry. There was an error with the purchase.", message: "Please quit the app and try again. If the error continues please email support@*****.com", preferredStyle: .alert)
                    failedPurchaseAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                        self.activity.isHidden = true
                        self.activity.stopAnimating()
                    }))

                default:
                    let failedPurchaseAlert = UIAlertController(title: "We're sorry. There was an error with the purchase.", message: "Please quit the app and try again. If the error continues please email support@*****.com", preferredStyle: .alert)
                    failedPurchaseAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                        self.activity.isHidden = true
                        self.activity.stopAnimating()
                    }))

                    break
                }
            }
        }
    }

1 个答案:

答案 0 :(得分:3)

检查您的代码有很多可能导致客户端漏洞的潜在问题。

  1. 我建议在向用户显示UI之前预取SKProducts。这允许您在开始购买之前显示价格,这在我看来是一个很好的用户体验。

  2. 因为您在控制器级别存储了所请求产品的状态,所以如果用户能够分发两个单独的分接头,即每月快速连续点击,然后每年一次,则可能会出现错误禁用UI。这会弄乱你的代码。

  3. 您应该提前检查canMakePayments,以防止用户开始购买。

  4. 我会从productRequest:didReceiveResponse:中删除订单相关性。不要假设第0个产品是您要找的产品,请求所有产品并搜索您想要的产品。

  5. 最大的错误实施是您计算到期日期的方式。 Apple在收据信息中提供到期日期,这是获得订阅产品到期的正确方法。这意味着获取收据数据,将其发送到Apple(或理想情况下是您的后端)进行验证。事务队列观察器处理事务的日期与其实际购买日期不同。依赖到期日的收据消除了您的应用程序进行跟踪和到期计算的需要,这可能是此错误的根源。使用收据作为您订阅的真实来源将是让您的IAP代码与Apple相匹配的最佳方式。

  6. 我意识到&#34;重写你的IAP代码&#34;可能不是您正在寻找的答案,但这里有很多代码,这可能是StoreKit与您的应用之间的某些互动中的问题。你应该看一下we do IAP at RevenueCat的方式。这是一般性实现,但它应该让您了解如何与StoreKit正确交互。