<?php
if(!defined('DEDEINC')) exit('Request Error!');
/**
 *易寶接口類
 */
class yeepay
{
    var $dsql;
    var $mid;
  
    # 業務類型
    # 支付請求，固定值"Buy" 
    var $p0_Cmd = 'Buy';
  
    # 送貨地址
    # 為"1": 需要用戶將送貨地址留在易寶支付系統;為"0": 不需要，默認為 "0".
    var $p9_SAF = "0";
    
    # 網關地址不能隨便更改
    var $reqURL_onLine = "https://www.yeepay.com/app-merchant-proxy/node";
    //$reqURL_onLine = "http://tech.yeepay.com:8080/robot/debug.action";
    
    var $return_url='/plus/carbuyaction.php?dopost=return'; //返回處理地址
        
    /**
    * 構造函數
    *
    * @access  public
    * @param
    *
    * @return void
    */
    function yeepay()
    {
        global $dsql;
        $this->dsql = $dsql;
    }

    function __construct()
    {
        $this->yeepay();
    }
    
    /**
     *  設定接口會送地址
     *
     *  例如: $this->SetReturnUrl($cfg_basehost."/tuangou/control/index.php?ac=pay&orderid=".$p2_Order)
     *
     * @param     string  $returnurl  會送地址
     * @return    void
     */
    function SetReturnUrl($returnurl='')
    {
        if (!empty($returnurl))
        {
            $this->return_url = $returnurl;
        }
    }

    /**
    * 生成支付代碼
    * @param   array   $order      訂單信息
    * @param   array   $payment    支付方式信息
    */
    function GetCode($order, $payment)
    {
        global $cfg_basehost,$cfg_cmspath;
        //對於二級目錄的處理
        if(!empty($cfg_cmspath)) $cfg_basehost = $cfg_basehost.'/'.$cfg_cmspath;

        #    商家設置用戶購買商品的支付信息.
        ##易寶支付平台統一使用GBK/GB2312編碼方式,參數如用到中文，請注意轉碼
        
        #    商戶訂單號,選填.
        ##若不為""，提交的訂單號必須在自身賬戶交易中唯一;為""時，易寶支付會自動生成隨機的商戶訂單號.
        $p2_Order                    = trim($order['out_trade_no']);
        
        #    支付金額,必填.
        ##單位:元，精確到分.
        $p3_Amt                        = $order['price'];
        
        #    交易幣種,固定值"CNY".
        $p4_Cur                        = "CNY";
        
        #    商品名稱
        ##用於支付時顯示在易寶支付網關左側的訂單產品信息.
        $p5_Pid                        = trim($order['out_trade_no']);
        
        #    商品種類
        $p6_Pcat                    = 'cart';
        
        #    商品描述
        $p7_Pdesc                    = '';
        
        #    商戶接收支付成功數據的地址,支付成功後易寶支付會向該地址發送兩次成功通知.
        //$p8_Url                        = $cfg_basehost."/plus/carbuyaction.php?dopost=return&code=".$payment['code'];  
        $p8_Url                        =   $cfg_basehost.$this->return_url.'&code='.$payment['code'];
        
        #    商戶擴展信息
        ##商戶可以任意填寫1K 的字符串,支付成功時將原樣返回.                                                
        $pa_MP                        = 'member';
        
        #    應答機制
        ##為"1": 需要應答機制;為"0": 不需要應答機制.
        $pr_NeedResponse    = 1;
        
        #    銀行編碼
            ##默認為""，到易寶支付網關.若不需顯示易寶支付的頁面，直接跳轉到各銀行、神州行支付、駿網一卡通等支付頁面，該字段可依照附錄:銀行列表設置參數值.            
        $pd_FrpId                    = '';
        #調用簽名函數生成簽名串
        $hmac = $this->getReqHmacString($payment['yp_account'],$payment['yp_key'],$p2_Order,$p3_Amt,$p4_Cur,$p5_Pid,$p6_Pcat,$p7_Pdesc,$p8_Url,$pa_MP,$pd_FrpId,$pr_NeedResponse);
        
        $button = '<form target="_blank" method="post" action="'.$this->reqURL_onLine.'">
                            <input type="hidden" value="'.$this->p0_Cmd.'" name="p0_Cmd">
                            <input type="hidden" value="'.$payment['yp_account'].'" name="p1_MerId">
                            <input type="hidden" value="'.$p2_Order.'" name="p2_Order">
                            <input type="hidden" value="'.$p3_Amt.'" name="p3_Amt">
                            <input type="hidden" value="'.$p4_Cur.'" name="p4_Cur">
                            <input type="hidden" value="'.$p5_Pid.'" name="p5_Pid">
                            <input type="hidden" value="'.$p6_Pcat.'" name="p6_Pcat">
                            <input type="hidden" value="'.$p7_Pdesc.'" name="p7_Pdesc">
                            <input type="hidden" value="'.$p8_Url.'" name="p8_Url">
                            <input type="hidden" value="'.$this->p9_SAF.'" name="p9_SAF">
                            <input type="hidden" value="'.$pa_MP.'" name="pa_MP">
                            <input type="hidden" value="'.$pd_FrpId.'" name="pd_FrpId">
                            <input type="hidden" value="'.$pr_NeedResponse.'" name="pr_NeedResponse"    >
                            <input type="hidden" value="'.$hmac.'" name="hmac">
                            <input type="submit" value="立即使用YeePay易寶支付"></form>';

    /* 清空購物車 */
        require_once DEDEINC.'/shopcar.class.php';
        $cart     = new MemberShops();
        $cart->clearItem();
        $cart->MakeOrders();
        return $button;
    }

