代码之家  ›  专栏  ›  技术社区  ›  Alfonso Tesauro

使用受信任的服务器进行应用内购买收据验证。你知道我为什么没有收到服务器的最新收据吗?

  •  0
  • Alfonso Tesauro  · 技术社区  · 5 年前

    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) 我从两台设备上删除任何现有的应用程序版本。好啊

    4) 我从设备A上的Xcode启动应用程序,因为这是第一次启动,它没有收据,所以它通过SKRefreshReceiptRequest获得一个收据。好啊

    5) 我从设备B上的Xcode启动应用程序,并且由于它是第一次启动,所以它也没有收据,所以它通过SKRefreshReceiptRequest获得一张收据。好啊

    6) 我在设备B上购买订阅。功能立即交付。好的。

    7) 现在,我在设备A上再次启动应用程序。它有一个收据(我们在第4点启动了它),但该收据已过时,因为它不包括我在设备B上购买的产品。调用PerformServerToServerValidation,但不幸的是,返回的JSON不包含“latest_Receive”字段。它仅包含“收据”、“状态”和“环境”键:

    Xcode Debugger Screenshot

    因此,我无法确定订阅是否已在其他设备上购买。我一定错过了一些重要的东西,但始终拥有最新的收据不应该是服务器收据验证的优势之一吗?需要知道的一件重要事情是,如果我恢复购买或再次尝试购买,那么,如果我调用服务器验证代码,它将正常工作,并且具有“latest_Receive”密钥。但这一切的意义何在?我希望使用服务器验证来避免强迫用户进行恢复购买或再次购买物品,即使不收费。谢谢你的帮助。

    1 回复  |  直到 5 年前
        1
  •  2
  •   Paulw11    5 年前

    服务器端收据验证的目的是防止用户注入伪造收据。服务器只是验证您发送的收据。它不会使用您发送的收据来检查是否有后续相应的收据可用。

    您可能会混淆服务器端收据验证和服务器通知,苹果可以在服务器上发送您的服务器 subscription status updates