来一个腾讯开平的工具类

由于一直做各种对接工作,所以也总结了下常用的接口,适用于目前黄钻、蓝钻的页游对接,有兴趣的朋友可以来指点。

<?php

/**
 * 腾讯接口统一API 此接口应该先调用init方法后再执行其他操作.即 QqApi::init($appid, $appkey, $server_name);
 */
class QqApi {

    private static $sandbox = true;

    private static $appid       = 0;
    private static $appkey      = '';
    private static $server_name = '';
    private static $format      = 'json';
    private static $stat_url    = "apistat.tencentyun.com";

    CONST OPEN_API_TENCENTYUN      = 'openapi.tencentyun.com';
    CONST OPEN_API_TENCENTYUN_TEST = '119.147.19.43';

    CONST OPEN_API_GET_PFKEY = '113.108.20.23';

    // 腾讯PF常量
    CONST PID_TENCENT = 'tencent';
    CONST PF_QQGAME = 'qqgame';
    CONST PF_QZONE = 'qzone';
    CONST PF_WEBSITE = 'website';
    CONST PF_OPENAPP = 'qqopenapp';
    CONST PF_TGP = 'tgp';
    CONST PF_UNION = 'union';
    CONST PF_XINYUE = 'xinyue';

    // **************** 集市任务cmd码 ****************
    /**
     * 开发者仅需要查询任务步骤是否完成,返回步骤完成状态。
     */
    CONST TASK_MARKET_CMD_CHECK = 'check';
    /**
     * 开发者需要查询任务步骤是否完成,若步骤已完成,直接给用户发货(payitem),并返回发货是否成功。
     */
    CONST TASK_MARKET_CMD_CHECK_AWARD = 'check_award';
    /**
     * 平台通知开发者直接给给用户发货,开发者返回发货是否成功。
     */
    CONST TASK_MARKET_CMD_AWARD = 'award';

    /**
     * 反馈给腾讯任务集市的状态码
     * !!沙箱联调时,ret返回码仅能返回0和3!!
     */
    /**
     * 步骤已完成 或 奖励发放成功
     */
    CONST TASK_MARKET_RET_OK = 0;
    /**
     * 用户尚未在应用内创建角色
     */
    CONST TASK_MARKET_RET_NO_ROLE = 1;
    /**
     * 用户尚未完成本步骤
     */
    CONST TASK_MARKET_RET_NOT_FINISHED = 2;
    /**
     * 该步骤奖励已发放过
     */
    CONST TASK_MARKET_RET_HAS_AWARDED = 3;
    /**
     * token已过期
     */
    CONST TASK_MARKET_RET_TOKEN_EXPIRE = 100;
    /**
     * token不存在
     */
    CONST TASK_MARKET_RET_TOKEN_NOT_FOUND = 101;
    /**
     * 奖励发放失败
     */
    CONST TASK_MARKET_RET_AWARD_FAILS = 102;
    /**
     * 请求参数错误
     */
    CONST TASK_MARKET_RET_PARAM_ERROR = 103;

    /**
     * 玩家信息
     */
    CONST S_QQ_API_USER_INFO = '/v3/user/get_info';

    /**
     * 取得PFKEY
     */
    CONST S_QQ_API_GET_PFKEY = '/v3/user/get_pfkey';
    /**
     * QQ API 蓝钻信息
     */
    CONST S_QQ_API_BLUE_VIP = '/v3/user/blue_vip_info';
    /**
     * QQ API 黄钻信息
     */
    CONST S_QQ_API_YELLOW_VIP = '/v3/user/is_vip';

    /**
     * QQ API 各类腾讯增值服务信息
     */
    CONST S_QQ_API_TOTAL_VIP = '/v3/user/total_vip_info';

    /**
     * QQ API 心悦信息
     */
    CONST S_QQ_API_XINYUE_INFO = '/v3/user/get_xinyue_info';

    /**
     * QQ API 是否登录
     */
    CONST S_QQ_API_IS_LOGIN = '/v3/user/is_login';
    /**
     * QQ API 防沉迷接口
     */
    CONST S_QQ_API_ANTIADDICTION_INFO = '/v3/user/get_antiaddiction_info';

    /**
     * QQ API 获取交易TOKEN
     */
    CONST S_QQ_API_BUY_GOODS = '/v3/pay/buy_goods';

    /**
     * 取得包月礼包
     */
    CONST S_QQ_API_GET_TOKEN = '/v3/pay/get_token';