    /**
    * 響應操作
    */
    function respond()
    {

        /* 引入配置文件 */
        require_once DEDEDATA.'/payment/'.$_REQUEST['code'].'.php';
        
        $p1_MerId = trim($payment['yp_account']);
        $merchantKey = trim($payment['yp_key']);
        
        #  解析返回參數.
        $return = $this->getCallBackValue($r0_Cmd, $r1_Code, $r2_TrxId, $r3_Amt, $r4_Cur, $r5_Pid, $r6_Order, $r7_Uid, $r8_MP, $r9_BType, $hmac);
        
        #  判斷返回簽名是否正確（True/False）
        $bRet = $this->CheckHmac($p1_MerId,$merchantKey,$r0_Cmd,$r1_Code,$r2_TrxId,$r3_Amt,$r4_Cur,$r5_Pid,$r6_Order,$r7_Uid,$r8_MP,$r9_BType,$hmac);

        #  校驗碼正確.
        if($bRet)
        {
            if($r1_Code=="1")
            {
                /*判斷訂單類型*/
                if(preg_match ("/S-P[0-9]+RN[0-9]/",$r6_Order)) 
                {
                    //獲取用戶mid
                    $row = $this->dsql->GetOne("SELECT * FROM #@__shops_orders WHERE oid = '{$r6_Order}'");
                    $this->mid = $row['userid'];
                    $ordertype="goods";
                } else if (preg_match ("/M[0-9]+T[0-9]+RN[0-9]/",$r6_Order)){
                    $row = $this->dsql->GetOne("SELECT * FROM #@__member_operation WHERE buyid = '{$r6_Order}'");
                    //獲取訂單信息，檢查訂單的有效性
                    if(!is_array($row)||$row['sta']==2) return $msg = "您的訂單已經處理，請不要重複提交!";
                    $ordertype = "member";
                    $product =    $row['product'];
                    $pname= $row['pname'];
                    $pid=$row['pid'];
                    $this->mid = $row['mid'];
                } else {    
                    return $msg = "支付失敗，您的訂單號有問題!";
                }


                #    需要比較返回的金額與商家數據庫中訂單的金額是否相等，只有相等的情況下才認為是交易成功.
                #    並且需要對返回的處理進行事務控制，進行記錄的排它性處理，防止對同一條交易重複發貨的情況發生.                
                if($r9_BType == "1" || $r9_BType == "3"){
                    if($ordertype == "goods"){ 
                        if($this->success_db($r6_Order))  return $msg = "支付成功!<br> <a href='/'>返回主頁</a> <a href='/member'>會員中心</a>";
                        else  return $msg = "支付失敗!<br> <a href='/'>返回主頁</a> <a href='/member'>會員中心</a>";
                    } else if ($ordertype=="member") {
                        $oldinf = $this->success_mem($r6_Order,$pname,$product,$pid);
                        return $msg = "<font color='red'>".$oldinf."</font><br> <a href='/'>返回主頁</a> <a href='/member'>會員中心</a>";
                    }
                } else if ( $r9_BType == "2" ){
                    #如果需要應答機制則必須回寫流,以success開頭,大小寫不敏感.
                    echo "success";
                    if($ordertype=="goods"){ 
                        if($this->success_db($r6_Order))  return $msg = "支付成功!<br> <a href='/'>返回主頁</a> <a href='/member'>會員中心</a>";
                        else  return $msg = "支付失敗!<br> <a href='/'>返回主頁</a> <a href='/member'>會員中心</a>";
                    } else if ($ordertype=="member") {
                        if($this->success_mem($r6_Order,$pname,$product,$pid))  return $msg = "支付成功!<br> <a href='/'>返回主頁</a> <a href='/member'>會員中心</a>";
                        else  return $msg = "支付失敗!<br> <a href='/'>返回主頁</a> <a href='/member'>會員中心</a>";
                    }
                }
            }
        } else {
            $this->log_result ("verify_failed");
            return $msg = "交易信息被篡!<br> <a href='/'>返回主頁</a> ";
        }
    }


