├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── UPGRADE.md ├── composer.json ├── config └── captcha.php ├── rector.php ├── resources └── views │ └── components │ ├── button.blade.php │ └── container.blade.php └── src ├── CaptchaManager.php ├── CaptchaServiceProvider.php ├── Contracts └── Captcha.php ├── Drivers ├── FakeCaptcha.php ├── HCaptcha.php ├── ReCaptcha.php └── TurnstileCaptcha.php ├── Facades └── Captcha.php ├── Rules └── Captcha.php └── Views └── Components ├── Button.php ├── Container.php └── Js.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## 4.0.0 - 2025-03-06 4 | 5 | ### Added 6 | 7 | - Laravel 12 Support https://github.com/RahulDey12/laravel-captcha/pull/29 8 | 9 | ## 3.0.0 - 2024-03-30 10 | 11 | ### Added 12 | 13 | - Laravel 11 Support https://github.com/RahulDey12/laravel-captcha/pull/23 14 | 15 | ## 2.0.0 - 2023-10-29 16 | 17 | ### Released v2.0.0 🎉 18 | 19 | - No Captcha 20 | - Ability to extend 21 | - Support for custom error message 22 | - Driver name is not case sensitive anymore 23 | - Robust validation 24 | - Limitless Customization & Much More. 25 | 26 | ## 1.2.0 - 2023-05-28 27 | 28 | ### Removed 29 | 30 | - Removed laravel 7 support #16 31 | 32 | ## 1.1.3 - 2023-05-28 33 | 34 | ### Added 35 | 36 | - Extended collision support for laravel 7 5bbb016 37 | 38 | ## 1.1.2 - 2023-05-28 39 | 40 | ### Patched 41 | 42 | - Fixed Guzzle Http version for laravel 7 43 | 44 | ## 1.1.1 - 2023-02-21 45 | 46 | ### Added 47 | 48 | - Added Laravel 10 Support in #13 49 | 50 | ## 1.1.0 - 2023-01-07 51 | 52 | ### Added 53 | 54 | - Added support for [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/) in #11 55 | 56 | ## 1.0.1 - 2022-03-25 57 | 58 | ### Changed 59 | 60 | - Updated dev dependencies (3e74ae5, 79ade43) 61 | 62 | ## 1.0.0 - 2022-03-20 63 | 64 | First stable release. 65 | 66 | ## Unreleased 67 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Rahul Dey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Poster Laravel Captcha

2 | 3 |

Laravel Captcha

4 | 5 |

6 | StyleCI Status 7 | Build Status 8 | Version 9 | Total Downloads 10 |

