PHP服務端 蘋果支付(IAP)處理

 公司做的app需要做IAP訂閱支付,開始覺得和微信的支付流程差不多,做起來還是有點麻煩,主要是網上的文章很少,不能拿來主義。自己做完總結一下,希望對小夥伴們有幫助我就很欣慰了。代碼寫的不好 不要噴我。。。

首先講一下我的業務邏輯,也就是php服務端需要做什麼事情。

先上圖:

                                              

下面我詳細的講一下每一步做些什麼,貼出來相應的代碼供大家參考。

 第一步: app 調用創建訂單接口,創建訂單信息,保存下來。

 第二步: app 調用sdk 發起支付,傳蘋果給的 receipt(票據)和 訂單號 給服務端,服務端通過驗證receipt 獲取訂單信息保存

                數據庫。至於票據長什麼樣子,怎麼驗證,後面貼代碼出來。

 第三步: 訂閱模式支付,首次支付蘋果服務器會異步發送兩次通知到服務端,之後你在app端操作的時候也會有通知,比如更                     改套餐,取消訂閱等操作都會有通知,這個就比較坑了,如果異步處理的時候沒弄清楚。

                很容易出問題,不能每次接收到通知就處理,沙盒模式下通知來的很奇怪,通知有很多狀態,要區別開來處理。

  接下來是代碼處理流程:php框架+tp 5.02

  首先是同步完成訂單時候的驗證:

  

/**
 * @title 驗證支付票據 完成訂單接口
 */
public function verifyReceipt()
{

   $receipt = Request::instance()->param('receipt');  //票據
   $orderSn = Request::instance()->param('orderSn');  /訂單號


   //判斷訂單是否存在 檢查狀態
 
    //寫入日誌  查看票據格式  記錄日誌的方法 這個方法不貼出來了 
   Tool::callAddLog('request-param',json_encode(['receipt'=>$receipt,'orderSn'=>$orderSn]));

   //password 是驗證祕鑰
   $jsonItem = json_encode(['receipt-data'=>$receipt, 'password'=>'XXXXXXXXXXXXXXXX']);

   
   //$url= 'https://buy.itunes.apple.com/verifyReceipt';      //正式
  
   $url= 'https://sandbox.itunes.apple.com/verifyReceipt';  //測試
   
   //模擬post提交   下面會貼出來 
   $result =  Tool::http_post_json($jsonItem,$url);

   if($result['status'] !== 0){
      //驗證失敗 返回app錯誤狀態
   }

   //驗證完成加入日誌
   Tool::callAddLog('verifyReceipt_finish',json_encode($result));

   //找出時間最大的數組
   $receiptitem = $result['latest_receipt_info'];

   //這個是排序的方法  下面回帖出來 蘋果會把這個會員往期的訂單信息全部返回,需要找出來最近的那一組信息
   $item = Tool::arraySort($receiptitem,'purchase_date','desc');

   //判斷一下過期時間 延長會員時間

   $orderThird = $item['transaction_id'];                //本次訂閱的訂單號
 
   $orderThirdFirst = $item['original_transaction_id'];  //這個是原始訂單號

   if($orderThird == $orderThirdFirst){                  //首次訂閱 兩個相等

    }else{

        //判斷過期時間和當前時間比較   expires_date_ms是毫秒單位

        if($item['expires_date_ms']/1000 > time()){

        }else{
           //過期處理  票據失效  
        }
    }

  //接下來處理訂單業務邏輯,處理訂單,修改訂單信息,original_transaction_id 把這個單號和存入訂單信息, 在異步回調的時候根據這個單號來查找訂單信息來判斷是哪個用戶,蘋果票據裏面也有產品product_id,是app_store的產品id,不是自己業務的產品id,可以在後臺把自己的產品ID和 product_id 關聯,處理業務邏輯。

}
//模擬post提交
public static function http_post_json($json,$url) {

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  //這兩行一定要加,不加會報SSL 錯誤
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    $response = curl_exec($ch);
    $errno = curl_errno($ch);
    $errmsg = curl_error($ch);
    curl_close($ch);

    $data = json_decode($response, true);
    return $data;
}
//查找最新數據的方法
 public static function arraySort($arr,$key,$type='asc'){

    $keyArr = []; // 初始化存放數組將要排序的字段值
    foreach ($arr as $k=>$v){
        $keyArr[$k] = $v[$key]; // 循環獲取到將要排序的字段值
    }
    if($type == 'asc'){
        asort($keyArr); // 排序方式,將一維數組進行相應排序
    }else{
        arsort($keyArr);
    }
    foreach ($keyArr as $k=>$v){
        $newArray[$k] = $arr[$k]; // 循環將配置的值放入響應的下標下
    }
    $newArray = array_merge($newArray); // 重置下標
    return $newArray[0]; // 數據返回
}
  