    #簽名函數生成簽名串
    function getReqHmacString($p1_MerId,$merchantKey,$p2_Order,$p3_Amt,$p4_Cur,$p5_Pid,$p6_Pcat,$p7_Pdesc,$p8_Url,$pa_MP,$pd_FrpId,$pr_NeedResponse)
    {
        #進行簽名處理，一定按照文檔中標明的簽名順序進行
        $sbOld = "";
        #加入業務類型
        $sbOld = $sbOld.$this->p0_Cmd;
        #加入商戶編號
        $sbOld = $sbOld.$p1_MerId;
        #加入商戶訂單號
        $sbOld = $sbOld.$p2_Order;     
        #加入支付金額
        $sbOld = $sbOld.$p3_Amt;
        #加入交易幣種
        $sbOld = $sbOld.$p4_Cur;
        #加入商品名稱
        $sbOld = $sbOld.$p5_Pid;
        #加入商品分類
        $sbOld = $sbOld.$p6_Pcat;
        #加入商品描述
        $sbOld = $sbOld.$p7_Pdesc;
        #加入商戶接收支付成功數據的地址
        $sbOld = $sbOld.$p8_Url;
        #加入送貨地址標識
        $sbOld = $sbOld.$this->p9_SAF;
        #加入商戶擴展信息
        $sbOld = $sbOld.$pa_MP;
        #加入銀行編碼
        $sbOld = $sbOld.$pd_FrpId;
        #加入是否需要應答機制
        $sbOld = $sbOld.$pr_NeedResponse;
        
        return $this->HmacMd5($sbOld,$merchantKey);
    } 

    #    取得返回串中的所有參數
    function getCallBackValue(&$r0_Cmd,&$r1_Code,&$r2_TrxId,&$r3_Amt,&$r4_Cur,&$r5_Pid,&$r6_Order,&$r7_Uid,&$r8_MP,&$r9_BType,&$hmac)
    {  
        $r0_Cmd       = $_REQUEST['r0_Cmd'];
        $r1_Code      = $_REQUEST['r1_Code'];
        $r2_TrxId     = $_REQUEST['r2_TrxId'];
        $r3_Amt       = $_REQUEST['r3_Amt'];
        $r4_Cur       = $_REQUEST['r4_Cur'];
        $r5_Pid       = $_REQUEST['r5_Pid'];
        $r6_Order     = $_REQUEST['r6_Order'];
        $r7_Uid       = $_REQUEST['r7_Uid'];
        $r8_MP        = $_REQUEST['r8_MP'];
        $r9_BType     = $_REQUEST['r9_BType']; 
        $hmac         = $_REQUEST['hmac'];
        return NULL;
    }

