草庐IT

swift - IAP 实际验证收据 (Swift)

coder 2023-09-08 原文

我一直在尝试在我的 spritekit 游戏中实现收据验证。我一直在关注各种教程,基本上以这段代码结束

enum RequestURL: String {
   case production = "https://buy.itunes.apple.com/verifyReceipt"
   case sandbox = "https://sandbox.itunes.apple.com/verifyReceipt"
   case myServer = "my server address"
}

enum ReceiptStatusCode: Int {

// Not decodable status
case unknown = -2

// No status returned
case none = -1

// valid status
case valid = 0

// The App Store could not read the JSON object you provided.
case JSONNotReadable = 21000

// The data in the receipt-data property was malformed or missing.
case malformedOrMissingData = 21002

// The receipt could not be authenticated.
case receiptCouldNotBeAuthenticated = 21003

// The shared secret you provided does not match the shared secret on file for your account.
// Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
case sharedSecretNotMatching = 21004

// The receipt server is currently not available.
case receiptServerUnavailable = 21005

// This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response.
// Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
case subscriptionExpired = 21006

//  This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.
case testReceipt = 21007

// This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.
case productionEnvironment = 21008
 }

  func validateReceipt(forTransaction transaction: SKPaymentTransaction) {

    guard let receiptURL = NSBundle.mainBundle().appStoreReceiptURL else { return }

    guard let receipt = NSData(contentsOfURL: receiptURL) else { return }

    let receiptData = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
    let payload = ["receipt-data": receiptData]

    var receiptPayloadData: NSData?

    do {
        receiptPayloadData = try NSJSONSerialization.dataWithJSONObject(payload, options: NSJSONWritingOptions(rawValue: 0))
    }
    catch let error as NSError {
        print(error.localizedDescription)
        return
    }

    guard let payloadData = receiptPayloadData else { return }
    guard let requestURL = NSURL(string: RequestURL.sandbox.rawValue) else { return }

    let request = NSMutableURLRequest(URL: requestURL)
    request.HTTPMethod = "POST"
    request.HTTPBody = payloadData

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) in
         if let error = error {
            print(error.localizedDescription)
            return
        }  
         guard let data = data else { return }          

         do {
            let jsonData = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as? NSDictionary

            guard let json = jsonData else { return }

            // Correct ?
            guard let status = json["status"] as? Int where status == ReceiptStatusCode.valid.rawValue else { return }

            // Unlock product here?
            // Other checks needed?
        }

        catch let error as NSError {
            print(error.localizedDescription)
            return
        }
     }

    task.resume()
}

这是漂亮的样板代码,可以按预期工作。我现在的问题是我不知道如何在最后一步(标记线)实际验证收据。 我想我现在必须携带 5 张左右的支票来验证收据。我只是不知道它们中的大部分是如何快速完成的。大多数教程要么是旧的,不包括此步骤,要么不是用 swift 编写的。

如果任何成功使用收据验证的人可以帮助我朝着正确的方向前进,我们将不胜感激。非常感谢

更新:

在 JSA986I 和 cbartel 给出了很好的答案后,我把它变成了 github 上的一个帮助程序。非常感谢您的帮助

https://github.com/crashoverride777/SwiftyReceiptValidator

最佳答案

这是在 Apple guide 之后使用 Swift 2.1 版的解决方案.

苹果也recommends the following :

When validating receipts on your server, your server needs to be able to handle a production-signed app getting its receipts from Apple’s test environment. The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code “Sandbox receipt used in production”, validate against the test environment instead.

validateReceipt(NSBundle.mainBundle().appStoreReceiptURL) { (success: Bool) -> Void in
            print(success)
        }

private func receiptData(appStoreReceiptURL : NSURL?) -> NSData? {

    guard let receiptURL = appStoreReceiptURL,
        receipt = NSData(contentsOfURL: receiptURL) else {
            return nil
    }

    do {
        let receiptData = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
        let requestContents = ["receipt-data" : receiptData]
        let requestData = try NSJSONSerialization.dataWithJSONObject(requestContents, options: [])
        return requestData
    }
    catch let error as NSError {
        print(error)
    }

    return nil
}

private func validateReceiptInternal(appStoreReceiptURL : NSURL?, isProd: Bool , onCompletion: (Int?) -> Void) {

    let serverURL = isProd ? "https://buy.itunes.apple.com/verifyReceipt" : "https://sandbox.itunes.apple.com/verifyReceipt"

    guard let receiptData = receiptData(appStoreReceiptURL),
        url = NSURL(string: serverURL)  else {
        onCompletion(nil)
        return
    }

    let request = NSMutableURLRequest(URL: url)
    request.HTTPMethod = "POST"
    request.HTTPBody = receiptData

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in

        guard let data = data where error == nil else {
            onCompletion(nil)
            return
        }

        do {
            let json = try NSJSONSerialization.JSONObjectWithData(data, options:[])
            print(json)
            guard let statusCode = json["status"] as? Int else {
                onCompletion(nil)
                return
            }
            onCompletion(statusCode)
        }
        catch let error as NSError {
            print(error)
            onCompletion(nil)
        }
    })
    task.resume()
}

public func validateReceipt(appStoreReceiptURL : NSURL?, onCompletion: (Bool) -> Void) {

    validateReceiptInternal(appStoreReceiptURL, isProd: true) { (statusCode: Int?) -> Void in
        guard let status = statusCode else {
            onCompletion(false)
            return
        }

        // This receipt is from the test environment, but it was sent to the production environment for verification.
        if status == 21007 {
            self.validateReceiptInternal(appStoreReceiptURL, isProd: false) { (statusCode: Int?) -> Void in
                guard let statusValue = statusCode else {
                    onCompletion(false)
                    return
                }

                // 0 if the receipt is valid
                if statusValue == 0 {
                    onCompletion(true)
                } else {
                    onCompletion(false)
                }

            }

        // 0 if the receipt is valid
        } else if status == 0 {
            onCompletion(true)
        } else {
            onCompletion(false)
        }
    }
}

关于swift - IAP 实际验证收据 (Swift),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32291701/

有关swift - IAP 实际验证收据 (Swift)的更多相关文章

  1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  4. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  5. ruby-on-rails - 如何将验证与模型分开 - 2

    我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:

  6. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  7. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  8. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  9. ruby - 是否可以在不实际发送或读取数据的情况下查明 ruby​​ 套接字是否处于 ESTABLISHED 或 CLOSE_WAIT 状态? - 2

    s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成

  10. ruby-on-rails - ruby on rails 模型验证中的浮点精度 - 2

    我正在尝试使用正则表达式验证美元金额:^[0-9]+\.[0-9]{2}$这工作正常,但每当用户提交表单并且美元金额以0(零)结尾时,ruby(或rails?)将0砍掉。所以500.00变成500.0,因此正则表达式验证失败。有没有办法让ruby​​/rails保持用户输入的格式,而不管尾随零? 最佳答案 我假设您的美元金额是小数类型。因此,用户在字段中输入的任何值在保存到数据库之前都会从字符串转换为适当的类型。验证适用于已转换为数字类型的值,因此在您的情况下,正则表达式并不是真正合适的验证过滤器。不过,您有几种可能性可以解决这个问

随机推荐