iOS测试App Receipt验证 [英] iOS test App Receipt Validation

查看:1148
本文介绍了iOS测试App Receipt验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有很多关于如何使用沙盒测试程序帐户测试应用程序内购买收据验证的示例。

There is a lot of example about how to test in-app purchase receipt validation by using a sandbox tester account.

但付费应用程序的收据如何?本身?我们怎样才能在开发环境中获得App Receipt?

But how is the Receipt for the paid App itself? How can we get the App Receipt in development environment?

我想做两件事:


  • 防止非购买应用程序的用户运行我们的应用程序的非法复制。
    正如我所看到的那个检测到连接的iTune帐户的应用程序没有拥有该应用程序(它向用户显示警告他们没有拥有该应用程序,但是他们无法阻止用户继续使用该应用程序)

  • To prevent illegal copy of our app running by the user who didn't purchase the app. As I have seen app that detected the iTune Account was connected doesn't owned the app (it shows warning to the user they didn't own the app, but they fail to stop the user to continue to use the app)

将应用购买收据发送到我们的服务器。我们想知道他们什么时候购买我们的应用程序,他们带来了什么版本的应用程序。

Send the app purchase receipt to our server. We want to know when do they buy our app, what version of app they brought.

推荐答案

答案的大部分内容都可以找到此处。但是存在差距,而Objective-c代码正在使用弃用的方法。

Most parts of the answer can be found here in Apple's documentation. But there are gaps and the objective-c code is using deprecated methods.

此Swift 3代码显示如何获取App Receipt并将其发送到应用商店进行验证。在保存所需数据之前,您绝对应该使用应用商店验证App Receipt。要求应用商店验证的好处是,它可以使用您可以轻松序列化为JSON的数据进行响应,并从中拉出所需键的值。无需加密。

This Swift 3 code shows how to get the App Receipt and send it to the app store for validation. You should definitely validate the App Receipt with the app store before saving the data you want. The advantage of asking the app store to validate is that it responds with data that you can easily serialize to JSON and from there pull out the values for the keys you want. No cryptography required.

正如Apple在该文档中所述,首选流程是这样的......

As Apple describes in that documentation the preferred flow is like this...

device -> your trusted server -> app store -> your trusted server -> device

当应用程序商店返回到您的服务器时,假设成功,那么您将序列化并拉动输出您需要的数据并根据需要保存。请参阅下面的JSON。您可以将结果和其他任何内容发送回应用程序。

When the app store returns to your server, assuming success, that's where you'll serialize and pull out the data you require and save it as you wish. See the JSON below. And you can send the result and whatever else you want back to the app.

在下面的 validateAppReceipt()中,为了使它成为一个有效的例子,它只是使用这个流...

In validateAppReceipt() below, to make it a working example, it simply uses this flow...

device -> app store -> device

要使用此功能,只需更改 validationURLString 指向您的服务器,并将您需要的任何内容添加到 requestDictionary

To make this work with your server just change validationURLString to point to your server and add whatever else your require to requestDictionary.

要在开发中测试这个,你需要:

To test this in development you need to:


  • 确保你有您在测试设备上的itunesconnect

  • 中设置的沙盒用户退出iTunes& App Store

  • 在测试期间,出现提示时,使用您的沙盒用户

这是代码。快乐的道路流得很好。错误和故障点只是打印或评论。根据您的需要处理这些问题。

Here's the code. The happy path flows just fine. Errors and failure points just print or are commented. Deal with those as you require.

此部分抓取应用收据。如果不存在(在测试时会发生),它会要求应用商店刷新。

This part grabs the app receipt. If it's not there (which will happen when you are testing) it asks the app store to refresh.

let receiptURL = Bundle.main.appStoreReceiptURL

func getAppReceipt() {
    guard let receiptURL = receiptURL else {  /* receiptURL is nil, it would be very weird to end up here */  return }
    do {
        let receipt = try Data(contentsOf: receiptURL)
        validateAppReceipt(receipt)
    } catch {
        // there is no app receipt, don't panic, ask apple to refresh it
        let appReceiptRefreshRequest = SKReceiptRefreshRequest(receiptProperties: nil)
        appReceiptRefreshRequest.delegate = self
        appReceiptRefreshRequest.start()
        // If all goes well control will land in the requestDidFinish() delegate method.
        // If something bad happens control will land in didFailWithError.
    }
}

func requestDidFinish(_ request: SKRequest) {
    // a fresh receipt should now be present at the url
    do {
        let receipt = try Data(contentsOf: receiptURL!) //force unwrap is safe here, control can't land here if receiptURL is nil
        validateAppReceipt(receipt)
    } catch {
        // still no receipt, possible but unlikely to occur since this is the "success" delegate method
    }
}

func request(_ request: SKRequest, didFailWithError error: Error) {
    print("app receipt refresh request did fail with error: \(error)")
    // for some clues see here: https://samritchie.net/2015/01/29/the-operation-couldnt-be-completed-sserrordomain-error-100/
}

此部分验证应用收据。这不是本地验证。请参阅注释中的注1和注2。

This part validates the app receipt. This is not local validation. Refer to Note 1 and Note 2 in the comments.

func validateAppReceipt(_ receipt: Data) {

    /*  Note 1: This is not local validation, the app receipt is sent to the app store for validation as explained here:
            https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1
        Note 2: Refer to the url above. For good reasons apple recommends receipt validation follow this flow:
            device -> your trusted server -> app store -> your trusted server -> device
        In order to be a working example the validation url in this code simply points to the app store's sandbox servers.
        Depending on how you set up the request on your server you may be able to simply change the 
        structure of requestDictionary and the contents of validationURLString.
    */
    let base64encodedReceipt = receipt.base64EncodedString()
    let requestDictionary = ["receipt-data":base64encodedReceipt]
    guard JSONSerialization.isValidJSONObject(requestDictionary) else {  print("requestDictionary is not valid JSON");  return }
    do {
        let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
        let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt"  // this works but as noted above it's best to use your own trusted server
        guard let validationURL = URL(string: validationURLString) else { print("the validation url could not be created, unlikely error"); return }
        let session = URLSession(configuration: URLSessionConfiguration.default)
        var request = URLRequest(url: validationURL)
        request.httpMethod = "POST"
        request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
        let task = session.uploadTask(with: request, from: requestData) { (data, response, error) in
            if let data = data , error == nil {
                do {
                    let appReceiptJSON = try JSONSerialization.jsonObject(with: data)
                    print("success. here is the json representation of the app receipt: \(appReceiptJSON)")
                    // if you are using your server this will be a json representation of whatever your server provided
                } catch let error as NSError {
                    print("json serialization failed with error: \(error)")
                }
            } else {
                print("the upload task returned an error: \(error)")
            }
        }
        task.resume()
    } catch let error as NSError {
        print("json serialization failed with error: \(error)")
    }
}

你应该最终得到这样的东西。在您的情况下,这是您将在服务器上使用的。

You should end up with something like this. In your case this is what you would be working with on your server.

{
    environment = Sandbox;
    receipt =     {
        "adam_id" = 0;
        "app_item_id" = 0;
        "application_version" = "0";  // for me this was showing the build number rather than the app version, at least in testing
        "bundle_id" = "com.yourdomain.yourappname";  // your app's actual bundle id
        "download_id" = 0;
        "in_app" =         (
        );
        "original_application_version" = "1.0"; // this will always return 1.0 when testing, the real thing in production.
        "original_purchase_date" = "2013-08-01 07:00:00 Etc/GMT";
        "original_purchase_date_ms" = 1375340400000;
        "original_purchase_date_pst" = "2013-08-01 00:00:00 America/Los_Angeles";
        "receipt_creation_date" = "2016-09-21 18:46:39 Etc/GMT";
        "receipt_creation_date_ms" = 1474483599000;
        "receipt_creation_date_pst" = "2016-09-21 11:46:39 America/Los_Angeles";
        "receipt_type" = ProductionSandbox;
        "request_date" = "2016-09-22 18:37:41 Etc/GMT";
        "request_date_ms" = 1474569461861;
        "request_date_pst" = "2016-09-22 11:37:41 America/Los_Angeles";
        "version_external_identifier" = 0;
    };
    status = 0;
}

这篇关于iOS测试App Receipt验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