captchaapi/laravel 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

captchaapi/laravel

Composer 安装命令:

composer require captchaapi/laravel

包简介

Official Laravel SDK for captchaapi.eu — EU-hosted, GDPR-compliant proof-of-work CAPTCHA with native Livewire support.

README 文档

README

Latest Version on Packagist Total Downloads Tests PHP Version Laravel Compatibility License: MIT

Official Laravel SDK for captchaapi.eu — EU-hosted, GDPR-compliant proof-of-work CAPTCHA. Drop-in Blade component, validation rule, and Livewire trait. No cookies, no tracking, no Google.

Why captchaapi.eu

  • EU-hosted (Hetzner Nuremberg) — GDPR-compliant by default, no data ever leaves the EU.
  • Proof-of-work — invisible to legitimate visitors, no friction puzzles to solve.
  • Server-side verification — your backend confirms each response with captchaapi.eu over a single call, secured by your secret key. The same model every major CAPTCHA uses, so the secret never reaches the browser.
  • Livewire-native — first-class trait + Blade wrapper instead of just plain HTML form support.

Requirements

  • PHP 8.2+
  • Laravel 12 or 13
  • (Optional) Livewire 4 for the trait + livewire-form component

Installation

composer require captchaapi/laravel

Publish the config file:

php artisan vendor:publish --tag=captchaapi-config

Set the credentials in .env:

CAPTCHAAPI_SITE_KEY=pk_live_...
CAPTCHAAPI_SECRET_KEY=sk_live_...

The site key is public and goes in the browser; the secret stays on your server. Get both from the project dashboard.

Usage

Plain HTML form

In your layout's <head> (renders the widget script and pushes config to window.CAPTCHA_*):

<x-captchaapi::widget />

In your form, add data-captcha:

<form action="/contact" method="POST" data-captcha>
    @csrf
    <input type="email" name="email" required>
    <button type="submit">Send</button>
</form>

In your validation:

use Captchaapi\Laravel\Rules\ValidCaptcha;

$request->validate([
    'email'               => ['required', 'email'],
    'captchaapi_response' => ['required', 'string', new ValidCaptcha],
]);

Or via the string alias:

$request->validate([
    'email'               => ['required', 'email'],
    'captchaapi_response' => ['required', 'captcha'],
]);

Livewire

In your layout (same as above):

<x-captchaapi::widget />

In your Livewire component:

use Captchaapi\Laravel\Concerns\WithCaptcha;
use Livewire\Component;

class RegisterForm extends Component
{
    use WithCaptcha;

    public string $email = '';

    public function register(): void
    {
        $this->validateWithCaptcha([
            'email' => 'required|email',
        ]);

        // proceed — captchaapi_response has been verified
    }
}

validateWithCaptcha() is sugar for $this->validate(array_merge($rules, $this->rulesForCaptcha())). If you need to compose validateOnly() flows yourself, rulesForCaptcha() is still public.

In the component view, use the Livewire-aware form wrapper:

<div>
    <x-captchaapi::livewire-form action="register" class="space-y-4">
        <input wire:model="email" type="email" required>
        <button type="submit">Register</button>
    </x-captchaapi::livewire-form>
</div>

The wrapper sets data-captcha-mode="event", includes the hidden captchaapi_response input, and dispatches to your Livewire register method once the response arrives.

Showing the validation error

When ValidCaptcha rejects a response (invalid, expired, or already used), Laravel attaches the message to the captchaapi_response field. Render it with the included helper:

<x-captchaapi::error />

Defaults to :for="captchaapi_response" and renders a <p role="alert">. Override the field name, the wrapping tag, and pass through any attributes:

<x-captchaapi::error for="captchaapi_response" as="span" class="text-red-600" />

The component is a thin wrapper around Laravel's @error directive — if you prefer to keep markup in your own templates, write it yourself:

@error('captchaapi_response')
    <p role="alert">{{ $message }}</p>
@enderror

Both approaches work for plain HTML forms (after a redirect-with-errors) and for Livewire (after validateWithCaptcha()).

Status element styling

Place a <div data-captcha-status></div> anywhere inside (or near) your form to show the widget's current state to your users. Six states exist (waiting, idle, solving, ready, error, rate_limited), each rendered as an icon + a localised message. The widget sets data-captcha-state="…" on the element so you can target each state with CSS.

The status element is opt-in. A form without a [data-captcha-status] child runs silently — submission still works, only the visible signal is absent. There is no auto-injection.

Default colors

The widget injects a low-specificity stylesheet on first use that applies sensible default colors per state:

State Default color
waiting #6b7280 grey
idle #6b7280 grey
solving #6b7280 grey
ready #059669 emerald
error #dc2626 red
rate_limited #d97706 amber

You don't have to write any CSS to get those colors. They appear automatically on every [data-captcha-status] element the widget finds.

Override one state

Write a higher-specificity rule (two attribute selectors instead of one). The customer rule wins regardless of cascade order — no !important needed:

[data-captcha-status][data-captcha-state="ready"] { color: #047857; }
[data-captcha-status][data-captcha-state="error"] { color: #b91c1c; }

Take over completely (Tailwind / design system)

Add data-captcha-no-color to suppress the widget's default stylesheet entirely. Your own classes / CSS apply cleanly:

<div data-captcha-status data-captcha-no-color
     class="text-cyan-700 dark:text-cyan-300"></div>

Note that waiting and ready share the shield icon and would be visually identical without color. If you opt out of the widget defaults, supply per-state colors in your own CSS so the two stay distinguishable.

Disabling the package

Flip CAPTCHAAPI_ENABLED=false in .env to disable both the validation rule and the widget without removing any wiring. The ValidCaptcha rule passes silently and <x-captchaapi::widget /> renders nothing, so you can keep the trait, the rule, and the Blade markup in place across local, CI, and staging environments where no live site key is set.

Defaults to true, so existing installs keep working unchanged. This is a permanent kill-switch, not a per-test bypass — for that, use FakeCaptchaapi::enable() (see Testing).

Configuration reference

Config key ENV variable Default Purpose
enabled CAPTCHAAPI_ENABLED true Master kill-switch. When false, the rule passes silently and the widget renders nothing.
site_key CAPTCHAAPI_SITE_KEY null Public site key from the dashboard. Required for widget rendering.
secret_key CAPTCHAAPI_SECRET_KEY null Project secret, sent as a Bearer token on the verify call. Server-side only.
base_url CAPTCHAAPI_BASE_URL null Override the API origin for the widget and the verify call. Defaults to https://captchaapi.eu.
timeout CAPTCHAAPI_VERIFY_TIMEOUT 5 Seconds to wait for the verify call before treating the server as unreachable.
fail_open CAPTCHAAPI_FAIL_OPEN true When the server is unreachable or returns a 5xx: true lets the submission through, false rejects it with a try-again message.
locale CAPTCHAAPI_LOCALE null Force widget language (en, de, cs, …). Falls back to <html lang> then en.
preload CAPTCHAAPI_PRELOAD lazy lazy waits for first form interaction; eager fires the challenge on page load.
debug CAPTCHAAPI_DEBUG false Log timing breakdown in the browser console.
mode CAPTCHAAPI_MODE null submit (native form POST) or event (CustomEvent for Livewire/SPA).

Fail policy

The verify call can fail to reach a verdict — the server is unreachable or returns a 5xx. fail_open decides what happens, and it defaults to true: the submission goes through. A CAPTCHA guards a public form, so your own outage blocking every submission is worse than the rare bot slipping past during it, and an attacker can't reach this path anyway — verification is server-to-server, off the browser.

Set it to false for sensitive actions (login, payment) where a missed bot costs more than a blocked visitor:

CAPTCHAAPI_FAIL_OPEN=false

The visitor is then asked to try again, never told they failed the CAPTCHA. There is no automatic retry on either setting: the response is single-use, and a second verify call would spend a token the visitor already solved.

Secret key rotation

Rotate from the dashboard — the package needs no list of keys. While a rotation is pending, the server accepts both the old and the new secret on every challenge issued, so you deploy the new key without a hard cutover:

  1. In the dashboard, click Rotate secret key — issues a new key while the current one keeps working.
  2. Update CAPTCHAAPI_SECRET_KEY in your .env and deploy. From here both keys verify, so the timing of your deploy doesn't matter.
  3. In the dashboard, activate the new key, then retire the old one.

The only thing tied to the old key is a challenge a visitor was already solving when you clicked Rotate — it lives at most the token lifetime (~2 minutes), so any brief overlap clears itself.

For a suspected compromise, use Revoke immediately to drop the old key in one step. This skips the overlap, so in-flight solutions fail until the new secret is deployed.

Testing

In feature tests, enable fake mode so ValidCaptcha accepts any input without a real response or a call to the server:

use Captchaapi\Laravel\Testing\FakeCaptchaapi;

beforeEach(function () {
    FakeCaptchaapi::enable();
});

afterEach(function () {
    FakeCaptchaapi::disable();
});

The fake state is stored on the Captchaapi singleton — it does not persist across requests in production code.

Catching Fortify's double validation

Fortify runs the login validator twice in one request, so a single-use response must survive both passes. To prove your integration handles this instead of silently bypassing it, opt the fake into the server's single-use contract:

FakeCaptchaapi::enable();
FakeCaptchaapi::enforceSingleUse();

The value now verifies once per request — the per-request memo covers the double call — while a replay in a later request is rejected. A missing memoization then fails the test instead of passing silently.

Laravel Octane

The package is safe under Octane (Swoole, RoadRunner, FrankenPHP), where the application and its singletons stay resident across requests:

  • Verification is stateless. Each check makes its own call to /verify and reads the secret from config; nothing is cached on the manager between requests.
  • The per-request memo lives on the request, not the singleton. Octane hands each request a fresh request instance, so the memo never carries over to the next visitor — every request verifies independently.
  • Fake state is test-only. fake() and enforceSingleUse() throw outside the testing environment, so the bypass and the consumed-token set can never be left set on a production worker.

Security

If you discover a security vulnerability, please do not open a public GitHub issue. Use GitHub's private vulnerability reporting so we can coordinate a fix before details become public.

Contributing

Bug reports and feature requests welcome at github.com/captchaapi/laravel/issues.

For development:

composer install
composer test     # Pest, parallel
composer lint     # Pint --test
composer stan     # PHPStan level 8
composer rector   # Rector --dry-run

License

MIT — see LICENSE.

captchaapi/laravel 适用场景与选型建议

captchaapi/laravel 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 484 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 05 月 03 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「captcha」 「laravel」 「proof-of-work」 「pow」 「eu」 「spam-protection」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 captchaapi/laravel 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 captchaapi/laravel 我们能提供哪些服务?
定制开发 / 二次开发

基于 captchaapi/laravel 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 484
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 47
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-03