hryvinskyi / magento2-invisible-captcha
Magento 2 Google Invisible Captcha (Recaptcha) module
Hryvinskyi_InvisibleCaptcha
Multi-provider invisible captcha and bot protection for Magento 2.
Version 3 merges the former Hryvinskyi_TurnstileProtection module into a single,
provider-agnostic extension that protects both individual forms and whole
routes, using whichever CAPTCHA provider you configure.
Providers
| Code | Provider | Mode |
|---|---|---|
recaptcha_v2_checkbox |
Google reCAPTCHA v2 — "I'm not a robot" | visible checkbox |
recaptcha_v2_invisible |
Google reCAPTCHA v2 — invisible badge | invisible |
recaptcha_v3 |
Google reCAPTCHA v3 | invisible, score-based |
recaptcha_enterprise |
Google reCAPTCHA Enterprise | invisible, score-based (assessments API) |
turnstile |
Cloudflare Turnstile | managed / invisible |
Capabilities
- Form protection — invisibly protects the same surface as Magento's native
reCAPTCHA suite: customer login (incl. AJAX popup & checkout), register,
forgot password, account edit, contact, newsletter,
send-to-friend, product review, share wishlist, apply coupon
(cart), PayPal Payflow Pro, and the admin login / forgot-password
forms. Each is toggled independently; score-based providers also support a
per-form threshold. - WebAPI / headless protection — REST and GraphQL validation for
place order, apply coupon (checkout), in-store pickup place order,
and resend confirmation email. Protected endpoints require the captcha
token in theX-Captcha-Tokenrequest header (X-ReCaptchaalso accepted).
AhryvinskyiInvisibleCaptchaConfig(formType:)GraphQL query exposes the
active provider's client config for headless storefronts. Endpoint coverage is
extensible viaApi\Webapi\WebapiConfigProviderInterface. - Route protection — a full-page interstitial challenge for requests that
match a Cloudflare-style rule expression (field / operator / value rows joined
by AND/OR). A verified visitor receives an HMAC-signed cookie and is keyed into
a separate Varnish cache bucket via the HTTP context. An optional fallback
provider is revealed on the challenge page after a delay. The interstitial is
a self-contained, system-font page (no external font/CDN requests) whose accent
color is themeable from the admin.
Installation
composer require hryvinskyi/magento2-invisible-captcha
bin/magento module:enable Hryvinskyi_InvisibleCaptcha
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy
Dependencies: hryvinskyi/magento2-base, hryvinskyi/magento2-theme-assets,
symfony/http-client + PSR-7/17/18.
Configuration
Stores → Configuration → Hryvinskyi → Invisible Captcha & Bot Protection
(hryvinskyi_invisible_captcha/*):
- General — master switch, active provider, lazy-load, disable-submit, debug.
- Provider Credentials — site/secret keys (+ Enterprise project id, widget
options) per provider. Secret keys are encrypted and flagged sensitive. - Form Protection — per-form toggles and score thresholds (storefront + admin).
- Route Protection — rules editor, route-gate provider override, fallback
provider, cookie lifetime, IP / user-agent exclusions, AJAX-marker params, etc.
Includes a Challenge Page Appearance sub-group that themes the interstitial
accent palette —primary_color(#2f6bd8),primary_color_deep(#2557b6) and
primary_color_soft(rgba(47,107,216,0.12)) under
hryvinskyi_invisible_captcha/route_protection/appearance/*, injected as
CSP-safe--primary*overrides and configurable per store view. - Advanced — outbound verification HTTP timeout.
Architecture (extension points)
Api\Provider\ProviderInterface+ProviderPoolInterface— add a provider by
implementing the interface and adding it to theModel\Provider\PoolDI array.Api\Verification\VerifierInterface/VerificationRequestInterface/
VerificationResultInterface— provider-agnostic verification.Api\Filter\FieldProviderInterface/OperatorProviderInterface— extend the
route rule engine with custom fields/operators via DI arrays.Model\Strategy\{Token,Failure}\*— pluggable token extraction & failure handling.
WebAPI / checkout note
The place_order, coupon_code (checkout), store_pickup and
resend_confirmation_email keys validate server-side on the WebAPI/GraphQL
call. They are off by default. Before enabling one, make sure your storefront
/ headless client sends the captcha token in the X-Captcha-Token header on that
call (for score-based providers, execute the provider with the matching action,
e.g. place_order) — otherwise those requests will be rejected. The token for
form-level (non-WebAPI) submissions is handled automatically via the hidden
hryvinskyi_invisible_token field.
Verify endpoint & caching
The route-gate challenge POSTs to invisiblecaptcha/verify. On success it sets the
hryvinskyi_captcha_verified cookie and the CAPTCHA_VERIFIED HTTP-context vary
key. After deploying v3, run a full page cache flush (the vary key / cookie /
X-InvisibleCaptcha-Challenge header were renamed from the old Turnstile names).
Upgrading from v2.x / TurnstileProtection
Run setup:upgrade. The data patch MigrateLegacyCaptchaConfig copies your old
hryvinskyi_invisible_captcha/* (reCAPTCHA v3) and hryvinskyi_turnstile/*
settings into the new tree (encrypting the legacy plaintext v3 secret). The
standalone Hryvinskyi_TurnstileProtection module is removed — disable it after
upgrading.
Migrating from Magento's native Google reCAPTCHA
If the store already uses Magento's built-in Google reCAPTCHA (Magento_ReCaptcha*),
import its configuration into this module with:
bin/magento hryvinskyi:invisible-captcha:migrate-recaptcha --dry-run # preview the change set
bin/magento hryvinskyi:invisible-captcha:migrate-recaptcha # apply
The command reads every recaptcha_frontend/* and recaptcha_backend/* row from
core_config_data (all scopes) and writes the equivalent
hryvinskyi_invisible_captcha/* settings:
- Credentials — v3, v2-checkbox and v2-invisible site/secret keys map to the
matching provider under Provider Credentials. The native module stores both
keys encrypted; the secret key is copied verbatim (this module's field is
encrypted too, so the ciphertext stays valid under the same crypt key), while the
site key is decrypted on copy because this module stores it in plain text.
Run the command on the installation that owns the crypt key. Frontend keys win
when frontend and backend differ. - Per-form selectors — each
type_for/<form>that isn't "disabled" turns on the
corresponding form under Form Protection (the nativeplace_ordergate enables
both checkout and in-store pickup here). The v3 section-wide score threshold is
fanned out to each enabled v3 form. - Derived — the active provider (most-used across the enabled forms), the master
switch and the form-protection switches are set so protection stays live. - Status hand-over — after copying, each migrated native
type_for/*selector
row is cleared, so the built-in reCAPTCHA stops challenging those forms and
this module takes over in the same run (source_disabledin the summary table).
Native credentials are left in place, so the switch is easy to revert.
Existing values in the target tree are never overwritten unless you pass --force.
The command flushes the config cache and prints a per-path summary table.
Upgrading from a version whose migration copied the site key without decrypting
it? Re-run with--forceto overwrite the broken ciphertext values.
skipped_undecryptablerows / missing site keys? The installation's crypt key
(app/etc/env.php) cannot decrypt those values — typical when the database was
imported from another environment without its crypt key. Nothing else encrypted
(payment keys, SMTP passwords) will decrypt on such an installation either.
Restore the original crypt key (all versions, in order) or re-enter the keys in
the admin, then re-run the command.
Because the hand-over disables the native reCAPTCHA at config level, physically
removing its modules is optional. If you still want them gone, the companion
metapackage hryvinskyi/magento2-invisible-captcha-recaptcha-replace replaces
every magento/module-re-captcha-* package (Two-Factor Auth and security.txt
from magento/security-package are left untouched). Caution: other installed
code may compile against the reCAPTCHA API modules — notably the bundled
PayPal Braintree module implements
Magento\ReCaptchaWebapiApi\Api\WebapiValidationConfigProviderInterface, so
removing all 25 packages breaks setup:di:compile on standard installs. On such
stores prefer the config-level disable above.
CLI
bin/magento hryvinskyi:invisible-captcha:disable [global|frontend|adminhtml] [--website_id=N]
bin/magento hryvinskyi:invisible-captcha:migrate-recaptcha [--dry-run] [--force]
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
[3.0.4] - 2026-07-08
Fixed
- Catch-all route rules deadlocked verification. The route gate also fired on
its owninvisiblecaptcha/verifyendpoint, so with a rule broad enough to match
it (e.g.action_name regex .*) the challenge page's token POST was answered
with another 403 challenge and a visitor could never pass.RequestCheckernow
always exempts the verify path (same normalization as the router); a direct GET
on the endpoint now yields a plain 404 instead of a challenge.
[3.0.3] - 2026-07-08
Fixed
- Route-protection regex rules accept bare (Cloudflare-style) patterns. The
regex/not_regexoperators passed the admin value straight to
preg_match(), which requires PCRE delimiters — so a natural pattern like
.*or^catalog_.*errored out ("invalid regex skipped" in the log) and the
rule never fired. Values are now tried as delimited PCRE first (modifiers like
~…~ikeep working) and auto-wrapped in~…~on a parse error; only patterns
invalid in both forms are skipped (still fail-safe, still logged).
Changed
migrate-recaptcha: undecryptable values are reported, not silently skipped.
When a native encrypted value cannot be decrypted with this installation's crypt
key (e.g. a DB imported from another environment), the row now appears in the
change table with the newskipped_undecryptablestatus and the command prints
a crypt-key warning with remediation steps, instead of omitting the row without
a trace.
[3.0.2] - 2026-07-08
Fixed
- Login-failure redirect never used the before-auth URL.
RedirectUrl\BeforeAuthUrl
was typed againstSessionManagerInterface, whose global preference is
Session\Generic— a session object with the default storage namespace, while
before_auth_urlis written into the customer namespace. The provider therefore
always fell back to the login URL. It now depends onMagento\Customer\Model\Session
(Proxy-wired inetc/frontend/di.xml), so a failed login challenge redirects back
to the customer's intended page again. - Unit test suite runs green end-to-end on PHP 8.5 / PHPUnit 10 (598 tests): fixed a
by-reference capture bug inRouteGateTest, seeded the global ObjectManager for the
backend block inProtectionRulesTest, avoided the undefinedBPconstant in
DebugTest, and re-pointedBeforeAuthUrlTestat the concrete customer session
(mocking the magic accessor on the interface fatally failed under PHPUnit 10).
[3.0.1] - 2026-07-08
Fixed
migrate-recaptcha: native site key was copied as ciphertext. Magento's
native reCAPTCHA storespublic_keythroughConfig\Backend\Encrypted, while
this module keepssite_keyin plain text — the migration now decrypts values
carrying the Magento crypt envelope (N:N:…) and passes legacy plaintext keys
through untouched; undecryptable values are skipped instead of written. Stores
that migrated with the broken version can repair by re-running with--force.
Added
migrate-recaptcha: status hand-over. After copying, the command clears
every migrated nativetype_for/*selector row (newsource_disabledchange
status, previewed in--dry-run), so the built-in reCAPTCHA is disabled and
this module is enabled in one run. Native credentials are left in place;
physically removing the native modules becomes optional — which also avoids
setup:di:compilebreakage on installs whose bundled code (e.g. PayPal
Braintree) compiles against the reCAPTCHA API modules.
[3.0.0] - 2026-07-08
Major rewrite into a multi-provider captcha & bot-protection module. The
former Hryvinskyi_TurnstileProtection module is merged in and removed.
Added
-
Five providers behind one abstraction (
Api\Provider\ProviderInterface+
ProviderPoolInterface): Google reCAPTCHA v2 checkbox, v2 invisible, v3,
reCAPTCHA Enterprise, and Cloudflare Turnstile. Pick the active provider in
Stores → Configuration → Hryvinskyi → Invisible Captcha & Bot Protection. -
Route-level protection (capability B, from TurnstileProtection): a
site-wide interstitial challenge driven by a Cloudflare-style rule engine
(Model\Filter\*), an HMAC-signed verified cookie, Varnish HTTP-context vary,
and a verify endpoint atinvisiblecaptcha/verify. -
reCAPTCHA Enterprise verifier using the
projects/{id}/assessmentsAPI. -
Bounded PSR-18 HTTP transport (
Model\Verification\Http\BoundedHttpClient)
with a configurable wall-clock budget; the verifier fails closed and fast. -
Per-provider client JS strategies (
view/base/web/js/provider/*) so any
provider/widget type renders from one form component. -
Provider-independent verified-cookie HMAC key (Magento crypt key) so switching
providers does not invalidate verified sessions. -
Data patch
Setup\Patch\Data\MigrateLegacyCaptchaConfigmigrating both the
legacyhryvinskyi_invisible_captcha/*(v2.x) andhryvinskyi_turnstile/*
settings into the unified v3 config tree. -
Configurable challenge accent color. New Challenge Page Appearance
sub-group under Route Protection
(hryvinskyi_invisible_captcha/route_protection/appearance/*) exposes
--primary(#2f6bd8),--primary-deep(#2557b6) and--primary-soft
(rgba(47,107,216,0.12)).ChallengeRendererinjects them as a CSP-safe
:rootoverride (values sanitized against CSS-injection) so the interstitial
re-themes per store view. -
Migration from Magento's native Google reCAPTCHA. New CLI command
hryvinskyi:invisible-captcha:migrate-recaptcha [--dry-run] [--force]imports
recaptcha_frontend/*/recaptcha_backend/*config (all scopes) into the
hryvinskyi_invisible_captcha/*tree: provider credentials (secrets copied
verbatim), per-formtype_for/*selectors, the v3 score threshold, and the
derived active provider + master switches. The migrator holds only mapping
policy (Model\Migration\RecaptchaMigrator) behind
Api\Migration\RecaptchaMigratorInterface, reads/writescore_config_data
through theApi\Migration\CoreConfigGatewayInterfaceport, carries per-run
state inModel\Migration\MigrationRun, and returns typed
Model\Migration\ChangeRecordresults; never clobbers existing values
without--force. -
Companion metapackage
hryvinskyi/magento2-invisible-captcha-recaptcha-replace
(published separately) thatreplaces everymagento/module-re-captcha-*
package to remove the native reCAPTCHA while leaving the rest of
magento/security-packageintact.
Changed
-
Unified, snake-case config tree under
hryvinskyi_invisible_captcha/{general, providers,form_protection,route_protection,advanced}. -
The form-verify flow is now provider-agnostic: one rich
Api\Verification\VerificationResultInterfacereplaces the reCAPTCHA-only
response; the "Provider" namespace now means a captcha vendor, while failure /
token / redirect strategies moved toModel\Strategy\*. -
Score thresholds apply only to score-based providers (v3 / Enterprise).
-
CSP whitelist is now global and covers Cloudflare + Google hosts across
script/frame/connect/form-action. -
Submit gating ("disable submit until ready") is now done with CSS + a small
parse-time inline script rendered next to the captcha marker, instead of
rewriting the whole HTML response server-side. The script adds the
hryvinskyi-captcha-disabled-submitclass to the surrounding form (the CSS in
_module.lessmakes the submit button unclickable while that class is
present);form-manager.jsremoves the class once the captcha is ready. -
Redesigned route-gate challenge page. The inline interstitial
(view/frontend/web/inline_challenge/) was reworked into a clean,
system-font layout and no longer loads Google Fonts (Inter / Cormorant
Garamond) — one fewer third-party request and a tighter CSP surface. The
placeholder contract andscript.jsbootstrap are unchanged.
Removed / BREAKING
Hryvinskyi_TurnstileProtectionis merged in and deleted. Its config under
hryvinskyi_turnstile/*is migrated automatically; external references to the
oldturnstile/verifyroute,X-Turnstile-Challengeheader,
hryvinskyi_turnstile_verifiedcookie andTURNSTILE_VERIFIEDvary key are
renamed (run a one-time full-page-cache flush after deploy).- Legacy v2.x classes (
Helper\Config\*,Model\ReCaptcha\*,Model\Verify\*,
Model\Provider\{Failure,TokenResponse}*,Observer\Captcha,LayoutSettings)
are removed; integrators should target the newApi\*contracts. - Requires
hryvinskyi/magento2-theme-assets(replaces the previous
asset-renderer dependency). - Removed the server-side
Plugin\DisableSubmitresponse rewriter and dropped
thevoku/simple_html_dom,symfony/dom-crawlerandext-domrequirements
it needed; submit gating is now handled in CSS + JS (see above).
[2.5.5] - 2026-06-01
Fixed
- PHP 8.5: added the
: intreturn type and an explicitreturn Command::SUCCESS;
toCommand\Captcha::execute(), matching Symfony ConsoleCommand::execute(): int
(fixes the "must be compatible" fatal error). - Monolog 3:
Model\Debug::write()now acceptsMonolog\LogRecordinstead ofarray
to matchMagento\Framework\Logger\Handler\Base::write().
2.5.4 - 2026-06-01
Added
- PHP 8.5 support (
~8.5added to thephprequirement).
Fixed
- PHP 8.4/8.5 compatibility: replaced implicit-nullable parameter declarations
(Type $param = null) with explicit nullable types (?Type $param = null)
across the failure providers, area config, and abstract config classes, removing
the "Implicitly marking parameter as nullable is deprecated" notices.
Security
- Widened the
symfony/dom-crawlerrequirement from>=2.7 <5.0to
^5.4.52 || ^6.4.40 || ^7.4.12 || ^8.0.12. The previous constraint could only
resolve to versions affected by CVE-2026-45071 (XXE / local file disclosure
inDomCrawler::addXmlContent()viavalidateOnParse = true), because no fix
exists for the 2.x–4.x branches. The new constraint pins to the patched releases
and aligns with the Symfony components shipped by Magento 2.4.x.
2.5.3 - 2025-10-06
Changed
- Updated dependencies and removed leftover
console.logdebug output.
2.5.2 - 2025-08-12
Changed
composer.jsonmaintenance.
2.5.1 - 2025-06-24
Changed
composer.jsonmaintenance.
2.5.0 - 2025-06-17
Changed
composer.jsonmaintenance.
| Version | Stability | QA Status | Compatibility | Released |
|---|---|---|---|---|
| 3.1.0 | stable | Fail | Magento 2.4.8-2.4.9 Details | 2026-07-11 08:35:50 |
| 3.0.4 | stable | Fail | Magento 2.4.8-2.4.9 Details | 2026-07-08 17:32:52 |
| 3.0.3 | stable | Not tested | Not yet tested Details | 2026-07-08 17:00:33 |
| 3.0.2 | stable | Not tested | Not yet tested Details | 2026-07-08 16:06:56 |
| 3.0.1 | stable | Not tested | Not yet tested Details | 2026-07-08 15:21:10 |
| 3.0.0 | stable | Not tested | Not yet tested Details | 2026-07-08 14:14:55 |
| 2.5.5 | stable | Fail | Magento 2.4.8 Details | 2026-06-01 19:51:18 |
| 2.5.4 | stable | Not tested | Not yet tested Details | 2026-06-01 12:25:56 |
| 2.5.3 | stable | Not tested | Not yet tested Details | 2025-11-05 18:06:24 |
| 2.5.2 | stable | Not tested | Not yet tested Details | 2025-08-12 09:01:06 |
| 2.5.1 | stable | Not tested | Not yet tested Details | 2025-06-24 09:54:36 |
| 2.5.0 | stable | Not tested | Not yet tested Details | 2025-06-16 22:29:28 |
| 2.4.10 | stable | Not tested | Not yet tested Details | 2025-06-16 22:27:23 |
| 2.4.9 | stable | Not tested | Not yet tested Details | 2025-05-05 18:55:28 |
| 2.4.8.1 | stable | Not tested | Not yet tested Details | 2025-05-05 13:18:08 |
| 2.4.8 | stable | Not tested | Not yet tested Details | 2024-03-28 20:23:25 |
| 2.4.7 | stable | Not tested | Not yet tested Details | 2024-01-18 12:29:05 |
| 2.4.6 | stable | Not tested | Not yet tested Details | 2024-01-18 11:31:05 |
| 2.4.5 | stable | Not tested | Not yet tested Details | 2023-12-13 10:56:58 |
| 2.4.5-dev | dev | Not tested | Not yet tested Details | 2022-09-01 07:46:00 |
| 2.4.4 | stable | Not tested | Not yet tested Details | 2022-06-23 09:59:18 |
| 2.4.3 | stable | Not tested | Not yet tested Details | 2022-06-21 12:26:03 |
| 2.4.2 | stable | Not tested | Not yet tested Details | 2022-03-22 10:43:26 |
| 2.4.1 | stable | Not tested | Not yet tested Details | 2022-03-22 10:34:54 |
| 2.4 | stable | Not tested | Not yet tested Details | 2022-03-22 10:29:50 |
| 2.3.2 | stable | Not tested | Not yet tested Details | 2021-06-19 08:57:54 |
| 2.3.1 | stable | Not tested | Not yet tested Details | 2020-09-23 22:33:58 |
| 2.3.0 | stable | Not tested | Not yet tested Details | 2020-09-07 14:03:20 |
| 2.2.7 | stable | Not tested | Not yet tested Details | 2020-08-13 19:20:15 |
| 2.2.6 | stable | Not tested | Not yet tested Details | 2020-08-07 20:19:11 |
| 2.2.5 | stable | Not tested | Not yet tested Details | 2020-08-03 19:09:47 |
| 2.2.4 | stable | Not tested | Not yet tested Details | 2020-06-16 20:43:55 |
| 2.2.3 | stable | Not tested | Not yet tested Details | 2020-06-16 20:35:41 |
| 2.2.2 | stable | Not tested | Not yet tested Details | 2020-06-16 19:32:58 |
| 2.2.1 | stable | Not tested | Not yet tested Details | 2020-06-16 17:29:41 |
| 2.2.0 | stable | Not tested | Not yet tested Details | 2020-06-16 11:40:32 |
| 2.1.10.1 | stable | Not tested | Not yet tested Details | 2020-06-09 13:23:05 |
| 2.1.10 | stable | Not tested | Not yet tested Details | 2020-05-21 17:55:11 |
| 2.1.9.1 | stable | Not tested | Not yet tested Details | 2020-02-12 21:05:09 |
| 2.1.9 | stable | Not tested | Not yet tested Details | 2020-02-09 20:23:41 |
| 2.1.8 | stable | Not tested | Not yet tested Details | 2020-02-05 13:28:44 |
| 2.1.7.1 | stable | Not tested | Not yet tested Details | 2020-01-30 09:31:19 |
| 2.1.7 | stable | Not tested | Not yet tested Details | 2020-01-10 13:06:04 |
| 2.1.6 | stable | Not tested | Not yet tested Details | 2019-10-20 10:46:16 |
| 2.1.5 | stable | Not tested | Not yet tested Details | 2019-09-25 13:33:54 |
| 2.1.4 | stable | Not tested | Not yet tested Details | 2019-09-24 20:37:24 |
| 2.1.3 | stable | Not tested | Not yet tested Details | 2019-09-17 12:16:12 |
| 2.1.2 | stable | Not tested | Not yet tested Details | 2019-09-17 11:19:58 |
| 2.1.1 | stable | Not tested | Not yet tested Details | 2019-09-17 07:22:08 |
| 2.1.0.4 | stable | Not tested | Not yet tested Details | 2019-06-11 14:19:19 |
| 2.1.0.3 | stable | Not tested | Not yet tested Details | 2019-06-11 13:38:50 |
| 2.1.0.2 | stable | Not tested | Not yet tested Details | 2019-06-05 13:36:06 |
| 2.1.0.1 | stable | Not tested | Not yet tested Details | 2019-06-01 17:21:40 |
| 2.1.0 | stable | Not tested | Not yet tested Details | 2019-05-26 10:36:24 |
| 2.0.4 | stable | Not tested | Not yet tested Details | 2019-04-22 14:56:14 |
| 2.0.3 | stable | Not tested | Not yet tested Details | 2019-04-18 15:26:56 |
| 2.0.2 | stable | Not tested | Not yet tested Details | 2019-04-09 12:02:45 |
| 2.0.1 | stable | Not tested | Not yet tested Details | 2019-03-22 08:50:42 |
| 2.0.0 | stable | Not tested | Not yet tested Details | 2019-03-21 23:52:56 |
| 1.0.1 | stable | Not tested | Not yet tested Details | 2018-06-27 12:07:03 |
| 1.0.0 | stable | Not tested | Not yet tested Details | 2017-11-10 09:37:22 |
Requires 29
| Package | Constraint |
|---|---|
| hryvinskyi/magento2-base | ^2.1 |
| hryvinskyi/magento2-configuration-fields | ^1.0.11 |
| hryvinskyi/magento2-theme-assets | ^1.0 |
| magento/framework | * |
| magento/module-backend | * |
| magento/module-catalog | * |
| magento/module-catalog-search | * |
| magento/module-checkout | * |
| magento/module-config | * |
| magento/module-contact | * |
| magento/module-customer | * |
| magento/module-graph-ql | * |
| magento/module-layered-navigation | * |
| magento/module-newsletter | * |
| magento/module-paypal | * |
| magento/module-quote | * |
| magento/module-review | * |
| magento/module-sales-rule | * |
| magento/module-send-friend | * |
| magento/module-store | * |
| magento/module-url-rewrite | * |
| magento/module-webapi | * |
| magento/module-wishlist | * |
| nyholm/psr7 | ^1.5 |
| php | ~8.1.0||~8.2.0||~8.3.0||~8.4.0||~8.5.0 |
| psr/http-client | ^1.0 |
| psr/http-factory | ^1.0 |
| psr/http-message | ^1.0 || ^2.0 |
| symfony/http-client | ^5 || ^6 || ^7 || ^8 |
Requires-dev 1
| Package | Constraint |
|---|---|
| roave/security-advisories | dev-latest |
Compatibility
Each Magento release line is installed on its supported PHP versions, then the module is built (DI compilation + static-content deploy) and its unit and integration suites are run. The matrix shows the lines and PHP versions the module is confirmed to install and run on. Code-quality results further down (phpstan, phpcs, …) are reported separately and never affect compatibility.
Code Quality
Advisory checks against the module's source. Static analysis runs once across the whole module; PHPStan re-runs per Magento + PHP version because resolvable symbols differ between releases. These NEVER affect the Compatibility badge. A phpcs finding can't make a module incompatible.
Static analysis
Coding standards (phpcs), mess detection (phpmd), copy-pasted code (cpd), PHP cross-version compatibility, composer.json validity. Each runs once for the whole module.
| Tool | Status | Findings | Summary |
|---|---|---|---|
| PHPCS | Fail | 247 | 12 errors, 235 warnings (ruleset: Magento2), 10 auto-fixable with phpcbf |
| PHPMD | Warning | 25 | 25 rule violations (MissingImport:11, NPathComplexity:3, UnusedFormalParameter:3, TooManyPublicMethods:3, ErrorControlOperator:2) |
| Cpd | Warning | 9 | 9 duplicated chunks spanning 305 total lines (min-lines=5, min-tokens=70) |
| Composer validate | Info | 19 | valid; 19 advisory notes (composer validate --strict) |
PHPStan
Type-checks the module's PHP against a real Magento install at the configured gate level. Re-runs per Magento and PHP version because resolvable symbols differ between releases.
Tests
Unit and integration suites, run for each applicable Magento and PHP version. A test failure speaks to the module's behaviour, not its compatibility with a Magento line, so it is reported here separately and never reddens the compatibility matrix.
Unit tests
Security
Security checks run directly against the module: an audit of its declared dependencies for known vulnerabilities (composer audit) and a scan of its source for malware and web-shell signatures. Each runs once. A malware detection fails the version outright.
More from hryvinskyi
View vendorTurn an existing module into recurring revenue.
If you already maintain a Magento 2 module on GitHub or GitLab, listing it on Packagento takes about five minutes. We mirror your tags, handle distribution signing, and route paid licenses through Stripe Connect, so you can keep shipping the way you already do.