    /**
     * 取得包月礼包token
     * 参考 http://wiki.open.qq.com/wiki/v3/pay/get_token
     * @param string $openid
     * @param string $openkey
     * @param string $pf
     * @param string $pfkey
     * @param string $discountid
     * @param int $tokentype
     * @param string $method
     * @param string $protocol
     * @return array
     */
    static function getToken($openid, $openkey, $pf, $pfkey, $discountid, $tokentype=1, $method = 'GET', $protocol = 'http'){
        $params = array(
            'openid'=>$openid,
            'openkey'=>$openkey,
            'pf'=>$pf,
            'pfkey'=>$pfkey,
            'tokentype'=>$tokentype,
            'ts'=>$_SERVER['REQUEST_TIME'],
            'discountid'=>$discountid,
            'zoneid'=>0,
            'version' => 'v3',
        );
        return self::api(self::S_QQ_API_GET_TOKEN, $params, $method, $protocol);
    }

    /**
     * 取得完整的联盟PF
     * @param string $appid
     * @param string $openid
     * @return string $pf
     */
    static function unionPf($openid=''){
        $url = 'http://union.tencentlog.com/cgi-bin/Query.cgi?appid=' . self::$appid . '&opopenid=' . $openid;
        $ret = Utils::get($url, '', false);
        $ret = iconv('gb2312', 'utf-8', $ret);
        $result = json_decode($ret, 1);
        if ($result['iRet'] == 0) {
            return $result['sPf'];
        } else {
            return '';
        }
    }

    /**
     * 取得联盟的pfkey
     * @param string $openid
     * @param string $openkey
     * @param string $pf
     * @return string
     */
    static function unionPfkey($openid, $openkey, $pf){
        $url = 'http://union.tencentlog.com/control/GetPfkey.php?app=' . self::$appid . '&pf=' . $pf . '&openid=' . $openid . '&openkey=' . $openkey;
        $ret = Utils::get($url, '', false);
        $ret = iconv('gb2312', 'utf-8', $ret);
        $result = json_decode($ret, 1);
        if ($result['ret'] == 0) {
            return $result['pfkey'];
        } else {
            return '';
        }
    }

    static function pfkey($openid, $openkey, $pf, $method = 'GET', $protocol = 'http'){
        $params = array(
            'openid' => $openid,
            'openkey' => $openkey,
            'pf' => $pf,
        );
        /**
         * 返回值
         * ret      返回码 0 成功
         * pfkey
         * is_lost  忽略此值
         */
        $ret = self::api(self::S_QQ_API_GET_PFKEY, $params, $method, $protocol, self::OPEN_API_GET_PFKEY);
        if ( $openid == '5A86F5B031580BBA67945EC315F78A4A'){
            var_dump($ret);
        }
        return $ret['pfkey'] ? : '';
    }

    CONST S_QQ_API_GET_PFKEY_BY_OPENID = 'http://apps.game.qq.com/wan/box/App/GetPfkeyByOpenid.php';

    /**
     * 根据OPENID取得pfkey
     * @param $openid
     * @param $openkey
     * @param $pf
     * @return array|mixed
     */
    static function getPfkeyByOpenid($openid, $openkey, $pf){
        $params = array(
            'appid'=>self::$appid,
            'openid'=>$openid,
            'openkey'=>$openkey,
            'pf'=>$pf,
        );
        $ret =  Utils::get(self::S_QQ_API_GET_PFKEY_BY_OPENID, $params, false);
        $ret = str_ireplace('var GetPfkeyByOpenid_JSON = ', '', $ret);
        $json = json_decode($ret, 1);
        if ( $json ){
            if ( $json['ret'] == 0 ){
                return $json['pfkey'];
            }
        }
        return '';
    }

    /**
     * 获取交易TOKEN
     * @param array $params 应该包含如下字段:
     *      pfkey:      必须
     *      amt:            交易总价 Q点为单位 1Q币=10Q点
     *      amttype:        支付方式:coin:仅允许使用游戏币支付,不传为Q点
     *      ts:         必须 时间戳
     *      payitem:    必须 ID*Price*Num,单价最少不能少于2Q点
     *      appmode:        购买数量类型 1:不可选 2:可选
     *      max_num:        购买数量上限 appmode:2时有效
     *      goodsmeta:  必须 商品描述信息 256字符内utf8编码
     *      goodsurl:   必须 商品图片URL 116x116px
     *      zoneid:     必须 分区ID,默认0
     *      manyouid:       视情况,详见文档
     *      present:        是否是礼物   0或不传给自己 1:送给好友 2:索要
     *      paymode:        忽略
     *      cee_extend:     忽略
     * @return array
     */
    static function buyGoods($params=array()){
        /**
         * 参考 http://wiki.open.qq.com/wiki/v3/pay/buy_goods
         * ret          返回码
         * msg          错误信息
         * is_lost      是否有数据丢失(忽略此值)
         * token        ret=0时为临时订单号
         * url_params   ret=0时为真正购买物品的url参数,获取此值后传给前端有js唤起支付接口
         */
        return self::api(self::S_QQ_API_BUY_GOODS, $params, 'post', 'https');
    }