11 | 12 | Laravel Captcha is a wrapper around Turnstile, HCaptcha & Google Recaptcha. It provides very easy-to-use Facade, Validation Rule, and laravel components. 13 | 14 | > **Requires [PHP 8.0+](https://php.net/releases/)** 15 | 16 | ## Quick Start 17 | 18 | 1. Install via [Composer](https://getcomposer.org): 19 | 20 | ```bash 21 | composer require rahul900day/laravel-captcha 22 | ``` 23 | 24 | 2. Publish the config file with: 25 | 26 | ```bash 27 | php artisan vendor:publish --tag="captcha-config" 28 | ``` 29 | 30 | 3. Add required configuration to `.env` file: 31 | 32 | ```dotenv 33 | CAPTCHA_DRIVER=turnstile 34 | CAPTCHA_SITE_KEY="{Your Site Key}" 35 | CAPTCHA_SECRET_KEY="{Your Site Secret}" 36 | ``` 37 | 38 | 4. Display the Captcha 39 | 40 | ```blade 41 | 42 | 43 | 44 | 45 |
46 | 47 | 48 | 49 | ``` 50 | 51 | Please read the full documentation on https://laravel-captcha.rahuldey.dev/ 52 | 53 | ## Changelog 54 | 55 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 56 | 57 | ## Contributing 58 | 59 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 60 | 61 | ## Security Vulnerabilities 62 | 63 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 64 | 65 | ## Credits 66 | 67 | - [Rahul Dey](https://github.com/RahulDey12) 68 | - [All Contributors](../../contributors) 69 | 70 | ## Sponsors 71 | 72 | - [Pricop Alexandru](https://twitter.com/PricopX) 73 | - [Acolyte Academy](https://acolyte.academy) 74 | 75 | ## License 76 | 77 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 78 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | ## Upgrading to 3.0 from 2.x 4 | 5 | ### Minimum Versions 6 | 7 | The following requirement has been updated: 8 | 9 | - The minimum PHP version is now `v8.1` 10 | - The minimum Laravel version is now `v10.0.0` 11 | 12 | ### Updating Dependencies 13 | 14 | You should update the following dependency in your application's `composer.json` file: 15 | 16 | ```diff 17 | - "rahul900day/laravel-captcha": "^2.0" 18 | + "rahul900day/laravel-captcha": "^3.0" 19 | ``` 20 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rahul900day/laravel-captcha", 3 | "description": "Different types of Captcha implementation for Laravel Application.", 4 | "keywords": [ 5 | "rahul900day", 6 | "laravel", 7 | "laravel-captcha", 8 | "laravel-recaptcha", 9 | "laravel-hcaptcha" 10 | ], 11 | "homepage": "https://github.com/RahulDey12/laravel-captcha", 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Rahul Dey", 16 | "email": "rahul900day@gmail.com" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.1", 21 | "guzzlehttp/guzzle": "^7.5", 22 | "illuminate/http": "^10.0|^11.0|^12.0", 23 | "illuminate/support": "^10.0|^11.0|^12.0", 24 | "illuminate/validation": "^10.0|^11.0|^12.0", 25 | "illuminate/view": "^10.0|^11.0|^12.0" 26 | }, 27 | "require-dev": { 28 | "laravel/pint": "^1.5.0", 29 | "nunomaduro/collision": "^7.0|^8.0", 30 | "orchestra/testbench": "^8.0|^9.0|^10.0", 31 | "pestphp/pest": "^2.0|^3.7", 32 | "rector/rector": "^1.0|^2.0" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Rahul900day\\Captcha\\": "src" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "Rahul900day\\Captcha\\Tests\\": "tests" 42 | } 43 | }, 44 | "config": { 45 | "sort-packages": true, 46 | "allow-plugins": { 47 | "pestphp/pest-plugin": true 48 | } 49 | }, 50 | "extra": { 51 | "laravel": { 52 | "providers": [ 53 | "Rahul900day\\Captcha\\CaptchaServiceProvider" 54 | ], 55 | "aliases": { 56 | "Captcha": "Rahul900day\\Captcha\\Facades\\Captcha" 57 | } 58 | } 59 | }, 60 | "minimum-stability": "dev", 61 | "prefer-stable": true 62 | } 63 | -------------------------------------------------------------------------------- /config/captcha.php: -------------------------------------------------------------------------------- 1 | env('CAPTCHA_DRIVER', 'turnstile'), 18 | 19 | /* 20 | |------------------------------------------------------------ 21 | | Captcha Site Key 22 | |------------------------------------------------------------ 23 | | 24 | | The site key is used for showing the captcha in the front 25 | | end. You will get your site key from your preferred 26 | | vendor like "ReCaptcha", "HCaptcha", "Turnstile". 27 | | 28 | | ReCaptcha Docs: https://developers.google.com/recaptcha/docs/display 29 | | HCaptcha Docs: https://docs.hcaptcha.com/configuration 30 | | Turnstile Docs: https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#configurations 31 | | 32 | */ 33 | 'sitekey' => env('CAPTCHA_SITE_KEY', ''), 34 | 35 | /* 36 | |------------------------------------------------------------ 37 | | Captcha Secret Key 38 | |------------------------------------------------------------ 39 | | 40 | | The site key is used for validating the captcha responses. 41 | | You will get you secret key from your preferred vendor 42 | | like "ReCaptcha", "HCaptcha" or "Turnstile". 43 | | 44 | | ReCaptcha Docs: https://developers.google.com/recaptcha/docs/display 45 | | HCaptcha Docs: https://docs.hcaptcha.com/configuration 46 | | Turnstile Docs: https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#configurations 47 | | 48 | */ 49 | 'secret' => env('CAPTCHA_SECRET_KEY', ''), 50 | 51 | /* 52 | |------------------------------------------------------------ 53 | | Captcha Locale Configuration 54 | |------------------------------------------------------------ 55 | | 56 | | The captcha locale determines the default locale of your 57 | | Captcha. You can add any locale value that supported 58 | | by your captcha provider. 59 | | 60 | | ReCaptcha Language List: https://developers.google.com/recaptcha/docs/language 61 | | HCaptcha Language List: https://docs.hcaptcha.com/languages 62 | | 63 | | [NOTE]: Locale feature supported on Turnstile via component. 64 | | 65 | */ 66 | 'locale' => env('CAPTCHA_LOCALE', 'en'), 67 | 68 | /* 69 | |------------------------------------------------------------ 70 | | Captcha Theme 71 | |------------------------------------------------------------ 72 | | 73 | | The captcha theme is the default theme that will display 74 | | the captcha checkbox. 75 | | 76 | | Supported: "light", "dark" 77 | | 78 | */ 79 | 'theme' => env('CAPTCHA_THEME', 'light'), 80 | 81 | /* 82 | |------------------------------------------------------------ 83 | | Captcha Size 84 | |------------------------------------------------------------ 85 | | 86 | | The captcha size is the default size of the captcha 87 | | checkbox. According to your style preference you 88 | | can use any of the supported captcha size. 89 | | 90 | | Supported: "normal", "compact" 91 | | 92 | */ 93 | 'size' => env('CAPTCHA_SIZE', 'normal'), 94 | ]; 95 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | paths([ 13 | __DIR__.'/src', 14 | ]); 15 | 16 | $rectorConfig->rules([ 17 | InlineConstructorDefaultToPropertyRector::class, 18 | DeclareStrictTypesRector::class, 19 | ]); 20 | 21 | $rectorConfig->sets([ 22 | LevelSetList::UP_TO_PHP_81, 23 | SetList::CODE_QUALITY, 24 | SetList::DEAD_CODE, 25 | SetList::EARLY_RETURN, 26 | SetList::TYPE_DECLARATION, 27 | ]); 28 | }; 29 | -------------------------------------------------------------------------------- /resources/views/components/button.blade.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | @if(! $attributes->has('data-callback') && ! $has_custom_callback ) 10 | 15 | @endif 16 | -------------------------------------------------------------------------------- /resources/views/components/container.blade.php: -------------------------------------------------------------------------------- 1 |
merge([ 2 | 'class' => $containerClass, 3 | 'data-theme' => $theme, 4 | 'data-size' => $size, 5 | 'data-sitekey' => $site_key, 6 | ]) }}>
7 | -------------------------------------------------------------------------------- /src/CaptchaManager.php: -------------------------------------------------------------------------------- 1 | configurationIsCached()) { 21 | $this->mergeConfigFrom(__DIR__.'/../config/captcha.php', 'captcha'); 22 | } 23 | 24 | $this->app->singleton(CaptchaContract::class, fn ($app): CaptchaManager => new CaptchaManager($app)); 25 | 26 | $this->app->alias(CaptchaContract::class, 'captcha'); 27 | } 28 | 29 | public function boot(): void 30 | { 31 | $this->bootResources(); 32 | $this->bootBladeComponents(); 33 | $this->bootValidations(); 34 | $this->bootPublishing(); 35 | } 36 | 37 | protected function bootResources(): void 38 | { 39 | $this->loadViewsFrom(__DIR__.'/../resources/views', 'captcha'); 40 | } 41 | 42 | protected function bootBladeComponents(): void 43 | { 44 | $this->callAfterResolving(BladeCompiler::class, function (BladeCompiler $blade): void { 45 | $blade->component(Js::class, 'js', 'captcha'); 46 | $blade->component(Container::class, 'container', 'captcha'); 47 | $blade->component(Button::class, 'button', 'captcha'); 48 | }); 49 | } 50 | 51 | protected function bootValidations(): void 52 | { 53 | Validator::extend('captcha', Captcha::class.'@passes', __(Captcha::$message)); 54 | } 55 | 56 | protected function bootPublishing(): void 57 | { 58 | if ($this->app->runningInConsole()) { 59 | $this->publishes([ 60 | __DIR__.'/../config/captcha.php' => config_path('captcha.php'), 61 | ], 'captcha-config'); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Contracts/Captcha.php: -------------------------------------------------------------------------------- 1 | verify; 18 | } 19 | 20 | public function getResponseName(): string 21 | { 22 | return 'fake-response'; 23 | } 24 | 25 | public function getContainerClassName(): string 26 | { 27 | return 'fake-captcha-container'; 28 | } 29 | 30 | public function getJs(?string $hl): string 31 | { 32 | return << 34 | window.addEventListener('load', () => { 35 | const container = document.querySelector('.{$this->getContainerClassName()}'); 36 | const input = document.createElement('input'); 37 | input.setAttribute('type', 'hidden'); 38 | input.setAttribute('name', '{$this->getResponseName()}') 39 | input.setAttribute('value', 'pass') 40 | container.parentElement.appendChild(input) 41 | }) 42 | 43 | HTML; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Drivers/HCaptcha.php: -------------------------------------------------------------------------------- 1 | post(self::VERIFY_URL, [ 17 | 'response' => $token, 18 | 'secret' => config('captcha.secret'), 19 | 'remoteip' => request()->ip(), 20 | ])->json(); 21 | 22 | return (bool) collect($captcha_resp)->get('success'); 23 | } 24 | 25 | public function getResponseName(): string 26 | { 27 | return 'h-captcha-response'; 28 | } 29 | 30 | public function getContainerClassName(): string 31 | { 32 | return 'h-captcha'; 33 | } 34 | 35 | public function getJs(?string $hl = null): string 36 | { 37 | $hl ??= config('captcha.locale', 'en'); 38 | 39 | return ''; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Drivers/ReCaptcha.php: -------------------------------------------------------------------------------- 1 | post(self::VERIFY_URL, [ 17 | 'response' => $token, 18 | 'secret' => config('captcha.secret'), 19 | 'remoteip' => request()->ip(), 20 | ])->json(); 21 | 22 | return (bool) collect($captcha_resp)->get('success'); 23 | } 24 | 25 | public function getResponseName(): string 26 | { 27 | return 'g-recaptcha-response'; 28 | } 29 | 30 | public function getContainerClassName(): string 31 | { 32 | return 'g-recaptcha'; 33 | } 34 | 35 | public function getJs(?string $hl = null): string 36 | { 37 | $hl ??= config('captcha.locale', 'en'); 38 | 39 | return << 41 | 42 | 43 | HTML; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Drivers/TurnstileCaptcha.php: -------------------------------------------------------------------------------- 1 | post(self::VERIFY_URL, [ 17 | 'response' => $token, 18 | 'secret' => config('captcha.secret'), 19 | 'remoteip' => request()->ip(), 20 | ])->json(); 21 | 22 | return (bool) collect($captcha_resp)->get('success'); 23 | } 24 | 25 | public function getResponseName(): string 26 | { 27 | return 'cf-turnstile-response'; 28 | } 29 | 30 | public function getContainerClassName(): string 31 | { 32 | return 'cf-turnstile'; 33 | } 34 | 35 | public function getJs(?string $hl = null): string 36 | { 37 | return ''; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Facades/Captcha.php: -------------------------------------------------------------------------------- 1 | site_key = config('captcha.sitekey', ''); 27 | $this->containerClass = Captcha::getContainerClassName(); 28 | $this->nonce = Str::random(10); 29 | $this->callback = $callback ?? "onCaptchaSubmit_{$this->nonce}"; 30 | $this->has_custom_callback = (bool) $callback; 31 | } 32 | 33 | public function render(): View 34 | { 35 | return view('captcha::components.button'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Views/Components/Container.php: -------------------------------------------------------------------------------- 1 | site_key = config('captcha.sitekey', ''); 24 | $this->theme = $theme ?? config('captcha.theme', 'light'); 25 | $this->size = $size ?? config('captcha.size', 'normal'); 26 | $this->containerClass = Captcha::getContainerClassName(); 27 | } 28 | 29 | public function render(): View 30 | { 31 | return view('captcha::components.container'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Views/Components/Js.php: -------------------------------------------------------------------------------- 1 | lang); 19 | } 20 | } 21 | --------------------------------------------------------------------------------