Dji*_*rro 9 storekit in-app-purchase skproduct swift in-app-subscription
这是我第一次创建购买的经历。我正在开发的应用程序尚未发布。我一直在使用 Configuration.storekit 文件在本地测试订阅。一切正常。\n我最近遇到了一个问题 - 我的订阅不再显示在项目中。我在终端中收到如下错误:\n
更新:
\n我没有更改那些地方的代码。我尝试创建新的 .storekit 文件,但仍然不起作用。\n我尝试通过同步加载 .storekit 文件。在其中,价格被拉高并正确显示,与网站上一样,但在终端中再次写入相同的错误。
\n这是适用于购买的文件:
\nimport StoreKit\n\ntypealias RequestProductsResult = Result<[SKProduct], Error>\ntypealias PurchaseProductResult = Result<Bool, Error>\n\ntypealias RequestProductsCompletion = (RequestProductsResult) -> Void\ntypealias PurchaseProductCompletion = (PurchaseProductResult) -> Void\n\n\nclass Purchases: NSObject {\n static let `default` = Purchases()\n private let productIdentifiers = Set<String>(\n arrayLiteral: "test.1month", "test.6month", "test.12month"\n )\n\n private var products: [String: SKProduct]?\n private var productRequest: SKProductsRequest?\n private var productsRequestCallbacks = [RequestProductsCompletion]()\n fileprivate var productPurchaseCallback: ((PurchaseProductResult) -> Void)?\n \n \n func initialize(completion: @escaping RequestProductsCompletion) {\n requestProducts(completion: completion)\n }\n \n\n private func requestProducts(completion: @escaping RequestProductsCompletion) {\n guard productsRequestCallbacks.isEmpty else {\n productsRequestCallbacks.append(completion)\n return\n }\n\n productsRequestCallbacks.append(completion)\n\n let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers)\n productRequest.delegate = self\n productRequest.start()\n\n self.productRequest = productRequest\n }\n \n\n func purchaseProduct(productId: String, completion: @escaping (PurchaseProductResult) -> Void) {\n \n guard productPurchaseCallback == nil else {\n completion(.failure(PurchasesError.purchaseInProgress))\n return\n }\n \n guard let product = products?[productId] else {\n completion(.failure(PurchasesError.productNotFound))\n return\n }\n\n productPurchaseCallback = completion\n\n let payment = SKPayment(product: product)\n SKPaymentQueue.default().add(payment)\n }\n\n \n public func restorePurchases(completion: @escaping (PurchaseProductResult) -> Void) {\n guard productPurchaseCallback == nil else {\n completion(.failure(PurchasesError.purchaseInProgress))\n return\n }\n productPurchaseCallback = completion\n SKPaymentQueue.default().restoreCompletedTransactions()\n }\n}\n\n\nextension Purchases: SKProductsRequestDelegate {\n func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {\n guard !response.products.isEmpty else {\n print("Found 0 products")\n\n productsRequestCallbacks.forEach { $0(.success(response.products)) }\n productsRequestCallbacks.removeAll()\n return\n }\n\n var products = [String: SKProduct]()\n for skProduct in response.products {\n print("Found product: \\(skProduct.productIdentifier)")\n products[skProduct.productIdentifier] = skProduct\n }\n\n self.products = products\n\n productsRequestCallbacks.forEach { $0(.success(response.products)) }\n productsRequestCallbacks.removeAll()\n }\n\n func request(_ request: SKRequest, didFailWithError error: Error) {\n print("Failed to load products with error:\\n \\(error)")\n\n productsRequestCallbacks.forEach { $0(.failure(error)) }\n productsRequestCallbacks.removeAll()\n }\n}\n\n\nextension Purchases: SKPaymentTransactionObserver {\n func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {\n \n for transaction in transactions {\n switch transaction.transactionState {\n case .purchased, .restored:\n if finishTransaction(transaction) {\n SKPaymentQueue.default().finishTransaction(transaction)\n productPurchaseCallback?(.success(true))\n UserDefaults.setValue(true, forKey: "isPurchasedSubscription")\n } else {\n productPurchaseCallback?(.failure(PurchasesError.unknown))\n }\n \n case .failed:\n productPurchaseCallback?(.failure(transaction.error ?? PurchasesError.unknown))\n SKPaymentQueue.default().finishTransaction(transaction)\n \n default:\n break\n \n }\n }\n\n productPurchaseCallback = nil\n \n }\n}\n\n\nextension Purchases {\n func finishTransaction(_ transaction: SKPaymentTransaction) -> Bool {\n let productId = transaction.payment.productIdentifier\n print("Product \\(productId) successfully purchased")\n return true\n }\n}\n\nRun Code Online (Sandbox Code Playgroud)\n还有一个文件负责显示可用的订阅选项:
\n\n//\n// PremiumRatesTVC.swift\n// CalcYou\n//\n// Created by Admin on 29.08.2022.\n//\n\nimport StoreKit\nimport UIKit\n\nclass PremiumRatesTVC: UITableViewController {\n var oneMonthPrice = ""\n var sixMonthPrice = ""\n var twelveMonthPrice = ""\n \n @IBOutlet weak var oneMonthPriceLabel: UILabel!\n @IBOutlet weak var oneMothDailyPriceLabel: UILabel!\n \n @IBOutlet weak var sixMonthPriceLabel: UILabel!\n @IBOutlet weak var sixMonthDailyPriceLabel: UILabel!\n \n @IBOutlet weak var twelveMonthPriceLabel: UILabel!\n @IBOutlet weak var twelveMonthDailyPriceLabel: UILabel!\n \n @IBOutlet weak var tableViewCellOneMonth: UITableViewCell!\n @IBOutlet weak var tableViewCellSixMonth: UITableViewCell!\n @IBOutlet weak var tableViewCellTwelveMonth: UITableViewCell!\n \n \n @IBAction func cancelButton(_ sender: Any) {\n dismiss(animated: true, completion: nil)\n }\n \n \n // MARK: ViewDidLoad()\n override func viewDidLoad() {\n super.viewDidLoad()\n \n hideSubscriptions()\n navigationItem.title = "Premium PRO version"\n \n Purchases.default.initialize { [weak self] result in\n guard let self = self else { return }\n\n switch result {\n case let .success(products):\n guard products.count > 0 else {\n let message = "Failed to get a list of subscriptions. Please try again later."\n self.showMessage("Oops", withMessage: message)\n return\n \n }\n self.showSubscriptions()\n \n DispatchQueue.main.async {\n self.updateInterface(products: products)\n \n }\n \n default:\n break\n \n }\n }\n }\n \n \n // MARK: Functions()\n private func updateInterface(products: [SKProduct]) {\n updateOneMonth(with: products[0])\n updateSixMonth(with: products[1])\n updateTwelveMonth(with: products[2])\n }\n \n \n private func hideSubscriptions() {\n DispatchQueue.main.async {\n self.tableViewCellOneMonth.isHidden = true\n self.tableViewCellSixMonth.isHidden = true\n self.tableViewCellTwelveMonth.isHidden = true\n \n }\n }\n \n \n private func showSubscriptions() {\n DispatchQueue.main.async {\n self.tableViewCellOneMonth.isHidden = false\n self.tableViewCellSixMonth.isHidden = false\n self.tableViewCellTwelveMonth.isHidden = false\n \n }\n }\n \n \n func showMessage(_ title: String, withMessage message: String) {\n DispatchQueue.main.async {\n let alert = UIAlertController(title: title,\n message: message,\n preferredStyle: UIAlertController.Style.alert)\n let dismiss = UIAlertAction(title: "Ok",\n style: UIAlertAction.Style.default,\n handler: nil)\n \n alert.addAction(dismiss)\n self.present(alert, animated: true, completion: nil)\n }\n }\n\n \n override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {\n \n let storyboard = UIStoryboard(name: "Main", bundle: nil)\n \n if indexPath.section == 0 && indexPath.row == 0 {\n guard let premiumBuyVC = storyboard.instantiateViewController(identifier: "PremiumBuyVC") as? PremiumBuyVC else { return }\n \n premiumBuyVC.price = oneMonthPrice\n premiumBuyVC.productId = "1month"\n premiumBuyVC.period = "per month"\n show(premiumBuyVC, sender: nil)\n }\n \n if indexPath.section == 1 && indexPath.row == 0 {\n guard let premiumBuyVC = storyboard.instantiateViewController(identifier: "PremiumBuyVC") as? PremiumBuyVC else { return }\n \n premiumBuyVC.price = sixMonthPrice\n premiumBuyVC.productId = "6month"\n premiumBuyVC.period = "per 6 month"\n show(premiumBuyVC, sender: nil)\n }\n \n if indexPath.section == 2 && indexPath.row == 0 {\n guard let premiumBuyVC = storyboard.instantiateViewController(identifier: "PremiumBuyVC") as? PremiumBuyVC else { return }\n \n premiumBuyVC.price = twelveMonthPrice\n premiumBuyVC.productId = "12month"\n premiumBuyVC.period = "per 12 month"\n show(premiumBuyVC, sender: nil)\n }\n }\n}\n\n\nextension SKProduct {\n public var localizedPrice: String? {\n let numberFormatter = NumberFormatter()\n numberFormatter.locale = self.priceLocale\n numberFormatter.numberStyle = .currency\n return numberFormatter.string(from: self.price)\n }\n}\n\n\n// MARK: \xd0\x9e\xd0\xb1\xd0\xbd\xd0\xbe\xd0\xb2\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb5 \xd0\xb8\xd0\xbd\xd1\x84\xd0\xbe\xd1\x80\xd0\xbc\xd0\xb0\xd1\x86\xd0\xb8\xd0\xb8\n// \xd0\xb2 cell \xd0\xb4\xd0\xbb\xd1\x8f 1, 6, 12 \xd0\xbc\xd0\xb5\xd1\x81\xd1\x8f\xd1\x86\xd0\xb5\xd0\xb2\nextension PremiumRatesTVC {\n func updateOneMonth(with product: SKProduct) {\n let withCurrency = "\\(product.priceLocale.currencyCode ?? " ")"\n let daily = dailyPrice(from: Double(truncating: product.price), withMonth: 1.0)\n \n oneMonthPriceLabel.text = "\\(product.price) \\(withCurrency)"\n oneMothDailyPriceLabel.text = "\\(daily) \\(withCurrency)"\n oneMonthPrice = "\\(product.price) \\(withCurrency)"\n }\n \n func updateSixMonth(with product: SKProduct) {\n let withCurrency = "\\(product.priceLocale.currencyCode ?? " ")"\n let daily = dailyPrice(from: Double(truncating: product.price), withMonth: 6.0)\n \n sixMonthPriceLabel.text = "\\(product.price) \\(withCurrency)"\n sixMonthDailyPriceLabel.text = "\\(daily) \\(withCurrency)"\n sixMonthPrice = "\\(product.price) \\(withCurrency)"\n }\n\n func updateTwelveMonth(with product: SKProduct) {\n let withCurrency = "\\(product.priceLocale.currencyCode ?? " ")"\n let daily = dailyPrice(from: Double(truncating: product.price), withMonth: 12.0)\n \n twelveMonthPriceLabel.text = "\\(product.price) \\(withCurrency)"\n twelveMonthDailyPriceLabel.text = "\\(daily) \\(withCurrency)"\n twelveMonthPrice = "\\(product.price) \\(withCurrency)"\n }\n \n func dailyPrice(from value: Double, withMonth: Double) -> String {\n let days = withMonth * 30\n let result = value / days\n \n return String(format: "%.2f", result)\n }\n}\n\n\nRun Code Online (Sandbox Code Playgroud)\n此图显示了 testConfiguration.storekit 文件:
\n\n还有来自编辑方案的图像:
\n\n还有左侧菜单中带问号的 testConfiguration.storekit 文件。\n
我希望我详细且正确地描述了我遇到的问题。非常感谢所有抽出时间的人。
\n| 归档时间: |
|
| 查看次数: |
2258 次 |
| 最近记录: |