    function CheckHmac($p1_MerId,$merchantKey,$r0_Cmd,$r1_Code,$r2_TrxId,$r3_Amt,$r4_Cur,$r5_Pid,$r6_Order,$r7_Uid,$r8_MP,$r9_BType,$hmac)
    {
        if($hmac == $this->getCallbackHmacString($p1_MerId,$merchantKey,$r0_Cmd,$r1_Code,$r2_TrxId,$r3_Amt,$r4_Cur,$r5_Pid,$r6_Order,$r7_Uid,$r8_MP,$r9_BType))
            return TRUE;
        else
            return FALSE;
    }

    function getCallbackHmacString($p1_MerId,$merchantKey,$r0_Cmd,$r1_Code,$r2_TrxId,$r3_Amt,$r4_Cur,$r5_Pid,$r6_Order,$r7_Uid,$r8_MP,$r9_BType)
    {
        #取得加密前的字符串
        $sbOld = "";
        #加入商家ID
        $sbOld = $sbOld.$p1_MerId;
        #加入消息類型
        $sbOld = $sbOld.$r0_Cmd;
        #加入業務返回碼
        $sbOld = $sbOld.$r1_Code;
        #加入交易ID
        $sbOld = $sbOld.$r2_TrxId;
        #加入交易金額
        $sbOld = $sbOld.$r3_Amt;
        #加入貨幣單位
        $sbOld = $sbOld.$r4_Cur;
        #加入產品Id
        $sbOld = $sbOld.$r5_Pid;
        #加入訂單ID
        $sbOld = $sbOld.$r6_Order;
        #加入用戶ID
        $sbOld = $sbOld.$r7_Uid;
        #加入商家擴展信息
        $sbOld = $sbOld.$r8_MP;
        #加入交易結果返回類型
        $sbOld = $sbOld.$r9_BType;
        
        return $this->HmacMd5($sbOld,$merchantKey,'gbk');

    }

    function HmacMd5($data,$key,$lang='utf-8')
    {
        // RFC 2104 HMAC implementation for php.
        // Creates an md5 HMAC.
        // Eliminates the need to install mhash to compute a HMAC
        // Hacked by Lance Rushing(NOTE: Hacked means written)
        
        //需要配置環境支持iconv，否則中文參數不能正常處理
        if($GLOBALS['cfg_soft_lang'] != 'utf-8' || $lang!='utf-8')
        {
            $key = gb2utf8($key);
            $data = gb2utf8($data);
        }
        $b = 64; // byte length for md5
        if (strlen($key) > $b) {
            $key = pack("H*",md5($key));
        }
        $key = str_pad($key, $b, chr(0x00));
        $ipad = str_pad('', $b, chr(0x36));
        $opad = str_pad('', $b, chr(0x5c));
        $k_ipad = $key ^ $ipad ;
        $k_opad = $key ^ $opad;
        
        return md5($k_opad . pack("H*",md5($k_ipad . $data)));
    }

    /*處理物品交易*/
    function success_db($order_sn)
    {
        //獲取訂單信息，檢查訂單的有效性
        $row = $this->dsql->GetOne("SELECT state FROM #@__shops_orders WHERE oid='$order_sn' ");
        if($row['state'] > 0)
        {
            return TRUE;
        }    
        /* 改變訂單狀態_支付成功 */
        $sql = "UPDATE `#@__shops_orders` SET `state`='1' WHERE `oid`='$order_sn' AND `userid`='".$this->mid."'";
        if($this->dsql->ExecuteNoneQuery($sql))
        {
            $this->log_result("verify_success,訂單號:".$order_sn); //將驗證結果存入文件
            return TRUE;
        } else {
            $this->log_result ("verify_failed,訂單號:".$order_sn);//將驗證結果存入文件
            return FALSE;
        }
    }

