Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 35 additions & 23 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,46 @@
## Description
<!-- Provide a clear summary of the changes and the problem it solves. -->
<!-- Include any relevant context or background information. -->
<!-- Describe the changes introduced by this PR -->

**Motivation:**
## Related Issue
<!-- Link to the Jira ticket or GitHub issue -->
Ticket: [PRE-XXXX](https://payplug.atlassian.net/browse/PRE-XXXX)

**Related issue(s):** Closes #
## Type of Change
<!-- At least one required β€” replace [ ] with [x] -->
[ ] πŸ› Bug fix
[ ] ✨ New feature
[ ] πŸ’₯ Breaking change
[ ] ♻️ Refactor
[ ] πŸ”§ Configuration / CI
[ ] πŸš€ Release (`release/*` branch targeting `master`)
[ ] πŸ“¦ Dependency update
[ ] πŸ”’ Security fix
[ ] πŸ“ Documentation update

---

## Type of Change
<!-- check the corresponding checkbox(es) with an "x" -->
- πŸ› Bug fix (non-breaking change that fixes an issue) [ ]
- ✨ New feature (non-breaking change that adds functionality) [ ]
- πŸ’₯ Breaking change (fix or feature that causes existing functionality to change and that could impact other libs) [ ]
- πŸ”§ Refactor (no functional changes, code improvement only) [ ]
- πŸ“¦ Dependency update [ ]
- πŸ”’ Security fix [ ]
- πŸ“ Documentation update [ ]
## βœ… Quality Checklist

### Local Environment & Hooks
- [ ] Local Git hooks (**CaptainHook**) are installed and executed cleanly.
- [ ] Commit messages strictly follow the `(PRE|SMP)-XXXX: description` pattern.
- [ ] Core configuration files (`phpstan.neon` / `.php-cs-fixer.php`) were generated successfully from `.dist` templates.

### Testing & Code Quality
- [ ] Coding style rules have been applied locally (`composer cs:fix`).
- [ ] Static analysis checks pass with no new regressions (`vendor/bin/phpstan`).
- [ ] I have added/updated unit or integration tests if applicable.
- [ ] I have verified these changes locally on a native PrestaShop environment.

### CI/CD Deployment Context
- [ ] The CI pipeline passes fully on GitHub.
- [ ] **For Release Branches:** If this is a `release/*` branch, I am targeting the correct base branch to allow the automated `apply-release` version bumping job to run.

---

## Checklist
### Code Quality
- [ ] Code is linted and formatted
- [ ] No unnecessary commented-out code or debug logs
- [ ] No hardcoded values (use env variables or config)
## Screenshots (if applicable)
<!-- Add screenshots or screen recordings if relevant -->

### Testing
- [ ] Unit tests added / updated
## Notes for Reviewer
<!-- Anything specific the reviewer should pay attention to -->

### Security & Ops
- [ ] No sensitive data or secrets introduced
- [ ] Logging and error handling are appropriate
126 changes: 126 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Copilot Instructions

These instructions apply to code reviews on pull requests in this repository.

## Project Context

This is `payplug-plugin-mcp`, a PHP library that acts as the core integration layer for Payplug across multiple e-commerce plugins (WooCommerce, PrestaShop, etc.). It handles payment creation, refunds, DTO hydration, validation, and API communication via the `payplug/payplug-php` SDK.

**Stack**: PHP 7.4 (development and composer constraint), `payplug/payplug-php ^4.0`, PHPUnit 9.5, Mockery ^1.3

**PHP compatibility note**: the composer constraint is `^7.4`, but the source code is intentionally written without PHP 7.4+ syntax (no typed properties, no arrow functions, no `??=`) so that existing client deployments still running PHP 7.1 are not broken at runtime. Do not conflate the composer requirement with the runtime compatibility target.

**Namespace**: `PayPlugPluginMcp\`

**Key structure**:
- `src/Actions/PaymentAction.php` β€” orchestrates payment and refund flows
- `src/Gateways/` β€” payment method gateways (`StandardPaymentGateway`, `EmailLinkPaymentGateway`) and `RefundGateway`
- `src/Models/Entities/` β€” DTOs: `PaymentInputDTO`, `PaymentOutputDTO`, `RefundInputDTO`, `RefundOutputDTO`
- `src/Validators/` β€” `PaymentResourceValidator`, `RefundValidator`
- `src/Utilities/Services/Api.php` β€” wraps the PayPlug PHP SDK

## Intentional Patterns β€” Do Not Flag as Issues

- **Docblock type hints instead of typed properties** β€” all class properties use `/** @var type */` style on purpose. Typed properties are a PHP 7.4 feature; since this code must remain syntactically compatible with PHP 7.1 runtimes, they cannot be used. Do not suggest converting docblocks to typed properties.
- **`get_api()`, `get_payment_gateway()`, `get_refund_gateway()` are public in `PaymentAction`** β€” these factory methods are public so they can be mocked in unit tests via Mockery. This is the intended testability pattern for this project.
- **Amount in cents** β€” all monetary amounts in DTOs and API calls are in the smallest currency unit (cents). Any conversion must be explicit and done by the calling plugin, not this library.

## PayPlug PHP SDK β€” Dynamic Properties

The `payplug/payplug-php` SDK models (`Payplug\Resource\Payment`, `Payplug\Resource\Refund`, etc.) use **dynamic properties** β€” API response fields are set at runtime via `__set()` and are not declared as class properties. This means:

- Static analysis tools (PHPStan, IDEs) will emit warnings like `Property Payment::$failure does not exist` or `Property Payment::$is_paid does not exist` β€” **these are expected and not bugs**.
- There is no compile-time guarantee that a given field exists on a resource object. All access to SDK resource properties is inherently dynamic.
- Do not flag missing property declarations on SDK resource classes as issues.
- Do not suggest adding `@property` annotations to SDK classes β€” they are a vendored dependency.
- Null-checks before accessing SDK properties (e.g. `isset($payment->failure)`, `null !== $resource->id`) are intentional defensive patterns, not unnecessary guards.

## Code Review Dimensions

### Security
- API bearer tokens must never appear in logs, exception messages, or HTTP responses
- Payment amounts must be validated server-side β€” `RefundValidator` enforces this; bypassing it is a bug
- `redirect_url` values must come from the PayPlug API response, never constructed from user input
- IPN/webhook payloads must be verified via the SDK before any processing
- Card data (PAN, CVV) must never appear in logs, error messages, or stored metadata

### Performance
- Unnecessary object instantiation in hot paths
- Unbounded loops or unvalidated array traversal in DTOs
- Algorithmic complexity in validators

### Correctness
- `declare(strict_types=1)` is required on all files β€” flag any new file missing it
- PHP 7.4+ syntax (arrow functions, typed properties, `??=`, named arguments) must not be introduced β€” the source code must remain syntactically compatible with PHP 7.1 runtimes even though the composer constraint is `^7.4`
- DTOs must validate all required fields in `hydrate()` and throw on missing/invalid required inputs
- Refund amount must not exceed `payment.amount - payment.amount_refunded`
- `PaymentResourceValidator::validateIsPaid()` must be called before any refund β€” skipping it is a bug
- API bearer token must be loaded (`Api::load()`) before any SDK call β€” using an uninitialised API client is a bug
- `RefundOutputDTO` and `PaymentOutputDTO` must reflect the exact API response structure β€” silent field drops are bugs

### Maintainability
- Naming clarity, single responsibility, duplication
- Test coverage: PHPUnit in `tests/` with unit and integration groups
- PHPStan compliance at configured level β€” suppressions must go in the baseline, not inline
- PHP-CS-Fixer compliance with `.php-cs-fixer.dist.php` β€” `@PHP71Migration` ruleset, no 7.4+ fixer rules
- New gateway classes must extend `AbstractPaymentGateway` and implement `formatPaymentAttributes()`
- New validators must throw `\Exception` with a descriptive message on failure β€” no silent returns

## Output Format

Structure the review comment exactly as follows:

### 1. What's Good

A bullet list of positive observations β€” things done well, non-obvious correct decisions, solid patterns.

---

### 2. Summary table

A markdown table with two columns: **Dimension** and **Rating**. One row per review dimension. Use emoji inline with the rating text:

| Dimension | Rating |
|---|---|
| Security | βœ… Fine |
| Correctness | ⚠️ Medium (short reason) |
| Performance | βœ… Fine |
| Maintainability | ⚠️ Low (short reason) |

Severity scale:
- βœ… **Fine** β€” no issues
- ⚠️ **Low / Medium** β€” should be fixed but not blocking
- ❌ **High / Critical** β€” must be fixed before merge

---

### 3. Closing one-liner

A single sentence summarising what needs to be addressed before merge (or that the PR is ready if nothing critical).

---

### 4. Individual findings (one section per issue)

Each finding follows this exact structure:

**Heading:** `[Dimension] [emoji] [Severity]` β€” e.g. `Security ⚠️ Medium`

**Subtitle (bold):** short title followed by the file path and line number as a markdown link β€” e.g. `**Missing bearer validation** (Api.php:42)`

**Code block:** the relevant snippet from the diff showing the problem.

**Explanation paragraph:** what the risk is and why it matters. Be concrete.

**Fix line:** start with `Fix:` in bold, then a brief description, followed by a code block showing the suggested fix.

Lead with Critical/High findings. Omit the findings section entirely if there are no issues.

## Iterative Reviews

When reviewing a new commit on a PR that already has open review threads:

- **Resolve threads** for issues that have been addressed in the new commit β€” do not leave them open if the fix is present.
- **Do not re-open or re-comment** on issues that were already resolved in a previous round.
- Only open new threads for issues that are genuinely new or that remain unresolved.
- If a previous finding was partially addressed, update the thread with what still needs attention rather than opening a duplicate.
10 changes: 10 additions & 0 deletions .github/workflows/auto-tag-rc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: Auto-tag RC0

'on':
create:

jobs:
create-rc0-tag:
if: startsWith(github.ref, 'refs/heads/release/')
uses: payplug/template-ci/.github/workflows/auto_tag_rc.yml@main
secrets: inherit
108 changes: 14 additions & 94 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,110 +1,30 @@
name: CI

on:
push:
branches-ignore:
- main # main is never pushed to directly; releases are tag-driven
tags:
- '*'
pull_request:
branches:
- develop
types: [opened, synchronize, reopened, closed]
- master
types: [opened, synchronize, reopened]

jobs:
# -----------------------------------------------------------------------
# 1. QUALITY β€” static analysis & code style on PHP 8.4 source
# Runs on: push to feature branch | PR opened/updated β†’ develop
# QUALITY β€” static analysis, code style & unit tests on PHP 7.4
# Runs on: PR opened/updated β†’ develop or master
# -----------------------------------------------------------------------
quality:
if: github.ref_type != 'tag' && github.event.action != 'closed'
uses: payplug/template-ci/.github/workflows/php-quality.yml@main
with:
php-version: '8.4'
php-version: '7.4'

# -----------------------------------------------------------------------
# 2. DOWNGRADE β€” transpile src/ + tests/ to PHP 7.2
# Runs on: push to feature branch | PR opened/updated β†’ develop
# -----------------------------------------------------------------------
downgrade:
if: github.ref_type != 'tag' && github.event.action != 'closed'
needs: quality
uses: payplug/template-ci/.github/workflows/php-downgrade.yml@main
with:
php-version: '8.4'
artifact-name: 'payplug-core-php72'
artifact-retention-days: 7

# -----------------------------------------------------------------------
# 3. VALIDATE β€” install the downgraded package under PHP 7.2
# Runs on: push to feature branch | PR opened/updated β†’ develop
# -----------------------------------------------------------------------
validate:
if: github.ref_type != 'tag' && github.event.action != 'closed'
needs: downgrade
uses: payplug/template-ci/.github/workflows/php-validate.yml@main
with:
php-version: '7.2'
artifact-name: 'payplug-core-php72'

# -----------------------------------------------------------------------
# 4. TEST β€” run unit tests against the downgraded PHP 7.2 code
# Runs on: push to feature branch | PR opened/updated β†’ develop
# -----------------------------------------------------------------------
test-php72:
if: github.ref_type != 'tag' && github.event.action != 'closed'
needs: validate
uses: payplug/template-ci/.github/workflows/php-test-php72.yml@main
with:
php-version: '7.2'
artifact-name: 'payplug-core-php72'
test-namespace: 'PayplugPluginCore\tests\'

# -----------------------------------------------------------------------
# 5. PUSH-DIST β€” push transpiled code to dist/{php-target}/{branch}
# Runs in parallel with validate, as soon as downgrade succeeds.
# On feature branch push β†’ dist/php72/feature/xxxx
# On push to develop β†’ dist/php72/develop
# -----------------------------------------------------------------------
push-dist:
if: github.ref_type != 'tag' && github.event.action != 'closed'
needs: validate
permissions:
contents: write
uses: payplug/template-ci/.github/workflows/php72-push-dist.yml@main
with:
artifact-name: 'payplug-core-php72'
source-branch: ${{ github.head_ref || github.ref_name }}
php-target: 'php72'

# -----------------------------------------------------------------------
# 6. PACKAGE β€” zip and store the artifact on PR merged β†’ develop
# -----------------------------------------------------------------------
package:
if: github.ref_type != 'tag' && github.event.action != 'closed'
uses: payplug/template-ci/.github/workflows/php-package.yml@main
with:
php-version: '8.4'
zip-prefix: 'payplug-plugin-core-php72'

# -----------------------------------------------------------------------
# 7. CLEANUP-DIST β€” delete dist/php72/feature/xxxx when PR is merged
# -----------------------------------------------------------------------
cleanup-dist:
if: github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true
permissions:
contents: write
uses: payplug/template-ci/.github/workflows/php72-delete-dist.yml@main
with:
source-branch: ${{ github.head_ref }}
php-target: 'php72'

# -----------------------------------------------------------------------
# 8. RELEASE β€” create a GitHub Release from a tag on main
# -----------------------------------------------------------------------
release:
if: github.event_name == 'push' && github.ref_type == 'tag'
uses: payplug/template-ci/.github/workflows/php-release.yml@main
sonarcloud:
if: always() && !failure() && !cancelled() && github.base_ref == 'develop'
needs: [ quality ]
uses: payplug/template-ci/.github/workflows/sonarcloud.yml@main
with:
php-version: '8.4'
zip-prefix: 'payplug-plugin-core-php72'
project-name: 'github-payplug-payplug-payplug-plugin-mcp'
src-folder: 'src/'
secrets:
sonar-orga: ${{ secrets.SONAR_ORGA }}
sonar-token: ${{ secrets.SONAR_TOKEN }}
17 changes: 17 additions & 0 deletions .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Release β€” RC

'on':
push:
tags:
- '*.*.*-rc*'

jobs:
quality:
uses: payplug/template-ci/.github/workflows/php-quality.yml@main
with:
php-version: '7.4'

github-release:
needs: quality
uses: payplug/template-ci/.github/workflows/github_release_rc.yml@main
secrets: inherit
12 changes: 12 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Release

'on':
push:
tags:
- '*.*.*'
- '!*.*.*-rc*'

jobs:
github-release:
uses: payplug/template-ci/.github/workflows/github_release.yml@main
secrets: inherit
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ vendor
# PHP CS Fixer
.php-cs-fixer.php
.php-cs-fixer.cache
.php_cs.cache

# PHPStan
var/
payplug-core/
.claude
.claude
docs/
4 changes: 2 additions & 2 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
->setRiskyAllowed(true)
->setRules([
'@PSR12' => true,
'@PHP84Migration' => true,
'@PHP71Migration' => true,
'array_syntax' => ['syntax' => 'short'],
'no_unused_imports' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'single_quote' => true,
'trailing_comma_in_multiline' => true,
'trailing_comma_in_multiline' => ['elements' => ['arrays']],
'declare_strict_types' => true,
'void_return' => true,
'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced'],
Expand Down
Loading
Loading