我有一个用户在进行应用内购买时遇到了困难。我已经有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
}
}
}
}
答案 0 :(得分:3)
检查您的代码有很多可能导致客户端漏洞的潜在问题。
我建议在向用户显示UI之前预取SKProducts
。这允许您在开始购买之前显示价格,这在我看来是一个很好的用户体验。
因为您在控制器级别存储了所请求产品的状态,所以如果用户能够分发两个单独的分接头,即每月快速连续点击,然后每年一次,则可能会出现错误禁用UI。这会弄乱你的代码。
您应该提前检查canMakePayments
,以防止用户开始购买。
我会从productRequest:didReceiveResponse:
中删除订单相关性。不要假设第0个产品是您要找的产品,请求所有产品并搜索您想要的产品。
最大的错误实施是您计算到期日期的方式。 Apple在收据信息中提供到期日期,这是获得订阅产品到期的正确方法。这意味着获取收据数据,将其发送到Apple(或理想情况下是您的后端)进行验证。事务队列观察器处理事务的日期与其实际购买日期不同。依赖到期日的收据消除了您的应用程序进行跟踪和到期计算的需要,这可能是此错误的根源。使用收据作为您订阅的真实来源将是让您的IAP代码与Apple相匹配的最佳方式。
我意识到&#34;重写你的IAP代码&#34;可能不是您正在寻找的答案,但这里有很多代码,这可能是StoreKit
与您的应用之间的某些互动中的问题。你应该看一下we do IAP at RevenueCat的方式。这是一般性实现,但它应该让您了解如何与StoreKit
正确交互。