| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 | <?phpnamespace app\api\controller;use app\common\controller\Api;use think\Db;use addons\epay\library\Service;/** * 充值配置与充值订单 */class Payios extends Api{    /**     * 订单同步     */    public function verify_order(){        $eventSystem = new \Freeios\Event\SystemEvent();        $request_uri = addslashes($_SERVER['REQUEST_URI']);        $resp_str = file_get_contents( "php://input");        $eventSystem->add_error('苹果端回调-input',$request_uri,$resp_str);        $resp_str = stripslashes($resp_str);        $resp_data = json_decode($resp_str,true);        //苹果内购的验证收据,可以根据需要传递订单或者用户信息过来        $receipt_data = $resp_data['apple_receipt'];        $uid = $this->uid;        if (!$uid){            return_json_data(-99,'请先登录');        }        $eventSystem->add_error('苹果端回调-apple_receipt',$request_uri,$receipt_data);        // 验证支付状态        $result=$this->validate_apple_pay($receipt_data);        if(!$result['status']){ // 凭据验证不通过            $eventSystem->add_error('苹果端回调-result',$request_uri,'凭据验证不通过');            return_json_data(0,'Credential verification failed');        }        $notify = $result['data'];        $transId = $notify['transaction_id'];   // 交易的标识        $originalTransId = $notify['original_transaction_id'];  // 原始交易ID        $transTime = $this->toTimeZone($notify['purchase_date']);  // 购买时间        $transResult = $this->check_apple_trans($notify,$transId,$originalTransId,$transTime,$receipt_data);        if($transResult['status']<=0){            $eventSystem->add_error('苹果端回调-result',$request_uri,'交易号已经出现过了');            return_json_data(0,'交易号已经出现过了');        }        // 处理订单数据        $buyerInfo = $result['sandbox'];  // 1 沙盒数据 0 正式        $productId = $notify['product_id']; // 订单类型        $is_trial_period = $notify['is_trial_period'] == 'false' ? 0 : 1; //是否首次购买        $purchaseDate = str_replace(' America/Los_Angeles','',$notify['purchase_date_pst']);        $pay_detail = $this->pay_detail[$reward]; // 购买畅读卡        $products = array_column($pay_detail,null,'expend_identifier');        $products = $products[$productId];        $total_fee = $products['pay']*100; // 分        $type = 3; // 苹果内购支付        if($buyerInfo == 1){            $type = 6;//沙盒模式        }        // 写入订单(这个其实可以在IOS发起支付的时候请求服务端,先生成订单,并返回订单号)        $orderId = 'ios_a'.$this->uid.date("mdHis").rand(2000,8000);        if(!$orderId ){            $eventSystem->add_error('苹果端回调-result',$request_uri,'订单处理出错');            return_json_data(0,'写入订单失败');        }        // 处理订单        $rs = 1;        if(!$rs){            $eventSystem->add_error('苹果端回调-result',$request_uri,'更新数据错误失败');            return_json_data(0,'更新数据错误失败');        }        $eventSystem->add_error('苹果端回调-result',$request_uri,'订单处理成功');        return_json_data(1,'ok');    }    /** 自动续费订阅回调* password 秘钥: 43f37f26****adc66a1be* */    public function renew(){        $resp_str = file_get_contents( "php://input");        if(empty($resp_str)){            $inputArr = I('','trim','');            $resp_str = '';            foreach($inputArr as $key=>$value){                $resp_str.=$key."=".$value."&";            }        }        $eventSystem = new \Freeios\Event\SystemEvent();        $eventSystem->add_error('renew','AppleAutoPay',$resp_str);        $data = json_decode($resp_str,true);        if(!empty($resp_str)) {//有时候苹果那边会传空数据调用            // notification_type 几种状态            // NOTIFICATION_TYPE  描述            // INITIAL_BUY  初次购买订阅。latest_receipt通过在App Store中验证,可以随时将您的服务器存储在服务器上以验证用户的订阅状态。            // CANCEL  Apple客户支持取消了订阅。检查Cancellation Date以了解订阅取消的日期和时间。            // RENEWAL  已过期订阅的自动续订成功。检查Subscription Expiration Date以确定下一个续订日期和时间。            // INTERACTIVE_RENEWAL  客户通过使用应用程序界面或在App Store中的App Store中以交互方式续订订阅。服务立即可用。            // DID_CHANGE_RENEWAL_PREF  客户更改了在下次续订时生效的计划。当前的有效计划不受影响。            $notification_type = $data['notification_type'];//通知类型            $password = $data['password']; // 共享秘钥            if ($password == "43f37f26****c66a1be") {                $receipt = isset($data['latest_receipt_info']) ? $data['latest_receipt_info'] : $data['latest_expired_receipt_info']; //latest_expired_receipt_info 好像只有更改续订状态才有                $product_id = $receipt['product_id'];   // //商品的标识                $original_transaction_id = $receipt['original_transaction_id'];  // //原始交易ID                $transaction_id = $receipt['transaction_id'];  //  //交易的标识                $purchaseDate = str_replace(' America/Los_Angeles','',$receipt['purchase_date_pst']);                //查询出该apple ID最后充值过的用户                $userid = 0; // 去数据库查询是否充值过                if ($notification_type == 'CANCEL') { //取消订阅,做个记录                    if ($userid > 0) {                        $eventSystem->add_error('renew','AppleAutoPay','用户订阅取消记录成功');                    }                } else {                    //自动续订,给用户加时间                    //排除几种状态不用处理,1,表示订阅续订状态的更改 2,表示客户对其订阅计划进行了更改 3,在最初购买订阅时发生                    //if ($notification_type != "DID_CHANGE_RENEWAL_PREF" && $notification_type != "DID_CHANGE_RENEWAL_STATUS" && $notification_type != "INITIAL_BUY") {                    if ($notification_type == "INTERACTIVE_RENEWAL" || $notification_type == "RENEWAL") {                        $transTime = $this->toTimeZone($receipt['purchase_date']);                        //查询数据库,该订单是否已经处理过了                        $appleTransCnt = 1; // 去数据库查看该订单是否处理过                        if ($appleTransCnt == 0) { //没有使用过,继续走                            $order_type = $this->products[$product_id];                            $order_money = $this->product_money[$order_type];                            $eventSystem->add_error('renew','AppleAutoPay','续订成功');                        } else {                            $eventSystem->add_error('renew','AppleAutoPay','此次支付订单已处理过');                        }                    } else {                        $eventSystem->add_error('renew','AppleAutoPay','该类型通知不予处理--notification_type:' . $notification_type);                    }                }            } else {                $eventSystem->add_error('renew','AppleAutoPay','该通知传递的密码不正确--password:' . $password);            }        }    }    /**     * 验证这个交易号是否存在过了     * @param $notify     * @param $transId     * @param $totalAmount     * @param $tradeId     * @param $receipt_data     */    public function check_apple_trans($notify,$transId,$originalTransId,$transTime,$receipt_data){        $eventOrder = new \Freeios\Event\OrderEvent();        $where = ['trade_no'=>$transId, ];        $appleTransCnt = $eventOrder->get_order_count($where);        if($appleTransCnt>0){            return ['status'=>-1,'appleTransCnt'=>$appleTransCnt];        }else{            $eventOrder->add_order_log_apple([                'trans_id'=>$transId,                'original_trans_id'=>$originalTransId,                'content'=>json_encode(['appleTransCnt'=>$appleTransCnt,'notify'=>$notify,'receipt_data'=>$receipt_data]),            ]);            return ['status'=>1];        }    }    private function toTimeZone($src, $from_tz = 'Etc/GMT', $to_tz = 'Asia/Shanghai', $fm = 'Y-m-d H:i:s') {        $datetime = new \DateTime($src, new \DateTimeZone($from_tz));        $datetime->setTimezone(new \DateTimeZone($to_tz));        return $datetime->format($fm);    }    /**     * 根据语言获取当前地区时间     * 以本地服务器时间为准     * 比美国纽约快12小时     * 比泰国,印尼快1小时     * 比葡萄牙里本斯快7小时     * @param $language     */    private function format_time_zone($language,$is_format=true){        if($language == 1){            $f_time = strtotime('-12 hours');        }else if($language == 2 || $language == 3){            $f_time = strtotime('-1 hours');        }else{//葡萄牙语            $f_time = strtotime('-7 hours');        }        if($is_format){            $f_time = date('Y-m-d H:i:s',$f_time);        }        return $f_time;    }    private function format_to_time_zone($time_zone){        date_default_timezone_set($time_zone);//设置时区        $f_time = date('Y-m-d H:i:s');        date_default_timezone_set('Asia/Shanghai');//设置回默认的        return $f_time;    }    /**     * 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环境的验证服务     */    private function acurl($receipt_data, $sandbox=0){        //小票信息        $POSTFIELDS = array("receipt-data" => $receipt_data,"password"=>"43f37f26****c66a1be");        $POSTFIELDS = json_encode($POSTFIELDS);        //正式购买地址 沙盒购买地址        $url_buy     = "https://buy.itunes.apple.com/verifyReceipt";        $url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";        $url = $sandbox ? $url_sandbox : $url_buy;        //简单的curl        $ch = curl_init($url);        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);        curl_setopt($ch, CURLOPT_POST, 1);        curl_setopt($ch, CURLOPT_POSTFIELDS, $POSTFIELDS);        curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);  //这两行一定要加,不加会报SSL 错误        curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);        $result = curl_exec($ch);        curl_close($ch);        return $result;    }    /**     * 验证AppStore内付     * @param  string $receipt_data 付款后凭证     * @return array                验证是否成功     */    private function validate_apple_pay($receipt_data){        // 验证参数        if (strlen($receipt_data)<20){            $result=array(                'status'=>false,                'message'=>' Illegal param'            );            return $result;        }        // 请求验证        $html = $this->acurl($receipt_data);        $data = json_decode($html,true);        $data['sandbox'] = '0';        // 如果是沙盒数据 则验证沙盒模式        if($data['status']=='21007'){            $html = $this->acurl($receipt_data, 1);            $data = json_decode($html,true);            $data['sandbox'] = '1';        }        $eventSystem = new \Freeios\Event\SystemEvent();        $eventSystem->add_error('苹果验证','validate_apple_pay',json_encode($data));        // 判断是否购买成功        if(intval($data['status'])===0){ // 成功            $receipts = $data['latest_receipt_info']; // 自动续订的订阅项 时才会有            if(!isset($data['latest_receipt_info'])){                $receipts = $data['receipt']['in_app']; // 消费类型            }            if(count($receipts)>0){                $maxDate = '0';  //最新的日期,时间戳                $appData = null; //最新的那组数组                foreach($receipts as $k=>$app){                    if($maxDate<$app['purchase_date_ms']){                        $appData = $app;                        $maxDate = $app['purchase_date_ms'];                    }                }                $result=array(                    'status'=>true,                    'message'=>'Purchase success',                    'data'=>$appData,                    'sandbox'=>$data['sandbox'],                );            }else{                $result=array(                    'status'=>false,                    'message'=>'No data status:'.$data['status']                );            }        }else{ // 失败            $result=array(                'status'=>false,                'message'=>'Failed purchase status:'.$data['status']            );        }        return $result;    }}
 |