    /**
     * 获得用户信息
     * @param string $openid
     * @param string $openkey
     * @param string $pf
     * @param string $pfkey
     * @param int $flag 当pf为蓝钻时,flag为要获取的数据类型。1:昵称性别 2:蓝钻等级 3:昵称和蓝钻等级 4:照片秀标识
     * @param string $method
     * @param string $protocol
     * @return array
     */
    static function getUserInfo($openid, $openkey, $pf, $pfkey, $flag = 1, $method = 'GET', $protocol = 'http') {
        /**
         * 参考 http://wiki.open.qq.com/wiki/v3/user/get_info
         * ret 返回码
         * msg 错误信息
         * is_lost    判断是否有数据丢失。如果应用不使用cache,不需要关心此参数。 0或者不返回:没有数据丢失,可以缓存。 1:有部分数据丢失或错误,不要缓存。
         * nickname    昵称。
         * gender    性别。
         * country    国家(当pf=qzone、pengyou或qplus时返回)。
         * province    省(当pf=qzone、pengyou或qplus时返回)。
         * city    市(当pf=qzone、pengyou或qplus时返回)。
         * figureurl    头像URL。详见:前端页面规范#6. 关于用户头像的获取和尺寸说明。
         * openid    用户QQ号码转化得到的ID(当pf=qplus时返回)。
         * qq_level    用户QQ等级(当pf=qplus时返回)。
         * qq_vip_level    用户QQ会员等级(当pf=qplus时返回)。
         * qplus_level    用户Q+等级(当pf=qplus时返回)。
         * is_yellow_vip    是否为黄钻用户(0:不是; 1:是)。(当pf=qzone、pengyou或qplus时返回)
         * is_yellow_year_vip    是否为年费黄钻用户(0:不是; 1:是)。 (当pf=qzone、pengyou或qplus时返回)
         * yellow_vip_level    黄钻等级,目前最高级别为黄钻8级(如果是黄钻用户才返回此参数)。(当pf=qzone、pengyou或qplus时返回)
         * is_yellow_high_vip    是否为豪华版黄钻用户(0:不是; 1:是)。(当pf=qzone、pengyou或qplus时返回)
         * is_blue_vip    是否为蓝钻用户(0:不是; 1:是)。(当pf=qqgame或3366时返回)
         * is_blue_year_vip    是否为年费蓝钻用户(0:不是; 1:是)。(当pf=qqgame或3366时返回)
         * blue_vip_level    蓝钻等级(如果是蓝钻用户才返回此参数)。(当pf=qqgame或3366时返回)
         * 3366_level    3366用户的大等级。(当pf=3366时返回)
         * 3366_level_name    3366用户的等级名,如小游游、小游仙。(当pf=3366时返回)
         * 3366_grow_level    3366用户的成长等级。(当pf=3366时返回)
         * 3366_grow_value    3366用户的成长值。(当pf=3366时返回)
         * is_super_blue_vip    是否是豪华蓝钻。(当pf=qqgame或3366时返回)
         */
        $params = array(
            'openid' => $openid,
            'openkey' => $openkey,
            'pfkey' => $pfkey,
            'pf' => $pf,
        );
        if ('qqgame' == $pf) {
            $params['flag'] = $flag;
        }
        return self::api(self::S_QQ_API_USER_INFO, $params, $method, $protocol);
    }

    /**
     * 判断用户是否登录
     * @param string $openid
     * @param string $openkey
     * @param string $pf
     * @param string $pfkey
     * @param string $method
     * @param string $protocol
     * @return array
     */
    static function isLogin($openid, $openkey, $pf, $pfkey, $method = 'GET', $protocol = 'http') {
        /**
         * 参考 http://wiki.open.qq.com/wiki/v3/user/is_login
         * ret 返回码
         * msg 错误信息
         */
        return self::api(self::S_QQ_API_IS_LOGIN, array(
            'openid' => $openid,
            'openkey' => $openkey,
            'pfkey' => $pfkey,
            'pf' => $pf,
        ), $method, $protocol);
    }

    /**
     * 获得防沉迷信息
     * @param string $openid
     * @param string $openkey
     * @param string $pf
     * @param string $pfkey
     * @param int $cmd_type
     * @param string $method
     * @param string $protocol
     */
    static function getAntiaddiction($openid, $openkey, $pf, $pfkey, $cmd_type = 2, $method = 'GET', $protocol = 'http') {
        /**
         * 参考 http://open.qqgame.qq.com/inside/lodyapi/get_antiaddiction_info.htm
         * ret      返回码
         * msg      错误描述
         * audit    身份信息: 0:未成年 1:成年 2:无身份证验证(1时gametime无意义)
         * gametime 在线时长
         */
        return self::api(self::S_QQ_API_IS_LOGIN, array(
            'openid' => $openid,
            'openkey' => $openkey,
            'pfkey' => $pfkey,
            'pf' => $pf,
            'cmd_type' => $cmd_type,
        ), $method, $protocol);
    }

