在App Purchase中使用受信任的服务器进行收据验证。有什么想法为什么我没有从服务器获取最新的收据?

时间:2019-02-10 04:16:01

标签: ios cocoa-touch in-app-purchase

我正在迅速开发一个使用In App Purchase的iOS应用程序。我只有一种产品是自动续订的,还提供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”字段。它仅包含“收据”,“状态”和“环境”键:

Xcode Debugger Screenshot

因此,我无法确定订阅是否已在另一台设备上购买。我必须缺少一些重要的东西,但是不应该始终拥有最新的收据,这不是服务器收据验证的优点之一吗?要知道的一件事是,如果我恢复购买或尝试再次购买,那么,如果我调用服务器验证代码,它就可以正常工作并且具有“ latest_receipt”键。但是这一切的意义是什么?我想使用服务器验证来避免强迫用户进行恢复购买或再次购买物品,即使未付费也是如此。感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

服务器端收据验证的目的是保护自己,防止用户注入伪造的收据。服务器只是简单地验证您发送的收据。它不会使用您发送的收据来检查以后是否有相应的收据。

您描述的流程(需要在设备B上还原购买的流程)是预期的流程。

您可能会混淆服务器端收据验证与服务器通知,Apple可以在其中将服务器subscription status updates发送给