This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
PrestaShop Checkout is the official PrestaShop payment module in partnership with PayPal. The main branch supports v5 for PrestaShop 1.7, 8, and 9 simultaneously. Older versions use dedicated maintenance branches (prestashop/9.x, prestashop/8.x, prestashop/1.7.x).
Requirements: PHP, Composer, Docker, Docker Compose, GNU Make.
cp .env.dist .env # Configure PS_VERSION_TAG, MODULE_VERSION, etc.
cp <MODULE_VERSION>/.env.dist <MODULE_VERSION>/.env
cp docker-compose.local.yml.dist docker-compose.local.yml
make build # Build Docker images
make up # Start containers + install root dependenciesThe shop runs at http://localhost:8991 (admin: demo@prestashop.com / prestashop_demo).
Key .env variables:
MODULE_VERSION:ps17,ps8, orps9— controls which PrestaShop container to targetPS_VERSION_TAG: Docker image tag for the PrestaShop version (e.g.,8)SENTRY_DSN: Glitchtip/Sentry DSN for error monitoring
make lint # php-cs-fixer + autoindex
make php-cs-fixer # Fix code style only
make phpstan # PHPStan static analysis (uses $MODULE_VERSION)
make phpstan-baseline # Regenerate PHPStan baseline for current $MODULE_VERSION
make phpstan-baseline-all # Regenerate baselines for all versions
make unit-test # All unit tests (api, utility, core, presentation)
make integration-test # All integration tests (module, core, infrastructure)
make test # Full test suite
# Run a single test suite directly
make php-unit-core
make php-unit-api
make php-unit-utility
make php-unit-presentation
make php-integration
make php-integration-core
make php-integration-infrastructure
make ssh # Shell into the running PrestaShop container
make install-module # Install the module in PrestaShop via consoleModule logs (inside project root, not container): prestashop/<PS_VERSION_TAG>/var/logs/ps_checkout-*-<YYYY-MM-DD>.
All make test commands run inside the Docker container using $MODULE_VERSION and $PS_VERSION_TAG from .env.
make phpstan runs locally without Docker. All make *-test commands require a running Docker container (make up).
Do not run phpunit directly from the host — cross-package autoloading (e.g., api/ classes in core/ tests) only resolves inside the Docker container via the module's vendor/autoload.php. Always use make commands for tests.
Code style check without fixing: composer cs (from root).
The repository targets three PrestaShop versions using a monorepo approach:
ps17/ — PS 1.7 module shell (PHP ^7.1)
ps8/ — PS 8.x module shell (PHP ^7.2|^8.1)
ps9/ — PS 9.x module shell (PHP ^8.1, Symfony 6.4)
Each version directory contains:
ps_checkout.php— main module entry point extendingPaymentModulesentry.php— Sentry/Glitchtip error monitoring initializationconfig/— Symfony service container YAML definitionstests/— module-level integration testscomposer.json— version-specific dependencies
The version-specific directories are thin shells. All business logic lives in the shared monorepo packages below.
The monorepo packages are in the root alongside ps17/, ps8/, ps9/ and symlinked/mounted into each version's vendor/invertus/:
| Package | Namespace | Responsibility |
|---|---|---|
api/ |
PsCheckout\Api\ |
HTTP controllers, DTOs, value objects |
core/ |
PsCheckout\Core\ |
Business logic: orders, webhooks, PayPal, settings, payment tokens |
infrastructure/ |
PsCheckout\Infrastructure\ |
PrestaShop adapters, repositories, controllers, loggers |
presentation/ |
PsCheckout\Presentation\ |
Presenters for front/back-office UI |
utility/ |
PsCheckout\Utility\ |
Shared array/string/payload utilities |
The ps<version>/src/ directory (namespace PsCheckout\Module\) contains only version-specific presentation overrides.
PayPal/— PayPal API integration (orders, payments, webhooks, funding sources, refunds, Apple/Google Pay)Order/+OrderState/— Order processing and status transitionsWebhook/+WebhookDispatcher/— Webhook event handling pipelineSettings/— Configuration managementPaymentToken/— Payment token lifecycle
Adapter/— Bridge between the core domain and PrestaShop's legacy APIsRepository/— Database access layerBootstrap/— Module installation/upgrade logicController/— Web controllers (front + admin)Environment/— Runtime environment detection
ci.yml— runs tests on pull requestslint.yml— runs linting checkscreate-testing-zip.yml— generates module ZIP for INT/PREPROD environmentsgithub-release.yml— builds and attaches per-PS-version ZIPs to GitHub Releases (prereleased + released)publish-to-marketplace.yml— publishes to PrestaShop Marketplace (triggered bygithub-release.ymlcompletion, latest releases only)
make phpstan runs locally (no Docker needed). When modifying shared packages, always run make phpstan and fix reported errors. Only regenerate the baseline (make phpstan-baseline) if errors are pre-existing or unfixable (e.g., baseline count mismatches from removed code).
- Test files: PHPStan requires explicit return types on test methods (
: void),@returnannotations on data providers (e.g.,@return array<string, array{int, string}>), and@vartype hints when accessing nested array results fromhandle()methods. make phpstanonly checks the version set in$MODULE_VERSION. To verify all versions, also runcomposer phpstanfrom withinps9/andps17/(or whichever versions aren't the default).- PHPStan baselines (
ps8/phpstan-baseline.neon,ps9/phpstan-baseline.neon,ps17/phpstan-baseline.neon) have identical entries for sharedcore/test files — when updating baseline counts, apply the same change to all three.
PHP-CS-Fixer applies to: api/, core/, infrastructure/, presentation/, utility/ — not to the ps17/, ps8/, ps9/ version directories. Rules: PSR-2, AFL-3.0 header comment, no unused imports.
Each version (ps{8,9,17}/config/) has separate service containers for admin and front contexts. A service registered in config/front/*.yml is not available in admin controllers, and vice versa. If an admin controller calls $this->module->getService(Foo::class) and gets ServiceNotFoundException, check whether Foo is only registered in config/front/ and needs an entry in config/admin/ too.
Follow PrestaShop coding standards. Do not update the module version number in pull requests.
- Every new directory must contain an
index.phpfile (security requirement). Copy from any existingindex.phpincore/tests/.make lintruns autoindex which creates missing ones, but adding them upfront keeps commits clean.
- Response method: Use
exitWithResponse(['httpCode' => N, 'status' => bool, ...])for all JSON responses — nothttp_response_code()+ajaxRender(json_encode(...)). Noreturnneeded;exitWithResponseexits internally. - Request encoding: Admin AJAX calls must POST with
Content-Type: application/x-www-form-urlencodedsoTools::getValue('action')can route to the correctajaxProcess*method.application/jsonbodies are invisible toTools::getValue.
views/js/<name>.js— definesvar ps_checkout_<name> = {}with aninitialize(config)method (never inline in a template)views/templates/hook/partials/<name>.tpl— thin wrapper: guard withtypeof ps_checkout_<name> !== 'undefined', then call.initialize({...})with Smarty-escaped config values- Register in
hookActionAdminControllerSetMediaviaaddJS(...'?version=' . $this->version . '&rand=' . time(), false) - JS files under
ps{8,9,17}/views/js/are identical across versions — create one,cpto the other two - Admin controllers (
ps{8,9,17}/controllers/admin/) are nearly identical across versions — apply the same edit to all three. Exception: ps9 uses\Exception(namespaced), ps8/ps17 useException(global). - Logging in catch blocks: Always add local logging via
$this->module->getService(LoggerInterface::class)->error(...)alongside\Sentry\captureException(). Sentry alone is insufficient — it may be unconfigured in dev/staging. - Admin error messages: Default/fallback error messages in admin catch blocks should append
$exception->getMessage()for merchant debugging context. Customer-facing defaults should remain generic.