大部分答案都可以在 Apple 的文档中找到 here。但是存在差距,并且 Objective-c 代码正在使用已弃用的方法。
这段 Swift 3 代码展示了如何获取 App Receipt 并将其发送到应用商店进行验证。在保存所需数据之前,您绝对应该通过应用商店验证 App Receipt。要求应用商店进行验证的好处是,它会响应您可以轻松序列化为 JSON 的数据,并从那里提取您想要的键的值。不需要密码学。
正如 Apple 在该文档中描述的那样,首选流程是这样的......
device -> your trusted server -> app store -> your trusted server -> device
当应用商店返回您的服务器时,假设成功,您将在此处序列化并提取您需要的数据并按您的意愿保存。请参阅下面的 JSON。您可以将结果和您想要的任何其他内容发送回应用程序。
在下面的validateAppReceipt() 中,为了使其成为一个工作示例,它只是使用了这个流程......
device -> app store -> device
要使这项工作与您的服务器一起使用,只需将validationURLString 更改为指向您的服务器并将您需要的任何其他内容添加到requestDictionary。
要在开发中对此进行测试,您需要:
- 确保您在 itunesconnect 中设置了沙盒用户
- 在您的测试设备上退出 iTunes 和 App Store
- 在测试期间,出现提示时,使用您的沙盒用户
这是代码。幸福的道路流动得很好。错误和故障点只是打印或注释。根据需要处理那些。
这部分抓取应用收据。如果它不存在(在您测试时会发生),它会要求应用商店刷新。
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/
}
这部分验证应用收据。这不是本地验证。请参阅 cmets 中的注 1 和注 2。
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)")
}
}
你应该得到这样的结果。在您的情况下,这就是您将在服务器上使用的内容。
{
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;
}