├── LICENSE ├── README.md ├── app.yaml ├── composer.json └── src ├── ReCaptcha ├── ReCaptcha.php ├── RequestMethod.php ├── RequestMethod │ ├── Curl.php │ ├── CurlPost.php │ ├── Post.php │ ├── Socket.php │ └── SocketPost.php ├── RequestParameters.php └── Response.php └── autoload.php /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Google Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # reCAPTCHA PHP client library 2 | 3 | [![Build Status](https://travis-ci.org/google/recaptcha.svg)](https://travis-ci.org/google/recaptcha) 4 | [![Coverage Status](https://coveralls.io/repos/github/google/recaptcha/badge.svg)](https://coveralls.io/github/google/recaptcha) 5 | [![Latest Stable Version](https://poser.pugx.org/google/recaptcha/v/stable.svg)](https://packagist.org/packages/google/recaptcha) 6 | [![Total Downloads](https://poser.pugx.org/google/recaptcha/downloads.svg)](https://packagist.org/packages/google/recaptcha) 7 | 8 | reCAPTCHA is a free CAPTCHA service that protects websites from spam and abuse. 9 | This is a PHP library that wraps up the server-side verification step required 10 | to process responses from the reCAPTCHA service. This client supports both v2 11 | and v3. 12 | 13 | - reCAPTCHA: https://www.google.com/recaptcha 14 | - This repo: https://github.com/google/recaptcha 15 | - Hosted demo: https://recaptcha-demo.appspot.com/ 16 | - Version: 1.3.0 17 | - License: BSD, see [LICENSE](LICENSE) 18 | 19 | ## Installation 20 | 21 | ### Composer (recommended) 22 | 23 | Use [Composer](https://getcomposer.org) to install this library from Packagist: 24 | [`google/recaptcha`](https://packagist.org/packages/google/recaptcha) 25 | 26 | Run the following command from your project directory to add the dependency: 27 | 28 | ```sh 29 | composer require google/recaptcha "^1.3" 30 | ``` 31 | 32 | Alternatively, add the dependency directly to your `composer.json` file: 33 | 34 | ```json 35 | "require": { 36 | "google/recaptcha": "^1.3" 37 | } 38 | ``` 39 | 40 | ### Support for earlier versions of PHP 41 | 42 | The 1.3 release moves to PHP 8 and up. For earlier versions, you will need to 43 | stay with the 1.2 releases. 44 | 45 | ### Direct download 46 | 47 | Download the [ZIP file](https://github.com/google/recaptcha/archive/master.zip) 48 | and extract into your project. An autoloader script is provided in 49 | `src/autoload.php` which you can require into your script. For example: 50 | 51 | ```php 52 | require_once '/path/to/recaptcha/src/autoload.php'; 53 | $recaptcha = new \ReCaptcha\ReCaptcha($secret); 54 | ``` 55 | 56 | The classes in the project are structured according to the 57 | [PSR-4](https://www.php-fig.org/psr/psr-4/) standard, so you can also use your 58 | own autoloader or require the needed files directly in your code. 59 | 60 | ## Usage 61 | 62 | First obtain the appropriate keys for the type of reCAPTCHA you wish to 63 | integrate for v2 at https://www.google.com/recaptcha/admin or v3 at 64 | https://g.co/recaptcha/v3. 65 | 66 | Then follow the [integration guide on the developer 67 | site](https://developers.google.com/recaptcha/intro) to add the reCAPTCHA 68 | functionality into your frontend. 69 | 70 | This library comes in when you need to verify the user's response. On the PHP 71 | side you need the response from the reCAPTCHA service and secret key from your 72 | credentials. Instantiate the `ReCaptcha` class with your secret key, specify any 73 | additional validation rules, and then call `verify()` with the reCAPTCHA 74 | response (usually in `$_POST['g-recaptcha-response']` or the response from 75 | `grecaptcha.execute()` in JS which is in `$gRecaptchaResponse` in the example) 76 | and user's IP address. For example: 77 | 78 | ```php 79 | setExpectedHostname('recaptcha-demo.appspot.com') 82 | ->verify($gRecaptchaResponse, $remoteIp); 83 | if ($resp->isSuccess()) { 84 | // Verified! 85 | } else { 86 | $errors = $resp->getErrorCodes(); 87 | } 88 | ``` 89 | 90 | The following methods are available: 91 | 92 | - `setExpectedHostname($hostname)`: ensures the hostname matches. You must do 93 | this if you have disabled "Domain/Package Name Validation" for your 94 | credentials. 95 | - `setExpectedApkPackageName($apkPackageName)`: if you're verifying a response 96 | from an Android app. Again, you must do this if you have disabled 97 | "Domain/Package Name Validation" for your credentials. 98 | - `setExpectedAction($action)`: ensures the action matches for the v3 API. 99 | - `setScoreThreshold($threshold)`: set a score threshold for responses from the 100 | v3 API 101 | - `setChallengeTimeout($timeoutSeconds)`: set a timeout between the user passing 102 | the reCAPTCHA and your server processing it. 103 | 104 | Each of the `set`\*`()` methods return the `ReCaptcha` instance so you can chain 105 | them together. For example: 106 | 107 | ```php 108 | setExpectedHostname('recaptcha-demo.appspot.com') 111 | ->setExpectedAction('homepage') 112 | ->setScoreThreshold(0.5) 113 | ->verify($gRecaptchaResponse, $remoteIp); 114 | 115 | if ($resp->isSuccess()) { 116 | // Verified! 117 | } else { 118 | $errors = $resp->getErrorCodes(); 119 | } 120 | ``` 121 | 122 | You can find the constants for the libraries error codes in the `ReCaptcha` 123 | class constants, e.g. `ReCaptcha::E_HOSTNAME_MISMATCH` 124 | 125 | For more details on usage and structure, see [ARCHITECTURE](ARCHITECTURE.md). 126 | 127 | ### Examples 128 | 129 | You can see examples of each reCAPTCHA type in [examples/](examples/). You can 130 | run the examples locally by using the Composer script: 131 | 132 | ```sh 133 | composer run-script serve-examples 134 | ``` 135 | 136 | This makes use of the in-built PHP dev server to host the examples at 137 | http://localhost:8080/ 138 | 139 | These are also hosted on Google AppEngine Flexible environment at 140 | https://recaptcha-demo.appspot.com/. This is configured by 141 | [`app.yaml`](./app.yaml) which you can also use to [deploy to your own AppEngine 142 | project](https://cloud.google.com/appengine/docs/flexible/php/download). 143 | 144 | ## Contributing 145 | 146 | No one ever has enough engineers, so we're very happy to accept contributions 147 | via Pull Requests. For details, see [CONTRIBUTING](CONTRIBUTING.md) 148 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | runtime: php 2 | env: flex 3 | 4 | skip_files: 5 | - tests 6 | 7 | runtime_config: 8 | document_root: examples 9 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google/recaptcha", 3 | "description": "Client library for reCAPTCHA, a free service that protects websites from spam and abuse.", 4 | "type": "library", 5 | "keywords": ["recaptcha", "captcha", "spam", "abuse"], 6 | "homepage": "https://www.google.com/recaptcha/", 7 | "license": "BSD-3-Clause", 8 | "support": { 9 | "forum": "https://groups.google.com/forum/#!forum/recaptcha", 10 | "source": "https://github.com/google/recaptcha" 11 | }, 12 | "require": { 13 | "php": ">=8" 14 | }, 15 | "require-dev": { 16 | "phpunit/phpunit": "^10", 17 | "friendsofphp/php-cs-fixer": "^3.14", 18 | "php-coveralls/php-coveralls": "^2.5" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "ReCaptcha\\": "src/ReCaptcha" 23 | } 24 | }, 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "1.3.x-dev" 28 | } 29 | }, 30 | "scripts": { 31 | "lint": "PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer -vvv fix --using-cache=no --dry-run .", 32 | "lint-fix": "PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer -vvv fix --using-cache=no .", 33 | "test": "XDEBUG_MODE=coverage vendor/bin/phpunit", 34 | "serve-examples": "@php -S localhost:8080 -t examples" 35 | }, 36 | "config": { 37 | "process-timeout": 0 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ReCaptcha/ReCaptcha.php: -------------------------------------------------------------------------------- 1 | secret = $secret; 150 | $this->requestMethod = (is_null($requestMethod)) ? new RequestMethod\Post() : $requestMethod; 151 | } 152 | 153 | /** 154 | * Calls the reCAPTCHA siteverify API to verify whether the user passes 155 | * CAPTCHA test and additionally runs any specified additional checks 156 | * 157 | * @param string $response The user response token provided by reCAPTCHA, verifying the user on your site. 158 | * @param string $remoteIp The end user's IP address. 159 | * @return Response Response from the service. 160 | */ 161 | public function verify($response, $remoteIp = null) 162 | { 163 | // Discard empty solution submissions 164 | if (empty($response)) { 165 | $recaptchaResponse = new Response(false, array(self::E_MISSING_INPUT_RESPONSE)); 166 | return $recaptchaResponse; 167 | } 168 | 169 | $params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION); 170 | $rawResponse = $this->requestMethod->submit($params); 171 | $initialResponse = Response::fromJson($rawResponse); 172 | $validationErrors = array(); 173 | 174 | if (isset($this->hostname) && strcasecmp($this->hostname, $initialResponse->getHostname()) !== 0) { 175 | $validationErrors[] = self::E_HOSTNAME_MISMATCH; 176 | } 177 | 178 | if (isset($this->apkPackageName) && strcasecmp($this->apkPackageName, $initialResponse->getApkPackageName()) !== 0) { 179 | $validationErrors[] = self::E_APK_PACKAGE_NAME_MISMATCH; 180 | } 181 | 182 | if (isset($this->action) && strcasecmp($this->action, $initialResponse->getAction()) !== 0) { 183 | $validationErrors[] = self::E_ACTION_MISMATCH; 184 | } 185 | 186 | if (isset($this->threshold) && $this->threshold > $initialResponse->getScore()) { 187 | $validationErrors[] = self::E_SCORE_THRESHOLD_NOT_MET; 188 | } 189 | 190 | if (isset($this->timeoutSeconds)) { 191 | $challengeTs = strtotime($initialResponse->getChallengeTs()); 192 | 193 | if ($challengeTs > 0 && time() - $challengeTs > $this->timeoutSeconds) { 194 | $validationErrors[] = self::E_CHALLENGE_TIMEOUT; 195 | } 196 | } 197 | 198 | if (empty($validationErrors)) { 199 | return $initialResponse; 200 | } 201 | 202 | return new Response( 203 | false, 204 | array_merge($initialResponse->getErrorCodes(), $validationErrors), 205 | $initialResponse->getHostname(), 206 | $initialResponse->getChallengeTs(), 207 | $initialResponse->getApkPackageName(), 208 | $initialResponse->getScore(), 209 | $initialResponse->getAction() 210 | ); 211 | } 212 | 213 | /** 214 | * Provide a hostname to match against in verify() 215 | * This should be without a protocol or trailing slash, e.g. www.google.com 216 | * 217 | * @param string $hostname Expected hostname 218 | * @return ReCaptcha Current instance for fluent interface 219 | */ 220 | public function setExpectedHostname($hostname) 221 | { 222 | $this->hostname = $hostname; 223 | return $this; 224 | } 225 | 226 | /** 227 | * Provide an APK package name to match against in verify() 228 | * 229 | * @param string $apkPackageName Expected APK package name 230 | * @return ReCaptcha Current instance for fluent interface 231 | */ 232 | public function setExpectedApkPackageName($apkPackageName) 233 | { 234 | $this->apkPackageName = $apkPackageName; 235 | return $this; 236 | } 237 | 238 | /** 239 | * Provide an action to match against in verify() 240 | * This should be set per page. 241 | * 242 | * @param string $action Expected action 243 | * @return ReCaptcha Current instance for fluent interface 244 | */ 245 | public function setExpectedAction($action) 246 | { 247 | $this->action = $action; 248 | return $this; 249 | } 250 | 251 | /** 252 | * Provide a threshold to meet or exceed in verify() 253 | * Threshold should be a float between 0 and 1 which will be tested as response >= threshold. 254 | * 255 | * @param float $threshold Expected threshold 256 | * @return ReCaptcha Current instance for fluent interface 257 | */ 258 | public function setScoreThreshold($threshold) 259 | { 260 | $this->threshold = floatval($threshold); 261 | return $this; 262 | } 263 | 264 | /** 265 | * Provide a timeout in seconds to test against the challenge timestamp in verify() 266 | * 267 | * @param int $timeoutSeconds Expected hostname 268 | * @return ReCaptcha Current instance for fluent interface 269 | */ 270 | public function setChallengeTimeout($timeoutSeconds) 271 | { 272 | $this->timeoutSeconds = $timeoutSeconds; 273 | return $this; 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/ReCaptcha/RequestMethod.php: -------------------------------------------------------------------------------- 1 | curl = (is_null($curl)) ? new Curl() : $curl; 69 | $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; 70 | } 71 | 72 | /** 73 | * Submit the cURL request with the specified parameters. 74 | * 75 | * @param RequestParameters $params Request parameters 76 | * @return string Body of the reCAPTCHA response 77 | */ 78 | public function submit(RequestParameters $params) 79 | { 80 | $handle = $this->curl->init($this->siteVerifyUrl); 81 | 82 | $options = array( 83 | CURLOPT_POST => true, 84 | CURLOPT_POSTFIELDS => $params->toQueryString(), 85 | CURLOPT_HTTPHEADER => array( 86 | 'Content-Type: application/x-www-form-urlencoded' 87 | ), 88 | CURLINFO_HEADER_OUT => false, 89 | CURLOPT_HEADER => false, 90 | CURLOPT_RETURNTRANSFER => true, 91 | CURLOPT_SSL_VERIFYPEER => true 92 | ); 93 | $this->curl->setoptArray($handle, $options); 94 | 95 | $response = $this->curl->exec($handle); 96 | $this->curl->close($handle); 97 | 98 | if ($response !== false) { 99 | return $response; 100 | } 101 | 102 | return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/ReCaptcha/RequestMethod/Post.php: -------------------------------------------------------------------------------- 1 | siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; 60 | } 61 | 62 | /** 63 | * Submit the POST request with the specified parameters. 64 | * 65 | * @param RequestParameters $params Request parameters 66 | * @return string Body of the reCAPTCHA response 67 | */ 68 | public function submit(RequestParameters $params) 69 | { 70 | $options = array( 71 | 'http' => array( 72 | 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 73 | 'method' => 'POST', 74 | 'content' => $params->toQueryString(), 75 | // Force the peer to validate (not needed in 5.6.0+, but still works) 76 | 'verify_peer' => true, 77 | ), 78 | ); 79 | $context = stream_context_create($options); 80 | $response = file_get_contents($this->siteVerifyUrl, false, $context); 81 | 82 | if ($response !== false) { 83 | return $response; 84 | } 85 | 86 | return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/ReCaptcha/RequestMethod/Socket.php: -------------------------------------------------------------------------------- 1 | handle = fsockopen($hostname, $port, $errno, $errstr, (is_null($timeout) ? ini_get("default_socket_timeout") : $timeout)); 59 | 60 | if ($this->handle != false && $errno === 0 && $errstr === '') { 61 | return $this->handle; 62 | } 63 | return false; 64 | } 65 | 66 | /** 67 | * fwrite 68 | * 69 | * @see http://php.net/fwrite 70 | * @param string $string 71 | * @param int $length 72 | * @return int | bool 73 | */ 74 | public function fwrite($string, $length = null) 75 | { 76 | return fwrite($this->handle, $string, (is_null($length) ? strlen($string) : $length)); 77 | } 78 | 79 | /** 80 | * fgets 81 | * 82 | * @see http://php.net/fgets 83 | * @param int $length 84 | * @return string 85 | */ 86 | public function fgets($length = null) 87 | { 88 | return fgets($this->handle, $length); 89 | } 90 | 91 | /** 92 | * feof 93 | * 94 | * @see http://php.net/feof 95 | * @return bool 96 | */ 97 | public function feof() 98 | { 99 | return feof($this->handle); 100 | } 101 | 102 | /** 103 | * fclose 104 | * 105 | * @see http://php.net/fclose 106 | * @return bool 107 | */ 108 | public function fclose() 109 | { 110 | return fclose($this->handle); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/ReCaptcha/RequestMethod/SocketPost.php: -------------------------------------------------------------------------------- 1 | socket = (is_null($socket)) ? new Socket() : $socket; 65 | $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; 66 | } 67 | 68 | /** 69 | * Submit the POST request with the specified parameters. 70 | * 71 | * @param RequestParameters $params Request parameters 72 | * @return string Body of the reCAPTCHA response 73 | */ 74 | public function submit(RequestParameters $params) 75 | { 76 | $errno = 0; 77 | $errstr = ''; 78 | $urlParsed = parse_url($this->siteVerifyUrl); 79 | 80 | if (false === $this->socket->fsockopen('ssl://' . $urlParsed['host'], 443, $errno, $errstr, 30)) { 81 | return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; 82 | } 83 | 84 | $content = $params->toQueryString(); 85 | 86 | $request = "POST " . $urlParsed['path'] . " HTTP/1.0\r\n"; 87 | $request .= "Host: " . $urlParsed['host'] . "\r\n"; 88 | $request .= "Content-Type: application/x-www-form-urlencoded\r\n"; 89 | $request .= "Content-length: " . strlen($content) . "\r\n"; 90 | $request .= "Connection: close\r\n\r\n"; 91 | $request .= $content . "\r\n\r\n"; 92 | 93 | $this->socket->fwrite($request); 94 | $response = ''; 95 | 96 | while (!$this->socket->feof()) { 97 | $response .= $this->socket->fgets(4096); 98 | } 99 | 100 | $this->socket->fclose(); 101 | 102 | if (0 !== strpos($response, 'HTTP/1.0 200 OK')) { 103 | return '{"success": false, "error-codes": ["'.ReCaptcha::E_BAD_RESPONSE.'"]}'; 104 | } 105 | 106 | $parts = preg_split("#\n\s*\n#Uis", $response); 107 | 108 | return $parts[1]; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/ReCaptcha/RequestParameters.php: -------------------------------------------------------------------------------- 1 | secret = $secret; 77 | $this->response = $response; 78 | $this->remoteIp = $remoteIp; 79 | $this->version = $version; 80 | } 81 | 82 | /** 83 | * Array representation. 84 | * 85 | * @return array Array formatted parameters. 86 | */ 87 | public function toArray() 88 | { 89 | $params = array('secret' => $this->secret, 'response' => $this->response); 90 | 91 | if (!is_null($this->remoteIp)) { 92 | $params['remoteip'] = $this->remoteIp; 93 | } 94 | 95 | if (!is_null($this->version)) { 96 | $params['version'] = $this->version; 97 | } 98 | 99 | return $params; 100 | } 101 | 102 | /** 103 | * Query string representation for HTTP request. 104 | * 105 | * @return string Query string formatted parameters. 106 | */ 107 | public function toQueryString() 108 | { 109 | return http_build_query($this->toArray(), '', '&'); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/ReCaptcha/Response.php: -------------------------------------------------------------------------------- 1 | success = $success; 129 | $this->hostname = $hostname; 130 | $this->challengeTs = $challengeTs; 131 | $this->apkPackageName = $apkPackageName; 132 | $this->score = $score; 133 | $this->action = $action; 134 | $this->errorCodes = $errorCodes; 135 | } 136 | 137 | /** 138 | * Is success? 139 | * 140 | * @return boolean 141 | */ 142 | public function isSuccess() 143 | { 144 | return $this->success; 145 | } 146 | 147 | /** 148 | * Get error codes. 149 | * 150 | * @return array 151 | */ 152 | public function getErrorCodes() 153 | { 154 | return $this->errorCodes; 155 | } 156 | 157 | /** 158 | * Get hostname. 159 | * 160 | * @return string 161 | */ 162 | public function getHostname() 163 | { 164 | return $this->hostname; 165 | } 166 | 167 | /** 168 | * Get challenge timestamp 169 | * 170 | * @return string 171 | */ 172 | public function getChallengeTs() 173 | { 174 | return $this->challengeTs; 175 | } 176 | 177 | /** 178 | * Get APK package name 179 | * 180 | * @return string 181 | */ 182 | public function getApkPackageName() 183 | { 184 | return $this->apkPackageName; 185 | } 186 | /** 187 | * Get score 188 | * 189 | * @return float 190 | */ 191 | public function getScore() 192 | { 193 | return $this->score; 194 | } 195 | 196 | /** 197 | * Get action 198 | * 199 | * @return string 200 | */ 201 | public function getAction() 202 | { 203 | return $this->action; 204 | } 205 | 206 | public function toArray() 207 | { 208 | return array( 209 | 'success' => $this->isSuccess(), 210 | 'hostname' => $this->getHostname(), 211 | 'challenge_ts' => $this->getChallengeTs(), 212 | 'apk_package_name' => $this->getApkPackageName(), 213 | 'score' => $this->getScore(), 214 | 'action' => $this->getAction(), 215 | 'error-codes' => $this->getErrorCodes(), 216 | ); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/autoload.php: -------------------------------------------------------------------------------- 1 |