2017-11-16 15 views
0

앱 구매시 어려움을 겪고있는 한 명의 사용자가 있습니다. 나는 앱 구매에서 60-65의 성공을 거두었지만이 사용자는 몇 차례 문제를 겪었습니다. 인앱 구매는 갱신하지 않는 구독으로 설정됩니다. 나는 그가 구독 기간이 만료 된 당일에 구매를 시도하면서 문제가 발생했다고 생각합니다. 그의 앱 구매 프로세스가 완료되지 않는 이유는 무엇입니까?앱 구매시 절대 완료하지 않음

다음은 그가 어떻게되는지 설명합니다.

단계별 설명. 1. 구독료가 입니다. 2. 갱신 을 클릭하십시오. 3. 비밀번호 추가 보호 및 요금 지불을 위해 비밀번호를 사용하고 요금을 지불합니다. 4. 앱에서 청구 상태가 허용되지만 앱을 사용하기 위해 클릭하면 지속적으로 구독 갱신을 요청합니다. (작성자 주 : 나는 그가 내가 등록한 날짜가 오늘 날짜보다 빠를 때를 위해 앱에 넣은 경고 컨트롤러를 언급하고 있다고 생각한다.) 그런 다음 다시 지불하려고 할 때 이미 앱 구입에서 말하고있다. 적어도 5 번 시도했습니다. 5. 다시 구매하려는 시도가 실패 할 때마다 앱이 다시로드 모드로 표시됩니다. 앱 중간에 사고 방식의 회전 바퀴가있어 설명하는 가장 좋은 방법입니다.

@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 [email protected]*****.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 [email protected]" } 

        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 [email protected]****.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 [email protected]*****.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 [email protected]*****.com", preferredStyle: .alert) 
        failedPurchaseAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in 
         self.activity.isHidden = true 
         self.activity.stopAnimating() 
        })) 

        break 
       } 
      } 
     } 
    } 

답변

1

클라이언트 측 버그의 원인이 될 수있는 잠재적 인 문제를 많이 거기에 코드를 검토 :

여기에 관련 코드입니다.

  1. UI를 사용자에게 표시하기 전에 SKProducts을 프리 페치하는 것이 좋습니다. 이렇게하면 구입을 시작하기 전에 가격을 표시 할 수 있습니다. 이는 제 의견으로는 좋은 UX입니다.

  2. 컨트롤러 수준에서 요청한 제품의 상태를 저장하기 때문에 사용자가 별도의 탭을 두 번 실행할 수있는 경우 버그가 발생할 수 있습니다. 예를 들어 매월 탭을 누른 다음 연례로 빠르게 탭할 수 있습니다 UI를 비활성화하십시오. 이것은 코드를 망칠 것입니다.

  3. canMakePayments을 먼저 검사해야 사용자가 구매를 시작할 수 없습니다.

  4. productRequest:didReceiveResponse:에서 주문 종속성을 제거합니다. 0 번째 제품이 찾고있는 제품이라고 가정하지 말고, 모든 제품을 요청하고 원하는 제품을 검색하십시오.

  5. 가장 큰 잘못 구현은 만료 날짜를 계산하는 방식입니다. Apple은 영수증 정보에 만료일을 제공합니다. 이것은 가입 제품의 만료를 얻는 적절한 방법입니다. 이것은 영수증 데이터를 가져 와서 확인을 위해 Apple (또는 이상적으로 백엔드)으로 보냅니다. 트랜잭션 대기열 옵저버가 트랜잭션을 처리 한 날짜와 실제 구매 날짜는 동일하지 않습니다. 만료일에 대한 영수증에 의존하기 때문에이 버그의 원인 일 가능성이있는 추적 및 만료 계산을 앱에서 수행 할 필요가 없습니다. 구독 영수증에 영수증을 사용하면 IAP 코드가 Apple의 코드와 일치하도록하는 가장 좋은 방법입니다. 내가 실현 "당신의 IAP 코드를 다시 작성"

은 아마 당신이 찾고 있던 답은 아니지만 그 가능성이 문제는 StoreKit와 응용 프로그램 사이에 상호 작용이 많은 코드는 여기입니다. 너는 어떻게 we do IAP at RevenueCat을보아야한다.일반적인 구현이지만 StoreKit과 올바르게 상호 작용하는 방법에 대한 아이디어를 제공해야합니다.

+0

고마워요! 귀하의 답변은 제 코드를 이해하는 데 매우 도움이되었습니다. 귀하의 응답을 사용하여 코드를 정리하고 더 많은 리소스를 찾고 모든 것을 다시 작성했습니다. 문제의 근본 원인은 구입 후 finishTransaction (transaction)을 호출하지 않았기 때문에 몇 가지 구매 항목이 대기열에 걸렸습니다. 이 응답은 다음을 도왔습니다. https://stackoverflow.com/questions/36090901/clearing-skpmentsmentsue-forcing-unfinished-transactions-to-finish –