PHP (Laravel) 實現 iOS 內購服務端驗證

woann發表於2020-04-15

IOS內購的流程大致如下:

  1. 通過產品ID獲取產品資訊列表(客戶端的事,我們不用管)
  2. 新增監聽(客戶端的事,我們不用管)
  3. 把產品包裝成SKPayment(支付)傳送給蘋果伺服器(客戶端的事,我們不用管)
  4. 蘋果伺服器購買成功後會回撥監聽方法,根據蘋果伺服器返回資訊判斷是否購買成功。
  5. 購買失敗或已經購買過該商品則登出交易。如果購買成功,此時可以向自家伺服器傳送購買成功的訊息,並通過後臺向蘋果伺服器傳送驗證,然後登出交易。(這一步,客戶端會得到蘋果返回的支付憑證資料,然後用此資料發到我們服務端,服務端進行驗證)

程式碼如下


     /**
     * @Author woann <www.woann.cn>
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     * @des ios內購支付回撥
     */
    public function iosNotify(Request $request)
    {
        //蘋果內購的驗證收據
        $receipt_data = $request->input('apple_receipt');蘋果返回的apple_receipt
        $order_id = $request->input('order_id');//業務訂單id
        $order = Order::find($order_id);//查詢訂單狀態
        if (!$order || $order->state != 0) {
            return returnApi(500,'訂單狀態異常');
        }
        $ios_sandBox = env('IOS_SANDBOX',true);//判斷生產環境,開發環境
        if(empty($receipt_data)){
            return json(500, '引數不正確');
        }
        // 驗證ios內購支付狀態
        $result=$this->validate_apple_pay($receipt_data,$ios_sandBox);
        if($result['status']){
        //在此進行訂單狀態變更等回撥操作
        }else{
            return returnApi(500,$result['message']);
        }
    }

    /**
     * 驗證AppStore內付
     * @param  string $receipt_data 付款後憑證
     * @return array                驗證是否成功
     */
    protected  function validate_apple_pay($receipt_data,$ios_sandBox)
    {
        /**
         * 21000 App Store不能讀取你提供的JSON物件
         * 21002 receipt-data域的資料有問題
         * 21003 receipt無法通過驗證
         * 21004 提供的shared secret不匹配你賬號中的shared secret
         * 21005 receipt伺服器當前不可用
         * 21006 receipt合法,但是訂閱已過期。伺服器接收到這個狀態碼時,receipt資料仍然會解碼並一起傳送
         * 21007 receipt是Sandbox receipt,但卻傳送至生產系統的驗證服務
         * 21008 receipt是生產receipt,但卻傳送至Sandbox環境的驗證服務
         */

        // 驗證引數
        if (strlen($receipt_data)<20){
            $result=array(
                'status'=>false,
                'message'=>'非法引數'
            );
            return $result;
        }
        $POSTFIELDS = '{"receipt-data":"'. $receipt_data .'"}';
        if($ios_sandBox){
            // 請求驗證
            $data = $this->httpRequest('https://sandbox.itunes.apple.com/verifyReceipt', $POSTFIELDS);
        }else{
            // 請求驗證
            $data = $this->httpRequest('https://buy.itunes.apple.com/verifyReceipt', $POSTFIELDS);
        }
        if (!$data || !is_array($data) || !isset($data['status'])) {
            $result=array(
                'status'=>false,
                'message'=>'獲取資料失敗'
            );
            return $result;
        }
        // 判斷是否購買成功
        if($data['status'] ===0){
            $result=array(
                'status'=>true,
                'message'=>'購買成功'
            );
        }else{
            $result=array(
                'status'=>false,
                'message'=>'購買失敗 status:'.$data['status']
            );
        }
        return $result;
    }


protected function httpRequest($url, $postData = array(), $json = true)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    if ($postData) {
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    }
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
    $info = curl_getinfo($ch);
    $data = curl_exec($ch);
    curl_close($ch);
    if ($json) {
        return json_decode($data, true);
    } else {
        return $data;
    }
}    
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章