    /*處理點卡，會員升級*/
    function success_mem($order_sn,$pname,$product,$pid)
    {
        //更新交易狀態為已付款
        $sql = "UPDATE `#@__member_operation` SET `sta`='1' WHERE `buyid`='$order_sn' AND `mid`='".$this->mid."'";
        $this->dsql->ExecuteNoneQuery($sql);

        /* 改變點卡訂單狀態_支付成功 */
        if($product=="card")
        {
            $row = $this->dsql->GetOne("SELECT cardid FROM #@__moneycard_record WHERE ctid='$pid' AND isexp='0' ");;
            //如果找不到某種類型的卡，直接為用戶增加金幣
            if(!is_array($row))
            {
                $nrow = $this->dsql->GetOne("SELECT num FROM #@__moneycard_type WHERE pname = '{$pname}'");
                $dnum = $nrow['num'];
                $sql1 = "UPDATE `#@__member` SET `money`=money+'{$nrow['num']}' WHERE `mid`='".$this->mid."'";
                $oldinf ="已經充值了".$nrow['num']."金幣到您的帳號！";
            } else {
                $cardid = $row['cardid'];
                $sql1=" UPDATE #@__moneycard_record SET uid='".$this->mid."',isexp='1',utime='".time()."' WHERE cardid='$cardid' ";
                $oldinf='您的充值密碼是：<font color="green">'.$cardid.'</font>';
            }
            //更新交易狀態為已關閉
            $sql2=" UPDATE #@__member_operation SET sta=2,oldinfo='$oldinf' WHERE buyid='$order_sn'";
            if($this->dsql->ExecuteNoneQuery($sql1) && $this->dsql->ExecuteNoneQuery($sql2))
            {
                $this->log_result("verify_success,訂單號:".$order_sn); //將驗證結果存入文件
                return $oldinf;
            } else {
                $this->log_result ("verify_failed,訂單號:".$order_sn);//將驗證結果存入文件
                return "支付失敗！";
            }
        /* 改變會員訂單狀態_支付成功 */
        } else if ( $product=="member" ){
            $row = $this->dsql->GetOne("SELECT rank,exptime FROM #@__member_type WHERE aid='$pid' ");
            $rank = $row['rank'];
            $exptime = $row['exptime'];
            /*計算原來升級剩餘的天數*/
            $rs = $this->dsql->GetOne("SELECT uptime,exptime FROM #@__member WHERE mid='".$this->mid."'");
            if($rs['uptime']!=0 && $rs['exptime']!=0 ) 
            {
                $nowtime = time();
                $mhasDay = $rs['exptime'] - ceil(($nowtime - $rs['uptime'])/3600/24) + 1;
                $mhasDay=($mhasDay>0)? $mhasDay : 0;
            }
            //獲取會員默認級別的金幣和積分數
            $memrank = $this->dsql->GetOne("SELECT money,scores FROM #@__arcrank WHERE rank='$rank'");
            //更新會員信息
            $sql1 =  " UPDATE #@__member SET rank='$rank',money=money+'{$memrank['money']}',
                       scores=scores+'{$memrank['scores']}',exptime='$exptime'+'$mhasDay',uptime='".time()."' 
                       WHERE mid='".$this->mid."'";
            //更新交易狀態為已關閉
            $sql2=" UPDATE #@__member_operation SET sta='2',oldinfo='會員升級成功!' WHERE buyid='$order_sn' ";
            if($this->dsql->ExecuteNoneQuery($sql1) && $this->dsql->ExecuteNoneQuery($sql2))
            {
                $this->log_result("verify_success,訂單號:".$order_sn); //將驗證結果存入文件
                return "會員升級成功！";
            } else {
                $this->log_result ("verify_failed,訂單號:".$order_sn);//將驗證結果存入文件
                return "會員升級失敗！";
            }
        }    
    }

    function  log_result($word) {
        global $cfg_cmspath;
        $fp = fopen(dirname(__FILE__)."/../../data/payment/log.txt","a");
        flock($fp, LOCK_EX) ;
        fwrite($fp,$word.",執行日期:".strftime("%Y-%m-%d %H:%I:%S",time())."\r\n");
        flock($fp, LOCK_UN);
        fclose($fp);
    }

}//End API