scxcaptchaAjaxForm
==================

Неблокирующая числовая CAPTCHA для AjaxForm/FormIt. Легко читается человеком, затрудняет спам-ботов.
Все имена и классы — с префиксом scx_, чтобы не пересекаться с вашим кодом.

Требования
----------
- MODX Revolution 2.8+ или 3.x
- PHP 7.4+ (рекомендуется 8.x)
- Расширение GD с поддержкой TrueType (для TTF шрифта)

Установка
---------
1) Установите пакет через Менеджер пакетов.
2) Убедитесь, что шрифт `DejaVuSansMono.ttf` лежит в:
   /assets/components/scxcaptchaajaxform/fonts/ .
   Если шрифта нет, будет использован встроенный bitmap-шрифт.
3) Никаких системных настроек не требуется.

Использование
-------------
Вставьте блок капчи в форму (ВАЖНО: некэшируемо):

[[!ScxCaptchaAjaxForm]]

Пример с AjaxForm:
[[!AjaxForm?
   &snippet=`FormIt`
   &form=`tpl.AjaxForm.example`
   &hooks=`ScxCaptchaAjaxFormHook,email`
   &validate=`name:required,email:required:email,scx_code:required`
]]

Пример с чистым FormIt:
[[!FormIt?
   &hooks=`ScxCaptchaAjaxFormHook,email`
   &validate=`name:required,email:required:email,scx_code:required`
]]
[[!ScxCaptchaAjaxForm]]

Параметры сниппета
------------------
- ttl — время жизни токена в секундах. По умолчанию 1200 (20 минут).
  Пример: [[!ScxCaptchaAjaxForm? &ttl=`900`]]

Поля формы
----------
- scx_hp    — honeypot (должно оставаться пустым)
- scx_ts    — timestamp рендера формы (мс) для анти-бот проверки
- scx_code  — ответ пользователя (ровно 5 цифр)
- scx_token — скрытый токен текущей капчи

Как это работает
----------------
1) Сниппет генерирует токен, сохраняет его в $_SESSION['scx_allowed'] и сразу закрывает сессию (session_write_close),
   чтобы картинка загрузилась без гонки.
2) JS подставляет src картинки: /assets/components/scxcaptchaajaxform/captcha.php?t=TOKEN
3) captcha.php создаёт код (5 цифр), кладёт хэш в $_SESSION['scx_code'][TOKEN] и отдаёт PNG.
4) Хук ScxCaptchaAjaxFormHook проверяет honeypot, время (>= 3 сек), токен и код; при успехе очищает данные (одноразовая).

Отладка
-------
- Добавьте &debug=1 к URL картинки (captcha.php?...&debug=1) — получите текст с именем сессии, токеном и списком разрешённых токенов.
- Ошибка 400 Bad token? Проверьте:
  * сниппет вызван некэшируемо: [[!ScxCaptchaAjaxForm]]
  * страница и captcha.php отдаются с одного и того же хоста/поддомена;
  * PHP-сессии сохраняются (в MODX они в БД через modSessionHandler).

Кастомизация внешнего вида
--------------------------
Стили поставляются «один раз на страницу». Переопределяйте классами:
.scx-captcha, .scx-img, .scx-refresh, .scx-input, .scx-hp

Безопасность
------------
- Код хранится в сессии только как хэш (password_hash).
- Токены короткоживущие; просроченные регулярно удаляются.
- Поле ответа валидируется на 5 цифр.

Лицензия
--------
MIT — см. LICENSE.