    /**
     * 检测蓝钻状态
     * @param string $openid
     * @param string $openkey
     * @param string $pf
     * @param string $pfkey
     * @return mixed
     */
    static function isBlueVip($openid, $openkey, $pf, $pfkey, $method = 'GET', $protocol = 'http') {
        /**
         * 此接口返回值
         *  is_blue_vip int 是否蓝钻, 1 表示是,0 表示不是
         *  is_blue_year_vip int 是否年费蓝钻;1 表示是,0 表示不是
         *  is_super_blue_vip int 是否豪华蓝钻;1 表示是,0 表示不是
         *  is_expand_blue_vip int 是否超级蓝钻;1 表示是,0 表示不是
         *  blue_vip_level int 蓝钻等级
         *  is_have_growth int 蓝钻是否具备成长值,1 表示是,0 表示不是
         *  is_mobile_blue_vip int 是否手机蓝钻;1 表示是,0 表示不是
         *  server_time int 服务器时间,用于比较蓝钻开通时间和到期时间,unix 时间戳,单位为秒
         *  vip_reg_time int 蓝钻开通时间,unix 时间戳,单位为秒
         *  year_vip_reg_time int 年费蓝钻开通时间,unix 时间戳,单位为秒
         *  super_vip_reg_time int 豪华蓝钻开通时间,unix 时间戳,单位为秒
         *  expand_vip_reg_time int 超级蓝钻开通时间,unix 时间戳,单位为秒
         *  vip_valid_time int 蓝钻到期时间,unix 时间戳,单位为秒
         *  year_vip_valid_time int 年费蓝钻到期时间,unix 时间戳,单位为秒
         *  super_vip_valid_time int 豪华蓝钻到期时间,unix 时间戳,单位为秒
         *  expand_vip_valid_time int 超级蓝钻到期时间,unix 时间戳,单位为秒
         */
        return self::api(self::S_QQ_API_BLUE_VIP, array(
            'openid' => $openid,
            'openkey' => $openkey,
            'pfkey' => $pfkey,
            'pf' => $pf,
        ), $method, $protocol);
    }

    /**
     * 检测黄钻状态
     * @param string $openid
     * @param string $openkey
     * @param string $pf
     * @param string $pfkey
     * @return mixed
     */
    static function isYellowVip($openid, $openkey, $pf, $pfkey, $method = 'GET', $protocol = 'http') {
        /**
         * 此接口返回值 http://wiki.open.qq.com/wiki/v3/user/is_vip
         * ret 返回码
         * msg 消息
         * is_lost                1:有部分数据丢失或错误,不要缓存。
         * is_yellow_vip          是否为黄钻用户(0:不是; 1:是)
         * is_yellow_year_vip     是否为年费黄钻用户(0:不是; 1:是)
         * yellow_vip_level       黄钻等级。目前最高级别为黄钻8级(如果是黄钻用户才返回此字段)
         * is_yellow_high_vip     是否为豪华版黄钻用户(0:不是; 1:是)。(当pf=qzone、pengyou或qplus时返回)
         * yellow_vip_pay_way     用户的付费类型。0:非预付费用户(先开通业务后付费,一般指通过手机开通黄钻的用户);1:预付费用户(先付费后开通业务,一般指通过Q币Q点、财付通或网银付费开通黄钻的用户)。
         */
        return self::api(self::S_QQ_API_YELLOW_VIP, array(
            'openid' => $openid,
            'openkey' => $openkey,
            'pfkey' => $pfkey,
            'pf' => $pf,
        ), $method, $protocol);
    }

    /**
     * 返回是否心悦用户
     * @param string $openid
     * @param string $openkey
     * @param string $pf
     * @param string $pfkey
     * @param string $method
     * @param string $protocol
     * @return array
     */
    static function isXinyue($openid, $openkey, $pf, $pfkey, $method = 'GET', $protocol = 'http') {
        /**
         * 心悦接口:非普通API
         * xy_type: 心悦会员类型,1为普通心悦,2为蓝悦,3为财悦(目前暂时只支持1)。
         * xy_level: 心悦会员等级。
         */
        return self::api(self::S_QQ_API_XINYUE_INFO, array(
            'openid' => $openid,
            'openkey' => $openkey,
            'pfkey' => $pfkey,
            'pf' => $pf,
        ), $method, $protocol);
    }

