#ios #swift #in-app-purchase #nsurl
#iOS #swift #покупка в приложении #nsurl
Вопрос:
Я только что отправил свое первое приложение, содержащее покупки в приложении, и оно было одобрено прошлой ночью. Я загрузил свое приложение и попытался купить покупку в приложении в целях тестирования, но, к моему удивлению, оно вылетает при нажатии кнопки, предлагающей опцию в приложении. Что более интересно, оно вылетает только при загрузке из Appstore, я удалил приложение и повторно загрузил его прямо со своего компьютера / XCode, и приложение не вылетело.
Есть ли вероятность, что URL-адрес был изменен, чтобы использовать отправитель для целей тестирования, когда приложение находилось на рассмотрении?
Это URL-адрес, который я использовал для производства:
let storeURL = NSURL(string: "https://buy.itunes.apple.com/verifyReceipt")
Это URL-адрес, который я использовал для тестирования, который был закомментирован при отправке в Appstore:
let storeURL = NSURL(string: "https:/sandbox.itunes.apple.com/verifyReceipt")
Опять же, есть ли вероятность, что URL-адрес был изменен в целях тестирования, когда приложение было проверено и оставило URL-адрес для тестирования?
Есть ли способ узнать, какой URL-адрес используется в настоящее время в Appstore?
Спасибо
ОТРЕДАКТИРОВАНО 18.10.16:
Вот код, который я использую… Может кто-нибудь быть таким добрым и быстро взглянуть на него, чтобы увидеть, что что-то не так, особенно accessPremiumFeature
метод, который вызывается при сбое происходит?
К вашему сведению — у меня все еще нет сведений об ошибке, потому что я не могу воспроизвести ее локально, а приложение было одобрено несколько часов назад.
AppDelegate.swif
import StoreKit
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var canPurchase:Bool = false
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if SKPaymentQueue.canMakePayments(){
self.canPurchase = true
IAPManager.sharedInstance.setupInAppPurchases()
}
return true
}
}
SettingsViewController.swift — вот где происходит сбой при accessPremiumFeature
вызове. Здесь что-то не так?
import StoreKit
class SettingsViewController: UIViewController {
@IBAction func accessPremiumFeature() {
if NSUserDefaults.standardUserDefaults().boolForKey("com.domain.appName"){
let alert = UIAlertController(title: "PRO-Version", message: "You already have the PRO version.", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}else{
var productInfo:SKProduct?
for product in IAPManager.sharedInstance.products{
productInfo = product as? SKProduct
}
let alertController = UIAlertController(title: "Premium Features", message: "Unlock all premium features for (productInfo!.price)." "This includes... bla, bla, bla...", preferredStyle: .Alert)
alertController.view.tintColor = UIColor.myRedColor()
let okAction = UIAlertAction(title: "Ok", style: .Default, handler: nil)
let buyAction = UIAlertAction(title: "Buy", style: .Default) { (action) -> Void in
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("StoreTableView") as! StoreTableViewController
self.presentViewController(vc, animated: true, completion: nil)
}
alertController.addAction(okAction)
alertController.addAction(buyAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
}
}
IAPManager.swift — это основной код покупки в приложении (Brain).
import StoreKit
// protocol to notify when user restores purchase
protocol IAPManagerDelegate {
func managerDidRestorePurchases()
}
class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver, SKRequestDelegate {
static let sharedInstance = IAPManager()
var request:SKProductsRequest!
var products:NSArray!
var delegate:IAPManagerDelegate?
//Load product identifiers for store usage
func setupInAppPurchases(){
self.validateProductIdentifiers(self.getProductIdentifiersFromMainBundle())
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
}
//Get product identifiers
func getProductIdentifiersFromMainBundle() -> NSArray {
var identifiers = NSArray()
if let url = NSBundle.mainBundle().URLForResource("iap_product_ids", withExtension: "plist"){
identifiers = NSArray(contentsOfURL: url)!
}
return identifiers
}
//Retrieve product information
func validateProductIdentifiers(identifiers:NSArray) {
let productIdentifiers = NSSet(array: identifiers as [AnyObject])
let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
self.request = productRequest
productRequest.delegate = self
productRequest.start()
}
func createPaymentRequestForProduct(product:SKProduct){
let payment = SKMutablePayment(product: product)
payment.quantity = 1
SKPaymentQueue.defaultQueue().addPayment(payment)
}
func verifyReceipt(transaction:SKPaymentTransaction?){
let receiptURL = NSBundle.mainBundle().appStoreReceiptURL!
if let receipt = NSData(contentsOfURL: receiptURL){
//Receipt exists
let requestContents = ["receipt-data" : receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))]
//Perform request
do {
let requestData = try NSJSONSerialization.dataWithJSONObject(requestContents, options: NSJSONWritingOptions(rawValue: 0))
//Build URL Request
let storeURL = NSURL(string: "https://buy.itunes.apple.com/verifyReceipt")// production URL
//let storeURL = NSURL(string: "https:/sandbox.itunes.apple.com/verifyReceipt") // Testing URL
let request = NSMutableURLRequest(URL: storeURL!)
request.HTTPMethod = "Post"
request.HTTPBody = requestData
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: { (responseData:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
do {
let json = try NSJSONSerialization.JSONObjectWithData(responseData!, options: .MutableLeaves) as! NSDictionary
print(json)
if (json.objectForKey("status") as! NSNumber) == 0 {
if let latest_receipt = json["latest_receipt_info"]{
self.validatePurchaseArray(latest_receipt as! NSArray)
} else {
let receipt_dict = json["receipt"] as! NSDictionary
if let purchases = receipt_dict["in_app"] as? NSArray{
self.validatePurchaseArray(purchases)
}
}
if transaction != nil {
SKPaymentQueue.defaultQueue().finishTransaction(transaction!)
}
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
self.delegate?.managerDidRestorePurchases()
})
} else {
//Debug the receipt
print(json.objectForKey("status") as! NSNumber)
}
} catch {
print(error)
}
})
task.resume()
} catch {
print(error)
}
} else {
//Receipt does not exist
print("No Receipt")
}
}
func validatePurchaseArray(purchases:NSArray){
for purchase in purchases as! [NSDictionary]{
self.unlockPurchasedFunctionalityForProductIdentifier(purchase["product_id"] as! String)
}
}
func unlockPurchasedFunctionalityForProductIdentifier(productIdentifier:String){
NSUserDefaults.standardUserDefaults().setBool(true, forKey: productIdentifier)
NSUserDefaults.standardUserDefaults().synchronize()
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
func lockPurchasedFunctionalityForProductIdentifier(productIdentifier:String){
NSUserDefaults.standardUserDefaults().setBool(false, forKey: productIdentifier)
NSUserDefaults.standardUserDefaults().synchronize()
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
//MARK: SKProductsRequestDelegate
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
self.products = response.products
print(self.products)
}
// MARK: SKPaymentTransactionObserver Protocol
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions as [SKPaymentTransaction]{
switch transaction.transactionState{
case .Purchasing:
print("Purchasing")
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
case .Deferred:
print("Deferrred")
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
case .Failed:
print("Failed")
print(transaction.error?.localizedDescription)
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
case.Purchased:
print("Purchased")
self.verifyReceipt(transaction)
case .Restored:
print("Restored")
}
}
}
func restorePurchases(){
let request = SKReceiptRefreshRequest()
request.delegate = self
request.start()
}
func requestDidFinish(request: SKRequest) {
self.verifyReceipt(nil)
}
}
Комментарии:
1. У вас есть журнал сбоев? Вы обозначили это символом? Знаете ли вы ошибку сбоя и строку, вызывающую сбой? Укажите всю эту информацию в своем вопросе.
2. У меня НЕТ ошибки сбоя, поскольку приложение было одобрено 2 часа назад. Что вы подразумеваете под symbolicate ?
3. Посмотрите в XCode, Organizer, сбой или подключите устройство к Mac и посмотрите в Devices, ваше устройство, «Просмотр журналов устройств»
Ответ №1:
Вы можете попробовать Charles HTTP Proxy и посмотреть, какой URL запрашивается, или открыть приложение с помощью шестнадцатеричного просмотра и выполнить поиск строки.
Комментарии:
1. Извините, но я запутался в двух ваших предложениях. Как использовать прокси? Что такое hex-viewer и как им пользоваться? Спасибо
2. HTTP-прокси только что установлен на вашем компьютере и активирован с пробной лицензией. Затем вы открываете сетевые настройки своего iPhone / iPad и вводите локальный IP-адрес в разделе HTTP Proxy. После этого вы видите каждый трафик своего телефона на компьютере и можете увидеть, какой URL-адрес используется для вашей покупки.
3. Шестнадцатеричный редактор используется для просмотра скомпилированных приложений. Просто откройте загруженное приложение на своем компьютере и попробуйте выполнить поиск по используемому URL.
4. @Fluchaa это будет работать так просто, как вы говорите в наши дни? Учитывая это, и все это SSL, поэтому ему нужно будет добавить неподписанный сертификат в свое хранилище ключей.
5. @toast Charles Proxy предлагает пользовательский профиль, который можно установить на ваш iPhone / iPad. Затем вы можете клонировать SSL-сертификат и отслеживать каждый HTTPS-трафик.