[
  {
    "path": ".gitignore",
    "content": ".project\r\n.buildpath\r\n.settings\r\n"
  },
  {
    "path": "README.rst",
    "content": "Gt Php SDK\n===============\n使用 3.1 之前版本SDK的用户如果想更新到3.1以及以后版本请先联系极验客服,因为为了兼容老用户,新的特性需要修改验证设置\n本项目是面向服务器端的，具体使用可以参考我们的 `文档 <http://www.geetest.com/install/sections/idx-server-sdk.html>`_ ,客户端相关开发请参考我们的 `前端文档 <http://www.geetest.com/install/>`_.\n\n开发环境\n----------------\n\n - php5.2\n\n\n部署架构\n---------------\n详见 `部署架构 <http://www.geetest.com/install/sections/idx-basic-introduction.html#id7>`__ \n\n\n前端接口\n-------------------\n详见 `前端接口 <http://www.geetest.com/install/sections/idx-client-sdk.html#config-para>`__ \n\n宕机回滚\n--------------\n详见 `宕机回滚 <http://www.geetest.com/install/sections/idx-basic-introduction.html#id8>`__ \n\n\n文件说明\n---------------\n - config/config.php 极验ID和KEY配置文件,请在 `极验后台 <http://account.geetest.com>`__ 申请,进行替换\n - lib/class.geetestlib.php 极验库文件(请不要随意改动)\n - static/login.php 前端展示页面,根据您的需求进行自定义\n - web/StartCaptchaServlet.php 根据自己的私钥初始化验证\n - web/VerifyLoginServlet.php 根据post参数进行二次验证\n - static/gt.js 静态js文件，之前是直接引用http://static.geetest.com/static/tools/gt.js的\n\n\n\n常见问题\n--------------\n1. 3.1.0之前的老版本SDK,不兼容现在的id和key\n\n发布日志\n-----------------\n+ 3.2.0\n\n - 添加用户标识接口\n\n+ 3.1.1\n\n - 统一接口\n\n+ 3.1.0\n\n - 添加challenge加密特性，使验证更安全， 老版本更新请先联系管理员\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"gee-team/gt-php-sdk\",\n    \"description\": \"GtWeb Php Demo\",\n    \"authors\": [\n        {\n            \"name\": \"tanxu1993\",\n            \"email\": \"tanxu1993@gmail.com\"\n        }\n    ],\n    \"require\": {\n        \"php\": \">=5.0.0\"\n    },\n    \"autoload\": {\n        \"classmap\": [\n            \"lib\"\n        ]\n    },\n    \"license\": \"MIT\"\n}"
  },
  {
    "path": "config/config.php",
    "content": "<?php \n/**\n*  Geetest配置文件\n* @author Tanxu\n*/\ndefine(\"CAPTCHA_ID\", \"b46d1900d0a894591916ea94ea91bd2c\");\ndefine(\"PRIVATE_KEY\", \"36fc3fe98530eea08dfc6ce76e3d24c4\");\ndefine(\"MOBILE_CAPTCHA_ID\", \"7c25da6fe21944cfe507d2f9876775a9\");\ndefine(\"MOBILE_PRIVATE_KEY\", \"f5883f4ee3bd4fa8caec67941de1b903\");\n\n\n\n ?>"
  },
  {
    "path": "lib/class.geetestlib.php",
    "content": "<?php\n\n/**\n * 极验行为式验证安全平台，php 网站主后台包含的库文件\n *\n * @author Tanxu\n */\nclass GeetestLib {\n    const GT_SDK_VERSION = 'php_3.2.0';\n\n    public static $connectTimeout = 1;\n    public static $socketTimeout  = 1;\n\n    private $response;\n\n    public function __construct($captcha_id, $private_key) {\n        $this->captcha_id  = $captcha_id;\n        $this->private_key = $private_key;\n    }\n\n    /**\n     * 判断极验服务器是否down机\n     *\n     * @param null $user_id\n     * @return int\n     */\n    public function pre_process($user_id = null) {\n        $url = \"http://api.geetest.com/register.php?gt=\" . $this->captcha_id;\n        if (($user_id != null) and (is_string($user_id))) {\n            $url = $url . \"&user_id=\" . $user_id;\n        }\n        $challenge = $this->send_request($url);\n\n        if (strlen($challenge) != 32) {\n            $this->failback_process();\n\n            return 0;\n        }\n        $this->success_process($challenge);\n\n        return 1;\n    }\n\n    /**\n     * @param $challenge\n     */\n    private function success_process($challenge) {\n        $challenge      = md5($challenge . $this->private_key);\n        $result         = array(\n            'success'   => 1,\n            'gt'        => $this->captcha_id,\n            'challenge' => $challenge\n        );\n        $this->response = $result;\n    }\n\n    /**\n     *\n     */\n    private function failback_process() {\n        $rnd1           = md5(rand(0, 100));\n        $rnd2           = md5(rand(0, 100));\n        $challenge      = $rnd1 . substr($rnd2, 0, 2);\n        $result         = array(\n            'success'   => 0,\n            'gt'        => $this->captcha_id,\n            'challenge' => $challenge\n        );\n        $this->response = $result;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function get_response_str() {\n        return json_encode($this->response);\n    }\n\n    /**\n     * 返回数组方便扩展\n     *\n     * @return mixed\n     */\n    public function get_response() {\n        return $this->response;\n    }\n\n    /**\n     * 正常模式获取验证结果\n     *\n     * @param      $challenge\n     * @param      $validate\n     * @param      $seccode\n     * @param null $user_id\n     * @return int\n     */\n    public function success_validate($challenge, $validate, $seccode, $user_id = null) {\n        if (!$this->check_validate($challenge, $validate)) {\n            return 0;\n        }\n        $data = array(\n            \"seccode\" => $seccode,\n            \"sdk\"     => self::GT_SDK_VERSION,\n        );\n        if (($user_id != null) and (is_string($user_id))) {\n            $data[\"user_id\"] = $user_id;\n        }\n        $url          = \"http://api.geetest.com/validate.php\";\n        $codevalidate = $this->post_request($url, $data);\n        if ($codevalidate == md5($seccode)) {\n            return 1;\n        } else {\n            if ($codevalidate == \"false\") {\n                return 0;\n            } else {\n                return 0;\n            }\n        }\n    }\n\n    /**\n     * 宕机模式获取验证结果\n     *\n     * @param $challenge\n     * @param $validate\n     * @param $seccode\n     * @return int\n     */\n    public function fail_validate($challenge, $validate, $seccode) {\n        if ($validate) {\n            $value   = explode(\"_\", $validate);\n            $ans     = $this->decode_response($challenge, $value['0']);\n            $bg_idx  = $this->decode_response($challenge, $value['1']);\n            $grp_idx = $this->decode_response($challenge, $value['2']);\n            $x_pos   = $this->get_failback_pic_ans($bg_idx, $grp_idx);\n            $answer  = abs($ans - $x_pos);\n            if ($answer < 4) {\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            return 0;\n        }\n\n    }\n\n    /**\n     * @param $challenge\n     * @param $validate\n     * @return bool\n     */\n    private function check_validate($challenge, $validate) {\n        if (strlen($validate) != 32) {\n            return false;\n        }\n        if (md5($this->private_key . 'geetest' . $challenge) != $validate) {\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * GET 请求\n     *\n     * @param $url\n     * @return mixed|string\n     */\n    private function send_request($url) {\n\n        if (function_exists('curl_exec')) {\n\n            $ch = curl_init();\n            curl_setopt($ch, CURLOPT_URL, $url);\n            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout);\n            curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);\n            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n\n            $data = curl_exec($ch);\n\n            if (curl_errno($ch)) {\n                $err = sprintf(\"curl[%s] error[%s]\", $url, curl_errno($ch) . ':' . curl_error($ch));\n                $this->triggerError($err);\n            }\n\n            curl_close($ch);\n        } else {\n            $opts    = array(\n                'http' => array(\n                    'method'  => \"GET\",\n                    'timeout' => self::$connectTimeout + self::$socketTimeout,\n                )\n            );\n            $context = stream_context_create($opts);\n            $data    = file_get_contents($url, false, $context);\n        }\n\n        return $data;\n    }\n\n    /**\n     *\n     * @param       $url\n     * @param array $postdata\n     * @return mixed|string\n     */\n    private function post_request($url, $postdata = '') {\n        if (!$postdata) {\n            return false;\n        }\n\n        $data = http_build_query($postdata);\n        if (function_exists('curl_exec')) {\n            $ch = curl_init();\n            curl_setopt($ch, CURLOPT_URL, $url);\n            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout);\n            curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);\n\n            //不可能执行到的代码\n            if (!$postdata) {\n                curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);\n            } else {\n                curl_setopt($ch, CURLOPT_POST, 1);\n                curl_setopt($ch, CURLOPT_POSTFIELDS, $data);\n            }\n            $data = curl_exec($ch);\n\n            if (curl_errno($ch)) {\n                $err = sprintf(\"curl[%s] error[%s]\", $url, curl_errno($ch) . ':' . curl_error($ch));\n                $this->triggerError($err);\n            }\n\n            curl_close($ch);\n        } else {\n            if ($postdata) {\n                $opts    = array(\n                    'http' => array(\n                        'method'  => 'POST',\n                        'header'  => \"Content-type: application/x-www-form-urlencoded\\r\\n\" . \"Content-Length: \" . strlen($data) . \"\\r\\n\",\n                        'content' => $data,\n                        'timeout' => self::$connectTimeout + self::$socketTimeout\n                    )\n                );\n                $context = stream_context_create($opts);\n                $data    = file_get_contents($url, false, $context);\n            }\n        }\n\n        return $data;\n    }\n\n\n    /**\n     * 解码随机参数\n     *\n     * @param $challenge\n     * @param $string\n     * @return int\n     */\n    private function decode_response($challenge, $string) {\n        if (strlen($string) > 100) {\n            return 0;\n        }\n        $key             = array();\n        $chongfu         = array();\n        $shuzi           = array(\"0\" => 1, \"1\" => 2, \"2\" => 5, \"3\" => 10, \"4\" => 50);\n        $count           = 0;\n        $res             = 0;\n        $array_challenge = str_split($challenge);\n        $array_value     = str_split($string);\n        for ($i = 0; $i < strlen($challenge); $i++) {\n            $item = $array_challenge[$i];\n            if (in_array($item, $chongfu)) {\n                continue;\n            } else {\n                $value = $shuzi[$count % 5];\n                array_push($chongfu, $item);\n                $count++;\n                $key[$item] = $value;\n            }\n        }\n\n        for ($j = 0; $j < strlen($string); $j++) {\n            $res += $key[$array_value[$j]];\n        }\n        $res = $res - $this->decodeRandBase($challenge);\n\n        return $res;\n    }\n\n\n    /**\n     * @param $x_str\n     * @return int\n     */\n    private function get_x_pos_from_str($x_str) {\n        if (strlen($x_str) != 5) {\n            return 0;\n        }\n        $sum_val   = 0;\n        $x_pos_sup = 200;\n        $sum_val   = base_convert($x_str, 16, 10);\n        $result    = $sum_val % $x_pos_sup;\n        $result    = ($result < 40) ? 40 : $result;\n\n        return $result;\n    }\n\n    /**\n     * @param $full_bg_index\n     * @param $img_grp_index\n     * @return int\n     */\n    private function get_failback_pic_ans($full_bg_index, $img_grp_index) {\n        $full_bg_name = substr(md5($full_bg_index), 0, 9);\n        $bg_name      = substr(md5($img_grp_index), 10, 9);\n\n        $answer_decode = \"\";\n        // 通过两个字符串奇数和偶数位拼接产生答案位\n        for ($i = 0; $i < 9; $i++) {\n            if ($i % 2 == 0) {\n                $answer_decode = $answer_decode . $full_bg_name[$i];\n            } elseif ($i % 2 == 1) {\n                $answer_decode = $answer_decode . $bg_name[$i];\n            }\n        }\n        $x_decode = substr($answer_decode, 4, 5);\n        $x_pos    = $this->get_x_pos_from_str($x_decode);\n\n        return $x_pos;\n    }\n\n    /**\n     * 输入的两位的随机数字,解码出偏移量\n     *\n     * @param $challenge\n     * @return mixed\n     */\n    private function decodeRandBase($challenge) {\n        $base      = substr($challenge, 32, 2);\n        $tempArray = array();\n        for ($i = 0; $i < strlen($base); $i++) {\n            $tempAscii = ord($base[$i]);\n            $result    = ($tempAscii > 57) ? ($tempAscii - 87) : ($tempAscii - 48);\n            array_push($tempArray, $result);\n        }\n        $decodeRes = $tempArray['0'] * 36 + $tempArray['1'];\n\n        return $decodeRes;\n    }\n\n    /**\n     * @param $err\n     */\n    private function triggerError($err) {\n        trigger_error($err);\n    }\n}\n"
  },
  {
    "path": "static/gt.js",
    "content": "/* initGeetest 1.0.0\n * 用于加载id对应的验证码库，并支持宕机模式\n * 暴露 initGeetest 进行验证码的初始化\n * 一般不需要用户进行修改\n */\n(function (global, factory) {\n    \"use strict\";\n    if (typeof module === \"object\" && typeof module.exports === \"object\") {\n        // CommonJS\n        module.exports = global.document ?\n            factory(global, true) :\n            function (w) {\n                if (!w.document) {\n                    throw new Error(\"Geetest requires a window with a document\");\n                }\n                return factory(w);\n            };\n    } else {\n        factory(global);\n    }\n})(typeof window !== \"undefined\" ? window : this, function (window, noGlobal) {\n    \"use strict\";\n    if (typeof window === 'undefined') {\n        throw new Error('Geetest requires browser environment');\n    }\n    var document = window.document;\n    var Math = window.Math;\n    var head = document.getElementsByTagName(\"head\")[0];\n\n    function _Object(obj) {\n        this._obj = obj;\n    }\n\n    _Object.prototype = {\n        _each: function (process) {\n            var _obj = this._obj;\n            for (var k in _obj) {\n                if (_obj.hasOwnProperty(k)) {\n                    process(k, _obj[k]);\n                }\n            }\n            return this;\n        }\n    };\n    function Config(config) {\n        var self = this;\n        new _Object(config)._each(function (key, value) {\n            self[key] = value;\n        });\n    }\n\n    Config.prototype = {\n        api_server: 'api.geetest.com',\n        protocol: 'http://',\n        type_path: '/gettype.php',\n        fallback_config: {\n            slide: {\n                static_servers: [\"static.geetest.com\", \"dn-staticdown.qbox.me\"],\n                type: 'slide',\n                slide: '/static/js/geetest.0.0.0.js'\n            },\n            fullpage: {\n                static_servers: [\"static.geetest.com\", \"dn-staticdown.qbox.me\"],\n                type: 'fullpage',\n                fullpage: '/static/js/fullpage.0.0.0.js'\n            }\n        },\n        _get_fallback_config: function () {\n            var self = this;\n            if (isString(self.type)) {\n                return self.fallback_config[self.type];\n            } else if (self.new_captcha) {\n                return self.fallback_config.fullpage;\n            } else {\n                return self.fallback_config.slide;\n            }\n        },\n        _extend: function (obj) {\n            var self = this;\n            new _Object(obj)._each(function (key, value) {\n                self[key] = value;\n            })\n        }\n    };\n    var isNumber = function (value) {\n        return (typeof value === 'number');\n    };\n    var isString = function (value) {\n        return (typeof value === 'string');\n    };\n    var isBoolean = function (value) {\n        return (typeof value === 'boolean');\n    };\n    var isObject = function (value) {\n        return (typeof value === 'object' && value !== null);\n    };\n    var isFunction = function (value) {\n        return (typeof value === 'function');\n    };\n    var callbacks = {};\n    var status = {};\n    var random = function () {\n        return parseInt(Math.random() * 10000) + (new Date()).valueOf();\n    };\n    var loadScript = function (url, cb) {\n        var script = document.createElement(\"script\");\n        script.charset = \"UTF-8\";\n        script.async = true;\n        script.onerror = function () {\n            cb(true);\n        };\n        var loaded = false;\n        script.onload = script.onreadystatechange = function () {\n            if (!loaded &&\n                (!script.readyState ||\n                \"loaded\" === script.readyState ||\n                \"complete\" === script.readyState)) {\n\n                loaded = true;\n                setTimeout(function () {\n                    cb(false);\n                }, 0);\n            }\n        };\n        script.src = url;\n        head.appendChild(script);\n    };\n    var normalizeDomain = function (domain) {\n        return domain.replace(/^https?:\\/\\/|\\/$/g, '');\n    };\n    var normalizePath = function (path) {\n        path = path.replace(/\\/+/g, '/');\n        if (path.indexOf('/') !== 0) {\n            path = '/' + path;\n        }\n        return path;\n    };\n    var normalizeQuery = function (query) {\n        if (!query) {\n            return '';\n        }\n        var q = '?';\n        new _Object(query)._each(function (key, value) {\n            if (isString(value) || isNumber(value) || isBoolean(value)) {\n                q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';\n            }\n        });\n        if (q === '?') {\n            q = '';\n        }\n        return q.replace(/&$/, '');\n    };\n    var makeURL = function (protocol, domain, path, query) {\n        domain = normalizeDomain(domain);\n\n        var url = normalizePath(path) + normalizeQuery(query);\n        if (domain) {\n            url = protocol + domain + url;\n        }\n\n        return url;\n    };\n    var load = function (protocol, domains, path, query, cb) {\n        var tryRequest = function (at) {\n\n            var url = makeURL(protocol, domains[at], path, query);\n            loadScript(url, function (err) {\n                if (err) {\n                    if (at >= domains.length - 1) {\n                        cb(true);\n                    } else {\n                        tryRequest(at + 1);\n                    }\n                } else {\n                    cb(false);\n                }\n            });\n        };\n        tryRequest(0);\n    };\n    var jsonp = function (domains, path, config, callback) {\n        if (isObject(config.getLib)) {\n            config._extend(config.getLib);\n            callback(config);\n            return;\n        }\n        if (config.offline) {\n            callback(config._get_fallback_config());\n            return;\n        }\n        var cb = \"geetest_\" + random();\n        window[cb] = function (data) {\n            if (data.status === 'success') {\n                callback(data.data);\n            } else if (!data.status) {\n                callback(data);\n            } else {\n                callback(config._get_fallback_config());\n            }\n            window[cb] = undefined;\n            try {\n                delete window[cb];\n            } catch (e) {\n            }\n        };\n        load(config.protocol, domains, path, {\n            gt: config.gt,\n            callback: cb\n        }, function (err) {\n            if (err) {\n                callback(config._get_fallback_config());\n            }\n        });\n    };\n    var throwError = function (errorType, config) {\n        var errors = {\n            networkError: '网络错误'\n        };\n        if (typeof config.onError === 'function') {\n            config.onError(errors[errorType]);\n        } else {\n            throw new Error(errors[errorType]);\n        }\n    };\n    var detect = function () {\n        return !!window.Geetest;\n    };\n    if (detect()) {\n        status.slide = \"loaded\";\n    }\n    var initGeetest = function (userConfig, callback) {\n        var config = new Config(userConfig);\n        if (userConfig.https) {\n            config.protocol = 'https://';\n        } else if (!userConfig.protocol) {\n            config.protocol = window.location.protocol + '//';\n        }\n        jsonp([config.api_server || config.apiserver], config.type_path, config, function (newConfig) {\n            var type = newConfig.type;\n            var init = function () {\n                config._extend(newConfig);\n                callback(new window.Geetest(config));\n            };\n            callbacks[type] = callbacks[type] || [];\n            var s = status[type] || 'init';\n            if (s === 'init') {\n                status[type] = 'loading';\n                callbacks[type].push(init);\n                load(config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {\n                    if (err) {\n                        status[type] = 'fail';\n                        throwError('networkError', config);\n                    } else {\n                        status[type] = 'loaded';\n                        var cbs = callbacks[type];\n                        for (var i = 0, len = cbs.length; i < len; i = i + 1) {\n                            var cb = cbs[i];\n                            if (isFunction(cb)) {\n                                cb();\n                            }\n                        }\n                        callbacks[type] = [];\n                    }\n                });\n            } else if (s === \"loaded\") {\n                init();\n            } else if (s === \"fail\") {\n                throwError('networkError', config);\n            } else if (s === \"loading\") {\n                callbacks[type].push(init);\n            }\n        });\n    };\n    window.initGeetest = initGeetest;\n    return initGeetest;\n});\n\n"
  },
  {
    "path": "static/login.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <title>gt-php-sdk-demo</title>\n    <style>\n        body {\n            margin: 50px 0;\n            text-align: center;\n        }\n        .inp {\n            border: 1px solid gray;\n            padding: 0 10px;\n            width: 200px;\n            height: 30px;\n            font-size: 18px;\n        }\n        .btn {\n            border: 1px solid gray;\n            width: 100px;\n            height: 30px;\n            font-size: 18px;\n            cursor: pointer;\n        }\n        #embed-captcha {\n            width: 300px;\n            margin: 0 auto;\n        }\n        .show {\n            display: block;\n        }\n        .hide {\n            display: none;\n        }\n        #notice {\n            color: red;\n        }\n        /* 以下遮罩层为demo.用户可自行设计实现 */\n        #mask {\n            display: none;\n            position: fixed;\n            text-align: center;\n            left: 0;\n            top: 0;\n            width: 100%;\n            height: 100%;\n            background-color: rgba(0, 0, 0, 0.5);\n            overflow: auto;\n        }\n        /* 可自行设计实现captcha的位置大小 */\n        .popup-mobile {\n            position: relative;\n        }\n        #popup-captcha-mobile {\n            position: fixed;\n            display: none;\n            left: 50%;\n            top: 50%;\n            transform: translate(-50%, -50%);\n            -webkit-transform: translate(-50%, -50%);\n            z-index: 9999;\n        }\n    </style>\n</head>\n<body>\n<h1>极验验证SDKDemo</h1>\n<br><br>\n<hr>\n<br><br>\n\n<!-- 为使用方便，直接使用jquery.js库，如您代码中不需要，可以去掉 -->\n<script src=\"http://code.jquery.com/jquery-1.12.3.min.js\"></script>\n<!-- 引入封装了failback的接口--initGeetest -->\n<script src=\"./gt.js\"></script>\n\n<!-- 若是https，使用以下接口 -->\n<!-- <script src=\"https://code.jquery.com/jquery-1.12.3.min.js\"></script> -->\n<!-- <script src=\"https://static.geetest.com/static/tools/gt.js\"></script> -->\n\n<div class=\"popup\">\n    <h2>弹出式Demo，使用ajax形式提交二次验证码所需的验证结果值</h2>\n    <br>\n    <p>\n        <labe>用户名：</labe>\n        <input id=\"username1\" class=\"inp\" type=\"text\" value=\"极验验证\">\n    </p>\n    <br>\n    <p>\n        <label>密&nbsp;&nbsp;&nbsp;&nbsp;码：</label>\n        <input id=\"password1\" class=\"inp\" type=\"password\" value=\"123456\">\n    </p>\n\n    <br>\n    <input class=\"btn\" id=\"popup-submit\" type=\"submit\" value=\"提交\">\n\n    <div id=\"popup-captcha\"></div>\n</div>\n\n<script>\n    var handlerPopup = function (captchaObj) {\n        // 成功的回调\n        captchaObj.onSuccess(function () {\n            var validate = captchaObj.getValidate();\n            $.ajax({\n                url: \"../web/VerifyLoginServlet.php\", // 进行二次验证\n                type: \"post\",\n                dataType: \"json\",\n                data: {\n                    type: \"pc\",\n                    username: $('#username1').val(),\n                    password: $('#password1').val(),\n                    geetest_challenge: validate.geetest_challenge,\n                    geetest_validate: validate.geetest_validate,\n                    geetest_seccode: validate.geetest_seccode\n                },\n                success: function (data) {\n                    if (data && (data.status === \"success\")) {\n                        $(document.body).html('<h1>登录成功</h1>');\n                    } else {\n                        $(document.body).html('<h1>登录失败</h1>');\n                    }\n                }\n            });\n        });\n        $(\"#popup-submit\").click(function () {\n            captchaObj.show();\n        });\n        // 将验证码加到id为captcha的元素里\n        captchaObj.appendTo(\"#popup-captcha\");\n        // 更多接口参考：http://www.geetest.com/install/sections/idx-client-sdk.html\n    };\n    // 验证开始需要向网站主后台获取id，challenge，success（是否启用failback）\n    $.ajax({\n        url: \"../web/StartCaptchaServlet.php?type=pc&t=\" + (new Date()).getTime(), // 加随机数防止缓存\n        type: \"get\",\n        dataType: \"json\",\n        success: function (data) {\n            // 使用initGeetest接口\n            // 参数1：配置参数\n            // 参数2：回调，回调的第一个参数验证码对象，之后可以使用它做appendTo之类的事件\n            initGeetest({\n                gt: data.gt,\n                challenge: data.challenge,\n                product: \"popup\", // 产品形式，包括：float，embed，popup。注意只对PC版验证码有效\n                offline: !data.success // 表示用户后台检测极验服务器是否宕机，一般不需要关注\n                // 更多配置参数请参见：http://www.geetest.com/install/sections/idx-client-sdk.html#config\n            }, handlerPopup);\n        }\n    });\n</script>\n<br><br>\n<hr>\n<br><br>\n<form class=\"popup\" action=\"../web/VerifyLoginServlet.php\" method=\"post\">\n    <h2>嵌入式Demo，使用表单形式提交二次验证所需的验证结果值</h2>\n        <input class=\"inp\" name=\"type\" type=\"hidden\" value=\"pc\">\n    <br>\n    <p>\n        <label for=\"username2\">用户名：</label>\n        <input class=\"inp\" id=\"username2\" type=\"text\" value=\"极验验证\">\n    </p>\n    <br>\n    <p>\n        <label for=\"password2\">密&nbsp;&nbsp;&nbsp;&nbsp;码：</label>\n        <input class=\"inp\" id=\"password2\" type=\"password\" value=\"123456\">\n    </p>\n\n    <div id=\"embed-captcha\"></div>\n    <p id=\"wait\" class=\"show\">正在加载验证码......</p>\n    <p id=\"notice\" class=\"hide\">请先拖动验证码到相应位置</p>\n\n    <br>\n    <input class=\"btn\" id=\"embed-submit\" type=\"submit\" value=\"提交\">\n</form>\n\n<script>\n    var handlerEmbed = function (captchaObj) {\n        $(\"#embed-submit\").click(function (e) {\n            var validate = captchaObj.getValidate();\n            if (!validate) {\n                $(\"#notice\")[0].className = \"show\";\n                setTimeout(function () {\n                    $(\"#notice\")[0].className = \"hide\";\n                }, 2000);\n                e.preventDefault();\n            }\n        });\n        // 将验证码加到id为captcha的元素里，同时会有三个input的值：geetest_challenge, geetest_validate, geetest_seccode\n        captchaObj.appendTo(\"#embed-captcha\");\n        captchaObj.onReady(function () {\n            $(\"#wait\")[0].className = \"hide\";\n        });\n        // 更多接口参考：http://www.geetest.com/install/sections/idx-client-sdk.html\n    };\n    $.ajax({\n        // 获取id，challenge，success（是否启用failback）\n        url: \"../web/StartCaptchaServlet.php?type=pc&t=\" + (new Date()).getTime(), // 加随机数防止缓存\n        type: \"get\",\n        dataType: \"json\",\n        success: function (data) {\n            // 使用initGeetest接口\n            // 参数1：配置参数\n            // 参数2：回调，回调的第一个参数验证码对象，之后可以使用它做appendTo之类的事件\n            initGeetest({\n                gt: data.gt,\n                challenge: data.challenge,\n                product: \"embed\", // 产品形式，包括：float，embed，popup。注意只对PC版验证码有效\n                offline: !data.success // 表示用户后台检测极验服务器是否宕机，一般不需要关注\n                // 更多配置参数请参见：http://www.geetest.com/install/sections/idx-client-sdk.html#config\n            }, handlerEmbed);\n        }\n    });\n</script>\n<br><br>\n<hr>\n<br><br>\n<div class=\"popup-mobile\">\n    <h2>移动端手动实现弹出式Demo</h2>\n    <br>\n    <p>\n        <labe for=\"username3\">用户名：</labe>\n        <input id=\"username3\" class=\"inp\" type=\"text\" value=\"极验验证\">\n    </p>\n    <br>\n    <p>\n        <label for=\"password3\">密&nbsp;&nbsp;&nbsp;&nbsp;码：</label>\n        <input id=\"password3\" class=\"inp\" type=\"password\" value=\"123456\">\n    </p>\n    <br>\n    <input class=\"btn\" id=\"popup-submit-mobile\" type=\"submit\" value=\"提交\">\n    <div id=\"mask\"></div>\n    <div id=\"popup-captcha-mobile\"></div>\n</div>\n\n<script>\n    $(\"#mask\").click(function () {\n        $(\"#mask, #popup-captcha-mobile\").hide();\n    });\n    $(\"#popup-submit-mobile\").click(function () {\n        $(\"#mask, #popup-captcha-mobile\").show();\n    });\n    var handlerPopupMobile = function (captchaObj) {\n        // 将验证码加到id为captcha的元素里\n        captchaObj.appendTo(\"#popup-captcha-mobile\");\n        //拖动验证成功后两秒(可自行设置时间)自动发生跳转等行为\n        captchaObj.onSuccess(function () {\n            var validate = captchaObj.getValidate();\n            $.ajax({\n                url: \"../web/VerifyLoginServlet.php\", // 进行二次验证\n                type: \"post\",\n                dataType: \"json\",\n                data: {\n                    // 二次验证所需的三个值\n                    type: \"mobile\",\n                    username: $('#username3').val(),\n                    password: $('#password3').val(),\n                    geetest_challenge: validate.geetest_challenge,\n                    geetest_validate: validate.geetest_validate,\n                    geetest_seccode: validate.geetest_seccode\n                },\n                success: function (data) {\n                    if (data && (data.status === \"success\")) {\n                        $(document.body).html('<h1>登录成功</h1>');\n                    } else {\n                        $(document.body).html('<h1>登录失败</h1>');\n                    }\n                }\n            });\n        });\n        // 更多接口参考：http://www.geetest.com/install/sections/idx-client-sdk.html\n    };\n    $.ajax({\n        // 获取id，challenge，success（是否启用failback）\n        url: \"../web/StartCaptchaServlet.php?type=mobile&t=\" + (new Date()).getTime(), // 加随机数防止缓存\n        type: \"get\",\n        dataType: \"json\",\n        success: function (data) {\n            // 使用initGeetest接口\n            // 参数1：配置参数\n            // 参数2：回调，回调的第一个参数验证码对象，之后可以使用它做appendTo之类的事件\n            initGeetest({\n                gt: data.gt,\n                challenge: data.challenge,\n                offline: !data.success // 表示用户后台检测极验服务器是否宕机，一般不需要关注\n                // 更多配置参数请参见：http://www.geetest.com/install/sections/idx-client-sdk.html#config\n            }, handlerPopupMobile);\n        }\n    });\n</script>\n</body>\n</html>"
  },
  {
    "path": "web/StartCaptchaServlet.php",
    "content": "<?php \n/**\n * 使用Get的方式返回：challenge和capthca_id 此方式以实现前后端完全分离的开发模式 专门实现failback\n * @author Tanxu\n */\n//error_reporting(0);\nrequire_once dirname(dirname(__FILE__)) . '/lib/class.geetestlib.php';\nrequire_once dirname(dirname(__FILE__)) . '/config/config.php';\nif($_GET['type'] == 'pc'){\n\t$GtSdk = new GeetestLib(CAPTCHA_ID, PRIVATE_KEY);\n}elseif ($_GET['type'] == 'mobile') {\n\t$GtSdk = new GeetestLib(MOBILE_CAPTCHA_ID, MOBILE_PRIVATE_KEY);\n}\nsession_start();\n$user_id = \"test\";\n$status = $GtSdk->pre_process($user_id);\n$_SESSION['gtserver'] = $status;\n$_SESSION['user_id'] = $user_id;\necho $GtSdk->get_response_str();\n ?>"
  },
  {
    "path": "web/VerifyLoginServlet.php",
    "content": "<?php \n/**\n * 输出二次验证结果,本文件示例只是简单的输出 Yes or No\n */\n// error_reporting(0);\nrequire_once dirname(dirname(__FILE__)) . '/lib/class.geetestlib.php';\nrequire_once dirname(dirname(__FILE__)) . '/config/config.php';\nsession_start();\nif($_POST['type'] == 'pc'){\n    $GtSdk = new GeetestLib(CAPTCHA_ID, PRIVATE_KEY);\n}elseif ($_POST['type'] == 'mobile') {\n    $GtSdk = new GeetestLib(MOBILE_CAPTCHA_ID, MOBILE_PRIVATE_KEY);\n}\n\n$user_id = $_SESSION['user_id'];\nif ($_SESSION['gtserver'] == 1) {   //服务器正常\n    $result = $GtSdk->success_validate($_POST['geetest_challenge'], $_POST['geetest_validate'], $_POST['geetest_seccode'], $user_id);\n    if ($result) {\n        echo '{\"status\":\"success\"}';\n    } else{\n        echo '{\"status\":\"fail\"}';\n    }\n}else{  //服务器宕机,走failback模式\n    if ($GtSdk->fail_validate($_POST['geetest_challenge'],$_POST['geetest_validate'],$_POST['geetest_seccode'])) {\n        echo '{\"status\":\"success\"}';\n    }else{\n        echo '{\"status\":\"fail\"}';\n    }\n}\n?>\n"
  }
]