![](/img/trans.png)
[英]Testing In-App-Purchases in TestFlight/Sandbox purchase modal issue
[英]iOS In-App-Purchases don't work for non-sandbox accounts in TestFlight
我使用https://www.raywenderlich.com/5456-in-app-purchase-tutorial-getting-started 中的教程在我的應用程序中實現了應用內購買。 我沒有使用我自己的服務器來驗證購買或類似的東西。
我創建了一個沙箱用戶來測試代碼。 一切正常,但是,如果我嘗試使用我的個人 Apple ID 登錄,購買將失敗(在 TestFlight 上測試)。
這是預期的行為還是我做錯了什么? https://stackoverflow.com/a/37042040/11912101指出每個帳戶都應該能夠購買物品。
此外,我在 App Store Connect 中為沙盒用戶啟用了“中斷購買”選項。 下面的代碼將運行函數 failed(...),即使購買通過並且項目在我的應用程序中被解鎖並被添加到購買的產品標識符中。 有沒有辦法處理那些中斷的購買?
謝謝回答!
import StoreKit
public struct InAppPurchases {
static let proVersionID = "myapp.proversion"
private static let productIdentifiers: Set<ProductIdentifier> = [proVersionID]
public static let helper = InAppPurchaseHelper(productIds: InAppPurchases.productIdentifiers)
}
func resourceNameForProductIdentifier(_ productIdentifier: String) -> String? {
return productIdentifier.components(separatedBy: ".").last
}
public typealias ProductIdentifier = String
public typealias ProductsRequestCompletionHandler = (_ success: Bool, _ products: [SKProduct]?) -> Void
extension Notification.Name {
static let IAPHelperPurchaseNotification = Notification.Name("IAPHelperPurchaseNotification")
}
open class InAppPurchaseHelper: NSObject, ObservableObject {
private let productIdentifiers: Set<ProductIdentifier>
private var purchasedProductIdentifiers: Set<ProductIdentifier> = [] {
willSet {
self.objectWillChange.send()
}
}
private var productsRequest: SKProductsRequest?
private var productsRequestCompletionHandler: ProductsRequestCompletionHandler?
@Published var didFail = false
var availableProducts = [SKProduct]() {
willSet {
DispatchQueue.main.async {
self.objectWillChange.send()
}
}}
public init(productIds: Set<ProductIdentifier>) {
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)
reloadInAppPurchases()
}
func reloadInAppPurchases() {
DispatchQueue.main.async {
InAppPurchases.helper.requestProducts{ [weak self] success, products in
guard let self = self else { return }
if success {
self.availableProducts = products!
}
}
}
}
}
extension InAppPurchaseHelper {
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 func isProVersion () -> Bool {
if(isProductPurchased(InAppPurchases.proVersionID)) {
return true
}
return false
}
public class func canMakePayments() -> Bool {
return SKPaymentQueue.canMakePayments()
}
public func restorePurchases() {
SKPaymentQueue.default().restoreCompletedTransactions()
}
}
extension InAppPurchaseHelper: 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)
clearRequestAndHandler()
}
private func clearRequestAndHandler() {
productsRequest = nil
productsRequestCompletionHandler = nil
}
}
extension InAppPurchaseHelper: SKPaymentTransactionObserver {
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch (transaction.transactionState) {
case .purchased:
complete(transaction: transaction)
break
case .failed:
fail(transaction: transaction)
break
case .restored:
restore(transaction: transaction)
break
case .deferred:
break
case .purchasing:
break
@unknown default:
break
}
}
}
private func complete(transaction: SKPaymentTransaction) {
print("complete...")
deliverPurchaseNotificationFor(identifier: transaction.payment.productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
}
private func restore(transaction: SKPaymentTransaction) {
guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }
print("restore... \(productIdentifier)")
deliverPurchaseNotificationFor(identifier: productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
}
private func fail(transaction: SKPaymentTransaction) {
print("fail...")
if let transactionError = transaction.error as NSError?,
let localizedDescription = transaction.error?.localizedDescription,
transactionError.code != SKError.paymentCancelled.rawValue {
print("Transaction Error: \(localizedDescription)")
}
SKPaymentQueue.default().finishTransaction(transaction)
if(!isProVersion()){
didFail.toggle()
}
}
private func deliverPurchaseNotificationFor(identifier: String?) {
guard let identifier = identifier else { return }
purchasedProductIdentifiers.insert(identifier)
UserDefaults.standard.set(true, forKey: identifier)
NotificationCenter.default.post(name: .IAPHelperPurchaseNotification, object: identifier)
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.