请指教!我正在尝试测试沙盒用户的应用内购买。 我正在关注帖子底部的教程。
目前的问题是通过func productsRequest中的response.invalidProductIdentifiers.count!= 0接收无效的产品标识符(_ request:SKProductsRequest,didReceive response:SKProductsResponse)。
我已查看设置 - >能力 - >应用程序内购买。
我在代码中使用的productId是: 让PREMIUM_PRODUCT_ID =" com.iaptutorial.premium"。 然后像这样调用它: // MARK: - FETCH可用的IAP产品 func fetchAvailableProducts(){
// Put here your IAP Products ID's
let productIdentifiers = NSSet(objects: PREMIUM_PRODUCT_ID )
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
}
我也无法在应用程序之前使用沙箱用户登录,但这不应该是一个问题:iTunes account creation not allowed when trying to test In-App Purchases
以下是教程: https://code.tutsplus.com/tutorials/in-app-purchase-tutorial-with-swift-3-ios-sdk--cms-27595 http://www.appcoda.com/in-app-purchase-tutorial/
我在真正的iPad 2 iOS 9.0上进行测试
我已阅读很多关于应用内购买问题的SO帖子,但没有任何效果。
如果需要,我可以在github上传项目。
问题是什么?谢谢
这是我的代码:
import UIKit
import StoreKit
class ViewController: UIViewController, SKProductsRequestDelegate,SKPaymentTransactionObserver {
@IBAction func actionPurchase(_ sender: Any) {
purchaseMyProduct(product: iapProducts[0])
}
@IBAction func actionRestore(_ sender: Any) {
}
@IBOutlet weak var premiumLabel: UILabel!
@IBOutlet weak var nonconsumableLabel: UILabel!
/* Variables */
let PREMIUM_PRODUCT_ID = "com.iaptutorial.premium"
var productID = ""
var productsRequest = SKProductsRequest()
var iapProducts = [SKProduct]()
var nonConsumablePurchaseMade = UserDefaults.standard.bool(forKey: "nonConsumablePurchaseMade")
override func viewDidLoad() {
// Check your In-App Purchases
print("NON CONSUMABLE PURCHASE MADE: \(nonConsumablePurchaseMade)")
if nonConsumablePurchaseMade {
premiumLabel.text = "Premium version PURCHASED!"
}
else {
premiumLabel.text = "Premium version LOCKED!"
}
// Fetch IAP Products available
fetchAvailableProducts()
}
// MARK: - FETCH AVAILABLE IAP PRODUCTS
func fetchAvailableProducts() {
// Put here your IAP Products ID's
let productIdentifiers = NSSet(objects: PREMIUM_PRODUCT_ID )
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
}
// MARK: - RESTORE NON-CONSUMABLE PURCHASE BUTTON
@IBAction func restorePurchaseButt(_ sender: Any) {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
nonConsumablePurchaseMade = true
UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")
UIAlertView(title: "IAP Tutorial",
message: "You've successfully restored your purchase!",
delegate: nil, cancelButtonTitle: "OK").show()
}
// MARK: - REQUEST IAP PRODUCTS
func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {
print("inside productsRequest")
if (response.products.count > 0) {
iapProducts = response.products
/*
// 1st IAP Product (Consumable) ------------------------------------
let firstProduct = response.products[0] as SKProduct
// Get its price from iTunes Connect
let numberFormatter = NumberFormatter()
numberFormatter.formatterBehavior = .behavior10_4
numberFormatter.numberStyle = .currency
numberFormatter.locale = firstProduct.priceLocale
let price1Str = numberFormatter.string(from: firstProduct.price)
// Show its description
consumableLabel.text = firstProduct.localizedDescription + "\nfor just \(price1Str!)"
// ------------------------------------------------
*/
/*
// 2nd IAP Product (Non-Consumable) ------------------------------
if response.products.count > 0{
print("inside")
let secondProd = response.products[0] as SKProduct
// Get its price from iTunes Connect
let numberFormatter = NumberFormatter()
numberFormatter.locale = secondProd.priceLocale
let price2Str = numberFormatter.string(from: secondProd.price)
nonconsumableLabel.text = secondProd.localizedDescription + " for just \(price2Str!)"
}
*/
}
if response.invalidProductIdentifiers.count != 0 {
print("invalid")
print(response.invalidProductIdentifiers.description)
}
}
// MARK: - MAKE PURCHASE OF A PRODUCT
func canMakePurchases() -> Bool { return SKPaymentQueue.canMakePayments() }
func purchaseMyProduct(product: SKProduct) {
print("inside purchasemyproduct")
if self.canMakePurchases() {
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment)
print("PRODUCT TO PURCHASE: \(product.productIdentifier)")
productID = product.productIdentifier
// IAP Purchases dsabled on the Device
} else {
UIAlertView(title: "IAP Tutorial",
message: "Purchases are disabled in your device!",
delegate: nil, cancelButtonTitle: "OK").show()
}
}
// MARK:- IAP PAYMENT QUEUE
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print ("inside paymentqueue")
for transaction:AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
// The Consumable product (10 coins) has been purchased -> gain 10 extra coins!
if productID == PREMIUM_PRODUCT_ID {
// Save your purchase locally (needed only for Non-Consumable IAP)
nonConsumablePurchaseMade = true
UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")
premiumLabel.text = "Premium version PURCHASED!"
UIAlertView(title: "IAP Tutorial",
message: "You've successfully unlocked the Premium version!",
delegate: nil,
cancelButtonTitle: "OK").show()
}
break
case .failed:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
break
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
break
default: break
}}}
}
}