    /**
     * 返回腾讯所有VIP信息
     * @param string $openid
     * @param string $openkey
     * @param string $pf
     * @param string $pfkey
     * @param string $method
     * @param string $protocol
     * @return array
     */
    static function totalVIP($openid, $openkey, $pf, $pfkey, $method = 'GET', $protocol = 'http') {
        /**
         * 参考 http://wiki.open.qq.com/wiki/v3/user/total_vip_info
         * member_vip    string    是否查询QQ会员信息,1为查询,0或者不写为不查询。
         * blue_vip        string    是否查询蓝钻信息,1为查询,0或者不写为不查询。
         * yellow_vip    string    是否查询黄钻信息,1为查询,0或者不写为不查询。
         * red_vip        string    是否查询红钻信息,1为查询,0或者不写为不查询。
         * green_vip    string    是否查询绿钻信息,1为查询,0或者不写为不查询。
         * pink_vip        string    是否查询粉钻信息,1为查询,0或者不写为不查询。
         * superqq        string    是否查询超级qq信息,1为查询,0或者不写为不查询。
         * is_3366        string    是否查询3366信息,1为查询,0或者不写为不查询。
         */
        return self::api(self::S_QQ_API_TOTAL_VIP, array(
            'openid' => $openid,
            'openkey' => $openkey,
            'pfkey' => $pfkey,
            'pf' => $pf,
        ), $method, $protocol);
    }


    /**
     * 初始化QQApi(必须在调用本类其他方法前调用)
     *
     * @param int $appid 应用的ID
     * @param string $appkey 应用的密钥
     * @param bool $sandbox 服务器ID
     */
    static function init($appid, $appkey, $sandbox = false) {
        self::$appid  = $appid;
        self::$appkey = $appkey;
        self::isSandbox($sandbox);
    }

    /**
     * 设置服务器域名(并不考虑$debug值)
     * @param $server_name
     */
    static function setServerName($server_name) {
        return self::$server_name = $server_name;
    }

    static function setStatUrl($stat_url) {
        self::$stat_url = $stat_url;
    }

    /**
     * 设置调试模式(设置后URL也会根据$debug值判断)
     * @param bool $debug
     * @return bool
     */
    static function isSandbox($debug = false) {
        self::$sandbox       = $debug;
        self::$server_name = !!self::$sandbox ? self::OPEN_API_TENCENTYUN_TEST : self::OPEN_API_TENCENTYUN;
        return self::$sandbox;
    }


    CONST S_QQ_API_CONFIRM_DELIVERY = '/v3/pay/confirm_delivery';

    /**
     * 提交反馈信息
     * @param array $params
     * @param string $method
     * @param string $protocol
     * @return array
     */
    static function confirmDelivery($params=array(), $method = 'GET', $protocol = 'http'){
        $params = array(
            'openid'=>$params['openid'],
            'pf'=>$params['pf'],
            'ts' => $params['openid'],
            'payitem' => $params['payitemde'],
            'provide_errno' => 0,
            'token_id' => $params['token_id'],
            'billno' => $params['billno'],
            'amt' => $params['amt'],
            'payamt_coins' => $params['payamt_coins'],
            'providetype'=>0,
            'zoneid' => $params['zoneid'] ? : 0,
        );
        //file_put_contents('/tmp/order_delivery.txt', print_r($params, 1)."\r\n".self::$appid.'-'.self::$appkey.'-'.self::$sandbox."\r\n", FILE_APPEND);
        return self::api(self::S_QQ_API_CONFIRM_DELIVERY, $params, $method, $protocol);
    }

    /**
     * 执行API调用,返回结果数组
     *
     * @param string $script_name 调用的API方法,比如/v3/user/get_info,参考 http://wiki.open.qq.com/wiki/API_V3.0%E6%96%87%E6%A1%A3
     * @param array $params 调用API时带的参数
     * @param string $method 请求方法 post / get
     * @param string $protocol 协议类型 http / https
     * @return array 结果数组
     */
    static function api($script_name, $params, $method = 'post', $protocol = 'http', $server_name='') {
        // 检查 openid 是否为空
        if (!isset($params['openid']) || empty($params['openid'])) {
            return array(
                'ret' => 'OPENAPI_ERROR_REQUIRED_PARAMETER_EMPTY',
                'msg' => 'openid is empty');
        }
        // 检查 openid 是否合法
        if (!self::isOpenId($params['openid'])) {
            return array(
                'ret' => 'OPENAPI_ERROR_REQUIRED_PARAMETER_INVALID',
                'msg' => 'openid is invalid');
        }

        // 无需传sig, 会自动生成
        unset($params['sig']);

        // 添加一些参数
        $params['appid']  = self::$appid;
        $params['format'] = self::$format;

        // 生成签名
        $secret        = self::$appkey . '&';
        $sig           = self::makeSig($method, $script_name, $params, $secret);
        $params['sig'] = $sig;

        $url    = $protocol . '://' .( $server_name ? : self::$server_name ). $script_name;
        $cookie = array();

        //记录接口调用开始时间
        $start_time = self::getTime();

        // 发起请求
        $ret = self::makeRequest($url, $params, $cookie, $method, $protocol);
        if (false === $ret['result']) {
            return array(
                'ret' => 'OPENAPI_ERROR_CURL ' . +$ret['errno'],
                'msg' => $ret['msg'],
            );
        }

        $result_array = json_decode($ret['msg'], true);

        // 远程返回的不是 json 格式, 说明返回包有问题
        if (is_null($result_array)) {
            $result_array = array(
                'ret' => 'OPENAPI_ERROR_RESPONSE_DATA_INVALID',
                'msg' => $ret['msg']
            );
        }
        return $result_array;
    }