public static function http_post_json($json,$url) {

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  //這兩行一定要加,不加會報SSL 錯誤
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    $response = curl_exec($ch);
    $errno = curl_errno($ch);
    $errmsg = curl_error($ch);
    curl_close($ch);

    $data = json_decode($response, true);

    return $data;
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

驗證之後返回的狀態

驗證之前的票據是一個字符串:就是一個字符串

驗證之後的信息:這個是重點

到了 這同步主動驗證這邊就結束了。

 

下面是異步通知這塊:

異步通知的票據可以驗證發送到蘋果服務器驗證一下有效性,也可以不要驗證,它本身的信息就夠用了。

 

  通知類型: notification_type 有很多種情況,有些通知接收存入日誌,不用處理,

  需要處理的類型:RENEWAL           DID_CHANGE_RENEWAL_STATUS            

  但是 DID_CHANGE_RENEWAL_STATUS 這種通知 在首次訂閱的時候 也會發一個過來,這個時候不處理,也就是當參數       auto_renew_status  等於 true 的時候 不處理,其餘的時候都是需要處理的通知。

 

 

 

public function applePayReceive(){


    $str = file_get_contents('php://input');

    //寫入通知日誌

    $jsonItem = json_decode($str,true);
    

    if($jsonItem['notification_type'] == 'RENEWAL' || $jsonItem['notification_type'] == 'DID_CHANGE_RENEWAL_STATUS'){
      
        if($jsonItem['notification_type'] == 'DID_CHANGE_RENEWAL_STATUS'){
            if($jsonItem['auto_renew_status'] == 'true'){
                //第一次夠買的時候 不處理通知
              die;
        }

        $expires_date = $jsonItem['latest_receipt_info']['expires_date'];
        $expires_date = $expires_date/1000;

        Tool::asynAddLog('request-param',$expires_date.'----'.time());

        if($expires_date > time()){

        }else{
            //時間不對 不處理
        }
    }


    //處理訂閱產品的業務邏輯  
    
    echo '完成!';die;
}

好了,上面是我寫的蘋果支付所有流程和代碼,業務代碼部分我去掉了,大家自己寫自己的業務邏輯就可以。

看到最後的同志們,我上面有個地方寫錯了 ,蘋果正式環境,第一次購買的時候,異步的兩個通知,notification_type 等於DID_CHANGE_RENEWAL_STATUS 這個的時候,auto_renew_status 值有可能不是ture,我開始沒弄懂意思,現在我的理解是

等於true是自動續費的意思,就是說用戶選擇的是連續訂閱,而等於false的時候是用戶買了產品取消了訂閱模式。纔會出現這種情況。不是蘋果坑,而是自己沒理解,

 

其實根本不能用通知的類型來判斷是否給用戶重置,正確保險的方案是在主動通知的時候把蘋果的原始訂單號和本次交易的訂單號存入訂單,異步的時候判斷訂單是否完成。然後再寫業務邏輯。

 

重點:異步通知有可能收不到,這種情況下,需要把用戶首次訂閱的憑證存下來,等到用戶到期的時候 再去驗證一下這個憑證,然後取出訂閱相關信息,找到最近的一條,更新會員時間,會員時間用蘋果提供的過期時間就可以。

沙盒模式下蘋果的通知是亂的,請大家注意,不一定按照文檔上面說的時間有規則的發送通知,所以異步通知不能保證業務的完整性,處理業務時一定要把用戶首次支付的憑證持久化,以便後續通知收不到的情況,會員到期服務端主動去查詢會員是否續費 情況。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章