在 App Purchase 中使用受信任的服务器进行收据验证。为什么我没有从服务器获得最新收据有什么想法吗?
In App Purchase receipt validation with trusted server. Any ideas why I am not getting the latest receipt from the server?
我正在 swift 中开发一个 iOS 应用程序,它使用 In App Purchase。我只有一种产品,它是自动续订订阅,还提供 7 天试用期。根据我找到的大部分文档,首先 Apple docs,要遵循的最佳策略是使用受信任的服务器进行收据验证。我已经在我的项目和我信任的服务器上设置了所有内容,现在通信正常,至少没有错误。我在应用程序中的代码如下:
func performServerToServerValidation() {
guard let receiptUrl = Bundle.main.appStoreReceiptURL,
let receiptData = try? Data(contentsOf: receiptUrl)
else {
return
}
let baseUrlString = "https://<my.trusted.server>/validation/verifyReceipt.php"
let theRequest = AFHTTPRequestSerializer().request(withMethod: "POST", urlString: baseUrlString, parameters: nil, error: nil)
theRequest.timeoutInterval = 20.0
theRequest.httpBody = receiptData.base64EncodedString().data(using: String.Encoding.utf8)
operation = AFHTTPRequestOperation(request: theRequest as URLRequest)
operation?.responseSerializer = AFHTTPResponseSerializer()
var dict: Dictionary<String,Any>!
operation?.setCompletionBlockWithSuccess({ operation, responseObject in
if let responseObject = responseObject as? Data {
do {
dict = try JSONSerialization.jsonObject(with: responseObject, options: JSONSerialization.ReadingOptions.mutableContainers) as? Dictionary<String, Any>
print("Finished connection with the trusted server")
if let receiptBase64String = dict["latest_receipt"] as? String {
let decodedData = Data(base64Encoded: receiptBase64String)!
let newReceipt = Receipt.init(data: decodedData)
print("\(newReceipt!)")
self.validateReceipt(receipt: newReceipt)
self.analyzeInAppPurchasesInReceipt()
}
} catch {
}
}
})
// 5
operation?.start()
}
我在服务器上的代码如下:
<?php
function getReceiptData($receipt)
{
$fh = fopen('showme.txt',w);
fwrite($fh,$receipt);
fclose($fh);
$endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt';
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $receipt);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$errmsg = curl_error($ch);
curl_close($ch);
$msg = $response.' - '.$errno.' - '.$errmsg;
echo $response;
}
foreach ($_POST as $key=>$value){
$newcontent .= $key.' '.$value;
}
$new = trim($newcontent);
$new = trim($newcontent);
$new = str_replace('_','+',$new);
$new = str_replace(' =','==',$new);
if (substr_count($new,'=') == 0){
if (strpos('=',$new) === false){
$new .= '=';
}
}
$new = '{"receipt-data":"'.$new.'","password":"<my_shared_secret>"}';
$info = getReceiptData($new);
?>
现在我的问题来了。我想利用中间服务器,例如通过使用服务器验证来确定用户是否已在另一台设备上订阅。我的测试流程如下:
1) 我有两个设备,A 和 B。
2) 我从两台设备上删除了任何现有的应用程序版本。好的。
3) 我在 iTunesConnect 上创建了一个新的沙箱用户,并使用它在设置中登录两台设备的沙箱帐户。好的
4) 我在设备 A 上从 Xcode 启动应用程序,因为这是第一次启动它没有收据,所以它通过 SKRefreshReceiptRequest 获得了一个。好的
5) 我在设备 B 上从 Xcode 启动应用程序,但它也没有收据,因为它是第一次启动,所以它通过 SKRefreshReceiptRequest 获得了一个。好的
6) 我在设备 B 上购买了订阅。功能会立即交付。一切正常
7) 现在,我在设备 A 上再次启动该应用程序。它有一张收据(我们在第 4 点启动它))但该收据已过时,因为它不包括我在设备 B 上购买的产品。调用了 PerformServerToServerValidation,但不幸的是,返回的 JSON 不包含 "latest_receipt" 字段。它只包含 "receipt"、"status" 和 "environment" 键:
所以我无法确定是否已经在另一台设备上购买了订阅。我一定遗漏了一些重要的东西,但服务器收据验证的优势之一不应该是始终拥有最新的收据吗?要知道的一件重要事情是,如果我恢复购买或尝试再次购买,那么,如果我调用服务器验证代码,它会完美运行并具有 "latest_receipt" 密钥。但这一切的意义何在?我想使用服务器验证来避免强制用户进行恢复购买或再次购买商品,即使没有收费。感谢您的帮助。
服务器端收据验证的目的是防止用户输入伪造的收据。服务器只是验证您发送给它的收据。它不会使用您发送的收据来检查是否有以后可用的相应收据。
您描述的流程,即您需要在设备 B 上恢复购买的流程,是预期的流程。
您可能会将服务器端收据验证与服务器通知混淆,Apple 可以向您的服务器发送通知subscription status updates
我正在 swift 中开发一个 iOS 应用程序,它使用 In App Purchase。我只有一种产品,它是自动续订订阅,还提供 7 天试用期。根据我找到的大部分文档,首先 Apple docs,要遵循的最佳策略是使用受信任的服务器进行收据验证。我已经在我的项目和我信任的服务器上设置了所有内容,现在通信正常,至少没有错误。我在应用程序中的代码如下:
func performServerToServerValidation() {
guard let receiptUrl = Bundle.main.appStoreReceiptURL,
let receiptData = try? Data(contentsOf: receiptUrl)
else {
return
}
let baseUrlString = "https://<my.trusted.server>/validation/verifyReceipt.php"
let theRequest = AFHTTPRequestSerializer().request(withMethod: "POST", urlString: baseUrlString, parameters: nil, error: nil)
theRequest.timeoutInterval = 20.0
theRequest.httpBody = receiptData.base64EncodedString().data(using: String.Encoding.utf8)
operation = AFHTTPRequestOperation(request: theRequest as URLRequest)
operation?.responseSerializer = AFHTTPResponseSerializer()
var dict: Dictionary<String,Any>!
operation?.setCompletionBlockWithSuccess({ operation, responseObject in
if let responseObject = responseObject as? Data {
do {
dict = try JSONSerialization.jsonObject(with: responseObject, options: JSONSerialization.ReadingOptions.mutableContainers) as? Dictionary<String, Any>
print("Finished connection with the trusted server")
if let receiptBase64String = dict["latest_receipt"] as? String {
let decodedData = Data(base64Encoded: receiptBase64String)!
let newReceipt = Receipt.init(data: decodedData)
print("\(newReceipt!)")
self.validateReceipt(receipt: newReceipt)
self.analyzeInAppPurchasesInReceipt()
}
} catch {
}
}
})
// 5
operation?.start()
}
我在服务器上的代码如下:
<?php
function getReceiptData($receipt)
{
$fh = fopen('showme.txt',w);
fwrite($fh,$receipt);
fclose($fh);
$endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt';
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $receipt);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$errmsg = curl_error($ch);
curl_close($ch);
$msg = $response.' - '.$errno.' - '.$errmsg;
echo $response;
}
foreach ($_POST as $key=>$value){
$newcontent .= $key.' '.$value;
}
$new = trim($newcontent);
$new = trim($newcontent);
$new = str_replace('_','+',$new);
$new = str_replace(' =','==',$new);
if (substr_count($new,'=') == 0){
if (strpos('=',$new) === false){
$new .= '=';
}
}
$new = '{"receipt-data":"'.$new.'","password":"<my_shared_secret>"}';
$info = getReceiptData($new);
?>
现在我的问题来了。我想利用中间服务器,例如通过使用服务器验证来确定用户是否已在另一台设备上订阅。我的测试流程如下:
1) 我有两个设备,A 和 B。
2) 我从两台设备上删除了任何现有的应用程序版本。好的。
3) 我在 iTunesConnect 上创建了一个新的沙箱用户,并使用它在设置中登录两台设备的沙箱帐户。好的
4) 我在设备 A 上从 Xcode 启动应用程序,因为这是第一次启动它没有收据,所以它通过 SKRefreshReceiptRequest 获得了一个。好的
5) 我在设备 B 上从 Xcode 启动应用程序,但它也没有收据,因为它是第一次启动,所以它通过 SKRefreshReceiptRequest 获得了一个。好的
6) 我在设备 B 上购买了订阅。功能会立即交付。一切正常
7) 现在,我在设备 A 上再次启动该应用程序。它有一张收据(我们在第 4 点启动它))但该收据已过时,因为它不包括我在设备 B 上购买的产品。调用了 PerformServerToServerValidation,但不幸的是,返回的 JSON 不包含 "latest_receipt" 字段。它只包含 "receipt"、"status" 和 "environment" 键:
所以我无法确定是否已经在另一台设备上购买了订阅。我一定遗漏了一些重要的东西,但服务器收据验证的优势之一不应该是始终拥有最新的收据吗?要知道的一件重要事情是,如果我恢复购买或尝试再次购买,那么,如果我调用服务器验证代码,它会完美运行并具有 "latest_receipt" 密钥。但这一切的意义何在?我想使用服务器验证来避免强制用户进行恢复购买或再次购买商品,即使没有收费。感谢您的帮助。
服务器端收据验证的目的是防止用户输入伪造的收据。服务器只是验证您发送给它的收据。它不会使用您发送的收据来检查是否有以后可用的相应收据。
您描述的流程,即您需要在设备 B 上恢复购买的流程,是预期的流程。
您可能会将服务器端收据验证与服务器通知混淆,Apple 可以向您的服务器发送通知subscription status updates