    static public function getTime() {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }


    /**
     * 检查 openid 的格式
     *
     * @param string $openid openid
     * @return bool (true|false)
     */
    static private function isOpenId($openid) {
        return preg_match('/^[0-9a-fA-F]{32}$/i', $openid);
    }

    /**
     * 执行一个 HTTP 请求
     *
     * @param string $url 执行请求的URL
     * @param mixed $params 表单参数
     *                            可以是array, 也可以是经过url编码之后的string
     * @param mixed $cookie cookie参数
     *                            可以是array, 也可以是经过拼接的string
     * @param string $method 请求方法 post / get
     * @param string $protocol http协议类型 http / https
     * @return array 结果数组
     */
    static public function makeRequest($url, $params, $cookie, $method = 'post', $protocol = 'http') {
        $query_string  = self::makeQueryString($params);
        $cookie_string = self::makeCookieString($cookie);

        $ch = curl_init();

        if ('GET' == strtoupper($method)) {
            curl_setopt($ch, CURLOPT_URL, "$url?$query_string");
        } else {
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $query_string);
        }

        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);

        // disable 100-continue
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));

        if (!empty($cookie_string)) {
            curl_setopt($ch, CURLOPT_COOKIE, $cookie_string);
        }

        if ('https' == $protocol) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }

        $ret = curl_exec($ch);
        $err = curl_error($ch);

        if (false === $ret || !empty($err)) {
            $errno = curl_errno($ch);
            $info  = curl_getinfo($ch);
            curl_close($ch);

            return array(
                'result' => false,
                'errno' => $errno,
                'msg' => $err,
                'info' => $info,
            );
        }

        curl_close($ch);

        return array(
            'result' => true,
            'msg' => $ret,
        );

    }


    /**
     * 执行一个 HTTP 请求,以post方式,multipart/form-data的编码类型上传文件
     *
     * @param string $url 执行请求的URL
     * @param mixed $params 表单参数,必须是array, 对于文件表单项 直接传递文件的全路径, 并在前面增加'@'符号
     *                          举例: array('upload_file'=>'@/home/xxx/hello.jpg', 'field1'=>'value1');
     * @param mixed $cookie cookie参数
     *                            可以是array, 也可以是经过拼接的string
     * @param string $protocol http协议类型 http / https
     * @return array 结果数组
     */
    static public function makeRequestWithFile($url, $params, $cookie, $protocol = 'http') {
        $cookie_string = self::makeCookieString($cookie);

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);

        // disable 100-continue
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));

        if (!empty($cookie_string)) {
            curl_setopt($ch, CURLOPT_COOKIE, $cookie_string);
        }

        if ('https' == $protocol) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }

        $ret = curl_exec($ch);
        $err = curl_error($ch);

        if (false === $ret || !empty($err)) {
            $errno = curl_errno($ch);
            $info  = curl_getinfo($ch);
            curl_close($ch);

            return array(
                'result' => false,
                'errno' => $errno,
                'msg' => $err,
                'info' => $info,
            );
        }

        curl_close($ch);

        return array(
            'result' => true,
            'msg' => $ret,
        );

    }


    static public function makeQueryString($params) {
        if (is_string($params))
            return $params;

        $query_string = array();
        foreach ($params as $key => $value) {
            array_push($query_string, rawurlencode($key) . '=' . rawurlencode($value));
        }
        $query_string = join('&', $query_string);
        return $query_string;
    }

    static public function makeCookieString($params) {
        if (is_string($params))
            return $params;

        $cookie_string = array();
        foreach ($params as $key => $value) {
            array_push($cookie_string, $key . '=' . $value);
        }
        $cookie_string = join('; ', $cookie_string);
        return $cookie_string;
    }

    /**
     * 生成签名
     *
     * @param string $method 请求方法 "get" or "post"
     * @param string $url_path
     * @param array $params 表单参数
     * @param string $secret 密钥
     */
    static public function makeSig($method, $url_path, $params, $secret) {
        $mk      = self::makeSource($method, $url_path, $params);
        $my_sign = hash_hmac("sha1", $mk, strtr($secret, '-_', '+/'), true);
        $my_sign = base64_encode($my_sign);

        return $my_sign;
    }

    static private function makeSource($method, $url_path, $params) {
        $strs = strtoupper($method) . '&' . rawurlencode($url_path) . '&';

        ksort($params);
        $query_string = array();
        foreach ($params as $key => $val) {
            array_push($query_string, $key . '=' . $val);
        }
        $query_string = join('&', $query_string);

        return $strs . str_replace('~', '%7E', rawurlencode($query_string));
    }

    /**
     * 验证回调发货URL的签名 (注意和普通的OpenAPI签名算法不一样,详见@refer的说明)
     *
     * @param string $method 请求方法 "get" or "post"
     * @param string $url_path
     * @param array $params 腾讯调用发货回调URL携带的请求参数
     * @param string $secret 密钥
     * @param string $sig 腾讯调用发货回调URL时传递的签名
     *
     * @refer
     *  http://wiki.open.qq.com/wiki/%E5%9B%9E%E8%B0%83%E5%8F%91%E8%B4%A7URL%E7%9A%84%E5%8D%8F%E8%AE%AE%E8%AF%B4%E6%98%8E_V3
     */
    static public function verifySig($method, $url_path, $params, $secret, $sig) {
        unset($params['sig']);

        // 先使用专用的编码规则对value编码
        foreach ($params as $k => $v) {
            $params[$k] = self::encodeValue($v);
        }

        // 再计算签名
        $sig_new = self::makeSig($method, $url_path, $params, $secret);

        return $sig_new == $sig;
    }

    /**
     * 回调发货URL专用的编码算法
     *  编码规则为:除了 0~9 a~z A~Z !*()之外其他字符按其ASCII码的十六进制加%进行表示,例如"-"编码为"%2D"
     * @refer
     *  http://wiki.open.qq.com/wiki/%E5%9B%9E%E8%B0%83%E5%8F%91%E8%B4%A7URL%E7%9A%84%E5%8D%8F%E8%AE%AE%E8%AF%B4%E6%98%8E_V3
     */
    static private function encodeValue($value) {
        $rst = '';

        $len = strlen($value);

        for ($i = 0; $i < $len; $i++) {
            $c = $value[$i];
            if (preg_match("/[a-zA-Z0-9!\(\)*]{1,1}/", $c)) {
                $rst .= $c;
            } else {
                $rst .= ("%" . sprintf("%02X", ord($c)));
            }
        }

        return $rst;
    }

    public function market($params, $method = 'get') {
        echo '<pre>';
        var_dump($params);
        echo '<br><br>';
        echo $sig = $params['sig'];
        $url_path = '/qqjz/Market';
        //$url_path = '/cgi-bin/check_award';
        unset($params['sig']);//验证用
        unset($params['app_user_source']);//
        unset($params['app_contract_id']);//
        unset($params['app_custom']);//
        echo '<br>';
        // 生成签名
        echo $secret = $this->appkey . '&';
        //$secret = '111222333'. '&';
        $strs = strtoupper($method) . '&' . rawurlencode($url_path) . '&';

        ksort($params);
        $query_string = array();
        foreach ($params as $key => $val) {
            $value = self::encodeValue($val);
            array_push($query_string, $key . '=' . $value);
        }

        $query_string = join('&', $query_string);
        //var_dump($query_string);
        //echo $newGet = $strs . str_replace('~', '%7E', rawurlencode($query_string));

        $my_sign = hash_hmac("sha1", $newGet, strtr($secret, '-_', '+/'), true);
        echo $my_sign = base64_encode($my_sign);

        die;
    }
    // *********** QQ罗盘上报相关 *********** //
    // QQ罗盘上报相关
    /**
     * QQ 罗盘上报地址-充值
     */
    const QQ_COMPASS_URL_REPORT_CHARGE = 'http://tencentlog.com/stat/report_recharge.php';

    /**
     * QQ 罗盘上报地址-主动注册登录
     */
    const QQ_COMPASS_URL_REPORT_REGISTER = 'http://tencentlog.com/stat/report_register.php';

    /**
     * 支付充值:用户通过Q点/Q币兑换游戏内等值货币(例如“点券/金币/元宝”)的行为。
     * 必填字段:
     * @param int $modifyfee 如果没有变化,则填0, 上报单位为Q分(100Q分 = 10Q点 = 1Q币))
     * 推荐项:
     * $touid, toopenid, source, itemid, itemtype, itemcnt, modifyexp, totalexp, modifycoin, totalcoin, totalfee, level
     */
    public function reportCharge($opuid, $opopenid, $gold = 0, $domain = 1) {
        // 必填项目
        return self::compassReport($opuid, $opopenid, 2, array('modifyfee'), $gold, $domain);
    }

    /**
     * 默认的构建接口
     * 必选:
     * @param string $version 若未上报,则自动补齐为:1
     * @param string $appid 应用ID
     * @param string $userip 若未上报,则自动补齐为:http请求头里的客户机ip
     * @param string $svrip 若未上报,则自动补齐为:该cgi所在server的ip
     * @param int $time 若未上报,则自动补齐为:服务器当前时间
     * @param string $domain 域
     * @param string $worldid 若未上报,则自动补齐为:1
     * @param string $opuid
     * @param string $opopenid
     */
    static function compassReport($opuid, $opopenid, $report_flag = 1, $params = array(), $gold=0, $domain=1)
    {
        $req = Yii::app()->request;
        // 必填项目
        $version = $req->getParam('version'); // 由调用接口传
        $appid = self::$appid; // 由调用接口传
        $userip = $req->userHostAddress;
        $svrip = $req->getParam('srvip'); // 由调用接口传
        $time = time();
        $worldid = $req->getParam('worldid'); // 由调用接口传
//        $opuid = $req->getParam('opuid'); // 由调用接口传
//        $opopenid = $req->getParam('opopenid');
        // 构建默认必选参数表
        $params_str = <<<EOT
version={$version}&appid={$appid}&userip={$userip}&svrip={$svrip}&time={$time}&domain={$domain}&worldid={$worldid}&opuid={$opuid}&opopenid={$opopenid}
EOT;
        // 构造附加字段
        foreach ($params as $v) {
            $tmp_v = $req->getParam($v);
            if ($tmp_v) {
                $params_str .= "&{$v}=" . $tmp_v;
            }
        }
        $params_str.="&modifyfee=$gold";
        switch ($report_flag) {
            case 1:
                $url = self::QQ_COMPASS_URL_REPORT_REGISTER;
                break;
            case 2:
                $url = self::QQ_COMPASS_URL_REPORT_CHARGE;
                break;
            default:
                $url = '';
        }
        if ( $url ) {
            file_put_contents('/tmp/compass.txt', $url.'?'.$params);
            return Utils::get($url, $params_str, false);
        }
        return 0;
    }

    // *********** open.qq.com 服务器列表获取工具 *********** //
    /**
     * 取得腾讯开平的服务器配置
     * @return mixed
     */
    static function getServerlist() {
        $cacheKey = '__CACHE_OPEN_QQ_SERVER_LIST__'.self::$appid;
        $data = Cache::cache_get($cacheKey);
        if ( !$data || BOSS_NO_CACHE){
            $str       = Utils::get('http://openwebgame.qq.com/app/RecentServerInfo.php?appid=' . self::$appid, null, false);
            $str = str_replace('var user_all_servers = ', '', $str);
            $tmp_array = explode('var user_recent_servers', $str);
            $str = str_replace(';', '', $tmp_array[0]);
//             处理服务器列表去掉历史记录
            $data = json_decode($str, 1);
            // 给一个300秒的缓存
            Cache::cache_set($cacheKey, $data, 300);
        }
        return $data;
    }

    /**
     * 取得推荐服列表
     * @return mixed
     */
    static function getRecommendServers(){
        $cacheKey = $cacheKey = '__CACHE_OPEN_QQ_SERVER_RECOMMEND__'.self::$appid;
        $recommends = Cache::cache_get($cacheKey);
        if ( !$recommends || BOSS_NO_CACHE){
            $data = self::getServerlist();
            if (empty($data) || !is_array($data)){
                return $recommends;
            }
            $recommends = array();
            $lastServer = null;
            foreach($data as $zone){
                if ( empty($zone['subcat'])){
                    continue;
                }
                foreach($zone['subcat'] as $srv){
                    if ( $srv['sAttrValue']['iIsRecommend'] > 0 ){
                        $recommends[] = $srv;
                    }
                    if ( !$lastServer )
                        $lastServer = $srv;
                }
            }
            if ( count($recommends) > 0 ){
                // 找到1个以上的推荐服就缓存300秒
                Cache::cache_set($cacheKey, $recommends, 300);
            } else if ( $lastServer ){
                // 不存在推荐服时就取得一个最新的服务器, 即openqq服务器列表中最上面的那个服
                $recommends[] = $lastServer;
                Cache::cache_set($cacheKey, $recommends, 60);
            } else {
                // 尚未添加任意服务器也要缓存5秒防刷
                Cache::cache_set($cacheKey, $recommends, 5);
            }
        }
        return $recommends;
    }
}

猜你喜欢

转载自nomandia.iteye.com/blog/2352832