我到处都在搜索此内容,但是找不到我的问题的答案。这是场景:
我在App Store中有一个直播的游戏。我有x枚硬币的IAP消耗品。我为用户生成了一个促销代码,以获取一些免费的硬币。他在App Store中成功兑换了代码,但是当他打开我的游戏时,他的硬币没有被记入贷方。他尝试点击“恢复”按钮,重新启动设备,然后注销,然后使用他的Apple ID重新登录,但是没有任何效果。
我自己用另一个易用的促销代码试用了它,也无法使它正常工作。但是,我成功赎回了非消耗性的IAP。
所以我的问题是,我是否必须处理自己的代码中使用的消耗性促销代码?否则,用户如何兑换消费促销代码?
编辑:显示我的代码
GameStoreViewController.swift(从表视图中选择要购买的IAP)
@objc func tableView(_ tableView: UITableView!, didSelectRowAtIndexPath indexPath: IndexPath!) {
// First, make sure the user is authorised to make payments on his/her device
if IAPHelper.canMakePayments() {
// Then, make sure the product has been retrieved and placed in the _coinProducts array. If not, show alert
if _coinProducts.count > 0 {
activitySpinnerStart()
print("row \((indexPath as NSIndexPath).row) tapped")
let product = _coinProducts[(indexPath as NSIndexPath).row]
IAPList.store.buyProduct(product) // Purchasing the product. Fires productPurchased(notification:)
} else {
showAlertWith(Localization("NoConnectionAlertTitle"), message: Localization("NoConnectionAlertMessage"))
}
} else {
showAlertWith(Localization("NoConnectionAlertTitle"), message: Localization("NoIAPAllowedMessage"))
}
}
IAPHelper.swift:
import StoreKit
/// Notification that is generated when a product is purchased.
public let IAPHelperPurchaseNotification = "IAPHelperPurchaseNotification"
/// Notification that is generated when a transaction fails.
public let IAPHelperTransactionFailedNotification = "IAPHelperTransactionFailedNotification"
/// Notification that is generated when cannot retrieve IAPs from iTunes.
public let IAPHelperConnectionErrorNotification = "IAPHelperConnectionErrorNotification"
/// Notification that is generated when we need to stop the spinner.
public let IAPHelperStopSpinnerNotification = "IAPHelperStopSpinnerNotification"
/// Product identifiers are unique strings registered on the app store.
public typealias ProductIdentifier = String
/// Completion handler called when products are fetched.
public typealias ProductsRequestCompletionHandler = (_ success: Bool, _ products: [SKProduct]?) -> ()
/// A Helper class for In-App-Purchases.
open class IAPHelper : NSObject {
/// MARK: - User facing API
fileprivate let productIdentifiers: Set<ProductIdentifier>
fileprivate var purchasedProductIdentifiers = Set<ProductIdentifier>()
fileprivate var productsRequest: SKProductsRequest?
fileprivate var productsRequestCompletionHandler: ProductsRequestCompletionHandler?
// These two notifications are private, so are needed in addition to the public ones on line 12
//static let IAPHelperPurchaseNotification = "IAPHelperPurchaseNotification"
//static let IAPHelperTransactionFailedNotification = "IAPHelperTransactionFailedNotification"
/// Initialize the helper. Pass in the set of ProductIdentifiers supported by the app.
public init(productIds: Set<ProductIdentifier>) {
self.productIdentifiers = productIds
for productIdentifier in productIds {
let purchased = UserDefaults.standard.bool(forKey: productIdentifier)
if purchased {
purchasedProductIdentifiers.insert(productIdentifier)
print("Previously purchased: \(productIdentifier)")
} else {
print("Not purchased: \(productIdentifier)")
}
}
super.init()
SKPaymentQueue.default().add(self)
}
}
// MARK: - StoreKit API
extension IAPHelper {
public func requestProducts(_ completionHandler: @escaping ProductsRequestCompletionHandler) {
productsRequest?.cancel()
productsRequestCompletionHandler = completionHandler
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
productsRequest!.delegate = self
productsRequest!.start()
}
public func buyProduct(_ product: SKProduct) {
print("Buying \(product.productIdentifier)...")
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
}
public func isProductPurchased(_ productIdentifier: ProductIdentifier) -> Bool {
return purchasedProductIdentifiers.contains(productIdentifier)
}
public class func canMakePayments() -> Bool {
return SKPaymentQueue.canMakePayments()
}
public func restorePurchases() {
SKPaymentQueue.default().restoreCompletedTransactions()
}
// Adding functions from SA
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("Restore queue finished.")
NotificationCenter.default.post(name: Notification.Name(rawValue: IAPHelperStopSpinnerNotification), object: nil)
}
public func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
print("Restore queue failed.")
NotificationCenter.default.post(name: Notification.Name(rawValue: IAPHelperConnectionErrorNotification), object: nil)
}
}
// MARK: - SKProductsRequestDelegate
extension IAPHelper: SKProductsRequestDelegate {
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print("Loaded list of products...")
let products = response.products
productsRequestCompletionHandler?(true, products)
clearRequestAndHandler()
for p in products {
print("Found product: \(p.productIdentifier) \(p.localizedTitle) \(p.price.floatValue)")
}
}
public func request(_ request: SKRequest, didFailWithError error: Error) {
print("Failed to load list of products.")
print("Error: \(error.localizedDescription)")
productsRequestCompletionHandler?(false, nil)
NotificationCenter.default.post(name: Notification.Name(rawValue: IAPHelperConnectionErrorNotification), object: nil)
clearRequestAndHandler()
}
fileprivate func clearRequestAndHandler() {
productsRequest = nil
productsRequestCompletionHandler = nil
}
}
// MARK: - SKPaymentTransactionObserver
extension IAPHelper: SKPaymentTransactionObserver {
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch (transaction.transactionState) {
case .purchased:
completeTransaction(transaction)
break
case .failed:
failedTransaction(transaction)
break
case .restored:
restoreTransaction(transaction)
break
case .deferred:
break
case .purchasing:
break
}
}
}
fileprivate func completeTransaction(_ transaction: SKPaymentTransaction) {
print("completeTransaction...")
deliverPurchaseNotificationForIdentifier(transaction.payment.productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
}
fileprivate func restoreTransaction(_ transaction: SKPaymentTransaction) {
guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }
print("restoreTransaction... \(productIdentifier)")
deliverPurchaseNotificationForIdentifier(productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
}
fileprivate func failedTransaction(_ transaction: SKPaymentTransaction) {
print("failedTransaction...")
NotificationCenter.default.post(name: Notification.Name(rawValue: IAPHelperStopSpinnerNotification), object: nil)
if transaction.error!._code != SKError.paymentCancelled.rawValue {
print("Transaction Error: \(String(describing: transaction.error?.localizedDescription))")
NotificationCenter.default.post(name: Notification.Name(rawValue: IAPHelperTransactionFailedNotification), object: nil)
} else {
print("Transaction Error else statement")
}
SKPaymentQueue.default().finishTransaction(transaction)
NotificationCenter.default.post(name: Notification.Name(rawValue: IAPHelperTransactionFailedNotification), object: nil)
}
fileprivate func deliverPurchaseNotificationForIdentifier(_ identifier: String?) {
guard let identifier = identifier else { return }
purchasedProductIdentifiers.insert(identifier)
UserDefaults.standard.set(true, forKey: identifier)
UserDefaults.standard.synchronize()
NotificationCenter.default.post(name: Notification.Name(rawValue: IAPHelperPurchaseNotification), object: identifier)
}
}
IAPList.swift:
import Foundation
// Use enum as a simple namespace. (It has no cases so you can't instantiate it.)
public struct IAPList {
/// TODO: Change this to whatever you set on iTunes connect
fileprivate static let Prefix = "com.xxx.xxx."
/// MARK: - Supported Product Identifiers
/// List your IAPs here:
public static let PackOf4000Coins = Prefix + "4000Coins"
public static let PackOf10000Coins = Prefix + "10000Coins"
public static let PackOf30000Coins = Prefix + "30000Coins"
public static let PackOf75000Coins = Prefix + "75000Coins"
public static let PackOf175000Coins = Prefix + "175000Coins"
public static let RemoveAds = Prefix + "RemoveAds"
public static let PlayerEditor = Prefix + "PlayerEditor"
// All of the products assembled into a set of product identifiers.
fileprivate static let productIdentifiers: Set<ProductIdentifier> =
[IAPList.PackOf4000Coins, IAPList.PackOf10000Coins, IAPList.PackOf30000Coins,
IAPList.PackOf75000Coins, PackOf175000Coins, RemoveAds, PlayerEditor]
/// Static instance of IAPHelper.
public static let store = IAPHelper(productIds: IAPList.productIdentifiers)
}
/// Return the resource name for the product identifier.
func resourceNameForProductIdentifier(_ productIdentifier: String) -> String? {
return productIdentifier.components(separatedBy: ".").last
}