我正在开发包含非消耗品的应用。我已经测试了多个沙箱帐户,并且在开发过程中一切正常。但在应用程序审核期间,productid被作为无效返回,我无法通过审核。在提交应用程序进行审核之前,我确保不消耗品处于“等待审核”状态。我也有xcode中的最新配置文件设置。我已经两次被拒绝,对于可能导致此问题的任何想法,将不胜感激。
答案 0 :(得分:0)
在appDelegate中,您将以下代码放置在appDelegate中,并导入svprogresshud pod来处理错误
let PRODUCT_ID = "your itunes bundal identifire for IN app purrchase"
func fetchProductPrice(){
InAppPurchaseManager.sharedManager.fetchProductPrice(productId: PRODUCT_ID) { (productInfo, price) in
print("price : \(price)")
}
}
InAppPurchaseManager
import UIKit
import StoreKit
import SVProgressHUD
var productTitle:String = ""
var productPrice:String = ""
struct InAppMessages {
static let kUserNotAuthorizedMessage = "You are not authorize to make purchase."
static let kProductNotAvailableMessage = "Product not available."
static let kErrorConnectingToServer = "Error in connecting to iTunes server."
static let kErrorInFetchProductInfoMessage = "Failed to get product information."
static let kProductPurchaseFailed = "Failed to purchase product.";
}
var iTuneURL = "https://buy.itunes.apple.com/verifyReceipt"
class InAppPurchaseManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
typealias PurchaseFailureHandler = (_ error : String) -> ()
typealias PurchaseSuccessHandler = (_ transcation : SKPaymentTransaction) -> ()
typealias ReceiptFailureHandler = (_ error : String) -> ()
typealias ReceiptSuccessHandler = (_ receiptData : AnyObject) -> ()
typealias RestoreSuccessHandler = (_ restoreProductID : [String]) -> ()
typealias RestoreFailureHandler = (_ error : String) -> ()
typealias ProductFetchHandler = (_ productInfo : String, _ price: String) -> ()
static let sharedManager = InAppPurchaseManager()
var purchaseFailureHandler : PurchaseFailureHandler?
var purchaseSuccessHandler : PurchaseSuccessHandler?
var receiptFailureHandler : ReceiptFailureHandler?
var receiptSuccessHandler : ReceiptSuccessHandler?
var restoreSuccessHandler : RestoreSuccessHandler?
var restoreFailureHandler : RestoreFailureHandler?
var productFetchHandler : ProductFetchHandler?
var productToBuy : SKProduct?
var productID : String?
// #if DEBUG
// let iTuneURL = "https://sandbox.itunes.apple.com/verifyReceipt"
// #else
// #endif
override init() {
super.init()
//Add Payment Queue Transaction Observer
SKPaymentQueue.default().add(self)
}
//==========================================
//MARK: - In App Purchase Handler Methods
//==========================================
func finishInterruptedTransactionsWithSuccess(_ successHandler :@escaping PurchaseSuccessHandler, _ failureHandler : @escaping PurchaseFailureHandler){
purchaseSuccessHandler = successHandler
purchaseFailureHandler = failureHandler
let currentQueue : SKPaymentQueue = SKPaymentQueue.default()
print("Transactions: \(currentQueue.transactions.count)")
for transaction in currentQueue.transactions {
if (transaction.transactionState == SKPaymentTransactionState.failed) {
//possibly handle the error
currentQueue.finishTransaction(transaction);
SVProgressHUD.dismiss()
purchaseFailureHandler?(InAppMessages.kProductPurchaseFailed)
purchaseFailureHandler = nil
} else if (transaction.transactionState == SKPaymentTransactionState.purchased) {
//deliver the content to the user
currentQueue.finishTransaction(transaction)
purchaseSuccessHandler?(transaction)
purchaseSuccessHandler = nil
} else {
//handle other transaction states
SVProgressHUD.dismiss()
purchaseFailureHandler?(InAppMessages.kProductPurchaseFailed)
purchaseFailureHandler = nil
}
}
}
func canPurchase() -> Bool{
return SKPaymentQueue.canMakePayments()
}
func buyProduct(){
// show progress
self.showMessageIndicator(message: "Please wait...")
//AppData.showProgressGIFImage()
let payment = SKPayment(product: self.productToBuy!)
SKPaymentQueue.default().add(payment);
}
func fetchProductPrice(productId: String, fetchHandler : @escaping ProductFetchHandler){
productFetchHandler = fetchHandler
productID = productId
if (SKPaymentQueue.canMakePayments())
{
let productID:NSSet = NSSet(object: productId);
let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
}
}
func purchaseProductWithProductId(productId : String, fetchHandler : @escaping ProductFetchHandler ,successHandler : @escaping PurchaseSuccessHandler, failureHandler : @escaping PurchaseFailureHandler){
purchaseSuccessHandler = successHandler
purchaseFailureHandler = failureHandler
productFetchHandler = fetchHandler
productID = productId
if (SKPaymentQueue.canMakePayments())
{
// show indicator
self.showMessageIndicator(message:"Getting Product Information...")
let productID:NSSet = NSSet(object: productId);
let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
}else{
SVProgressHUD.dismiss()
purchaseFailureHandler?(InAppMessages.kErrorConnectingToServer)
// show error
// self.showErrorAlert(error: InAppMessages.kErrorConnectingToServer)
}
}
func restoreProduct(successHandler : @escaping RestoreSuccessHandler, failureHandler : @escaping RestoreFailureHandler){
// show indicator
self.showMessageIndicator(message:"Restoring purchase...")
restoreSuccessHandler = successHandler
restoreFailureHandler = failureHandler
SKPaymentQueue.default().restoreCompletedTransactions()
}
//==========================================
//MARK: - SKPayment Request Delegate Methods
//==========================================
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print("resposen invalid product \(response.invalidProductIdentifiers)")
let count : Int = response.products.count
if (count>0) {
let validProduct: SKProduct = response.products[0]
if (validProduct.productIdentifier == productID) {
let numberFormatter = NumberFormatter()
numberFormatter.formatterBehavior = NumberFormatter.Behavior.behavior10_4
numberFormatter.numberStyle = NumberFormatter.Style.currency
numberFormatter.locale = validProduct.priceLocale
let formattedPrice = numberFormatter.string(from: validProduct.price)
print("formattedPrice: \(formattedPrice!)")
productPrice = formattedPrice!
hideIndicator()
self.productToBuy = validProduct
productTitle = validProduct.localizedTitle
//let productMessage = "Do you want to buy \(validProduct.localizedTitle) at \(formattedPrice!) amount?"
let productMessage = " Want to access premium content for only \(formattedPrice!) a month?"
productFetchHandler?(productMessage, formattedPrice!)
// buy product
//buyProduct(product: validProduct);
}
else {
SVProgressHUD.dismiss()
print(validProduct.productIdentifier)
purchaseFailureHandler?(InAppMessages.kProductNotAvailableMessage)
//self.showErrorAlert(error: InAppMessages.kProductNotAvailableMessage)
}
} else {
SVProgressHUD.dismiss()
purchaseFailureHandler?(InAppMessages.kProductNotAvailableMessage)
// show error
// self.showErrorAlert(error: InAppMessages.kProductNotAvailableMessage)
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("Error Fetching product information : \(error.localizedDescription)");
SVProgressHUD.dismiss()
purchaseFailureHandler?(InAppMessages.kErrorInFetchProductInfoMessage)
// show error alert
// self.showErrorAlert(error: InAppMessages.kErrorInFetchProductInfoMessage)
}
//============================================
//MARK: - SKPaymentTransactionObserver Methods
//============================================
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){
//print("Received Payment Transaction Response from Apple");
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
// hide indicator
self.hideIndicator()
//print("Product Purchased: \(transaction.transactionIdentifier)")
SKPaymentQueue.default().finishTransaction(transaction);
self.purchaseSuccessHandler?(transaction)
self.purchaseSuccessHandler = nil
//To Get Receipt from Bundle
// let mainBundle = Bundle.main as Bundle;
// let receiptUrl = mainBundle.appStoreReceiptURL;
//
// let data = NSData(contentsOf: receiptUrl!);
//
// if(data != nil){
//
// let base64String = data!.base64EncodedString()
//
// // verify payment receipt
// verifyPaymentReceipt(base64EncodedString: base64String)
// }
break;
case .failed:
SVProgressHUD.dismiss()
purchaseFailureHandler?(InAppMessages.kProductPurchaseFailed)
purchaseFailureHandler = nil
SKPaymentQueue.default().finishTransaction(transaction);
SVProgressHUD.dismiss()
//self.showErrorAlert(error: InAppMessages.kProductPurchaseFailed)
break;
case .purchasing:
self.showMessageIndicator(message: "Please wait \ntransaction in progress...")
print("Transaction is being added to the server queue.");
case .restored:
SKPaymentQueue.default().finishTransaction(transaction);
print("Already Purchased");
case .deferred:
print("The transaction is in the queue, but its final status is pending external action.");
}
}
}
func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("Restore Purchase done")
var productIds = [String]()
for transcation in queue.transactions{
productIds.append(transcation.payment.productIdentifier)
}
hideIndicator()
self.restoreSuccessHandler?(productIds)
self.restoreSuccessHandler = nil
self.restoreFailureHandler = nil
}
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error){
self.restoreFailureHandler?(error.localizedDescription)
self.restoreSuccessHandler = nil
self.restoreFailureHandler = nil
}
//============================================
//MARK: - Verify Receipt
//============================================
func verifyPaymentReceipt(base64EncodedString : String,ReceiptValidationPassword : String, receiptSuccess : ReceiptSuccessHandler?, receiptFailure : ReceiptFailureHandler?){
receiptSuccessHandler = receiptSuccess
receiptFailureHandler = receiptFailure
self.showMessageIndicator(message: "Verifying payment...")
// Create the JSON object that describes the request
let requestContents = NSMutableDictionary();
requestContents.setObject(base64EncodedString, forKey: "receipt-data" as NSCopying);
requestContents.setObject(ReceiptValidationPassword, forKey: "password" as NSCopying);
var requestData : NSData?
do{
requestData = try JSONSerialization.data(withJSONObject: requestContents, options: JSONSerialization.WritingOptions()) as NSData?;
}catch{
NSLog("Error in json data creation at verifyPaymentReceipt");
}
StoreDataCheck(requestData!)
}
func StoreDataCheck(_ requestData:NSData)
{
// Create a POST request with the receipt data.
let storeURL = NSURL(string: iTuneURL);
var storeRequest = URLRequest(url: storeURL! as URL);
storeRequest.httpMethod = "POST";
storeRequest.httpBody = requestData as Data?;
let session = URLSession(configuration: URLSessionConfiguration.default)
let dataTask = session.dataTask(with: storeRequest) { (responseData, response, error) in
// self.hideIndicator()
if (error == nil){
var jsonResponse: NSDictionary = NSDictionary()
do{
jsonResponse = try JSONSerialization.jsonObject(with: responseData!, options: JSONSerialization.ReadingOptions.allowFragments) as! NSDictionary
if jsonResponse.value(forKey: "status") as? Int == 21007
{
iTuneURL = "https://sandbox.itunes.apple.com/verifyReceipt"
self.StoreDataCheck(requestData)
}
else
{
self.receiptSuccessHandler?(jsonResponse)
}
}catch{
self.receiptFailureHandler?("Parsing issue : verifyPaymentReceipt")
}
}
else{
self.receiptFailureHandler?(error!.localizedDescription)
}
}
dataTask.resume()
}
//============================================
//MARK: - Alert / Activity Indicator
//============================================
func showIndicator() {
SVProgressHUD.show()
}
func showMessageIndicator(message: String) {
hideIndicator()
SVProgressHUD.show(withStatus: message)
}
func hideIndicator() {
SVProgressHUD.dismiss()
}
func showErrorAlert(error: String){
hideIndicator()
SVProgressHUD.showError(withStatus: error)
}
func showWithStatus(status: String){
hideIndicator()
SVProgressHUD.showInfo(withStatus: status)
}
}
并将此代码放入您的按钮操作中
func InApp()
{
SVProgressHUD.show()
if InAppPurchaseManager.sharedManager.canPurchase(){
InAppPurchaseManager.sharedManager.purchaseProductWithProductId(productId: PRODUCT_ID, fetchHandler: { (productInfoMessage, price) in
SVProgressHUD.dismiss()
InAppPurchaseManager.sharedManager.buyProduct()
print(productInfoMessage)
}, successHandler: { (transaction) in
self.purchaseSuccessful(transaction)
transectionID = transaction.transactionIdentifier!
}, failureHandler: { (error) in
print(error)
InAppPurchaseManager.sharedManager.hideIndicator()
)
InAppPurchaseManager.sharedManager.hideIndicator()
})
}else{
print( "Device is not able to make purchase. \n Please enable it from Settings -> General -> Restrictions -> In-App Purchases")
}
}
func purchaseSuccessful(_ transaction : SKPaymentTransaction) {
do {
if let receiptURL = Bundle.main.appStoreReceiptURL {
let data = try Data(contentsOf: receiptURL)
if data != nil {
InAppPurchaseManager.sharedManager.verifyPaymentReceipt(base64EncodedString: data.base64EncodedString(),
ReceiptValidationPassword: inAppPassword,
receiptSuccess: { (response) in
print("response \(response)")
if let receipt = response as? NSDictionary {
if let receiptInfo = receipt.object(forKey: "receipt" as NSCopying) as? NSDictionary{
let strOriginalPurchaseTime = receiptInfo.object(forKey: "receipt_creation_date_ms" as NSCopying) as! String
let timeInMS = TimeInterval(Double(strOriginalPurchaseTime)!)
let date = Date(timeIntervalSince1970: timeInMS / 1000)
purchaseDate = "\(date)"
print("date of purchase \(date)")
}
}
}, receiptFailure: { (error) in
print("error \(error)")
})
}
}
}catch {
}
}