diff --git a/.agents/skills/package-compatibility/SKILL.md b/.agents/skills/package-compatibility/SKILL.md new file mode 100644 index 0000000..fcb2579 --- /dev/null +++ b/.agents/skills/package-compatibility/SKILL.md @@ -0,0 +1,41 @@ +--- +name: package-compatibility +description: "Use this skill when reviewing Laravel package compatibility across composer constraints, PHP versions, Laravel versions, Testbench versions, dependency stability lanes, Windows CI, or matrix-sensitive code and workflow changes." +license: MIT +metadata: + author: laravel +--- + +# Package Compatibility + +## Primary Goal + +Keep package code, dependencies, and workflows compatible with the supported Laravel 12/13 and PHP 8.3+ matrix. + +## Workflow + +1. Read `composer.json` first to determine PHP, Laravel, and Testbench constraints. +2. Check changed code against Laravel 12/13 APIs and PHP 8.3+ syntax before adopting newer framework or language features. +3. Review `.github/workflows/tests.yml` for dependency stability lanes, prefer-lowest coverage, prefer-stable coverage, and Windows concerns. +4. When changing dependencies, confirm constraints still allow the intended Laravel and Testbench versions. +5. Validate with the smallest local command available, then rely on CI for full OS and dependency matrix coverage. + +## References + +- `composer.json` +- `.github/workflows/tests.yml` +- `phpstan.neon.dist` +- `tests/` +- `workbench/` + +## Examples + +- Review a new Laravel API call by checking whether it exists in Laravel 12 and Laravel 13 before merging it into shared package code. +- Review a dependency bump by checking Composer constraints, Testbench constraints, prefer-lowest behavior, and Windows path assumptions. + +## Anti-Patterns + +- Assuming the latest local dependency version represents the whole support matrix. +- Adding PHP syntax or Laravel APIs that exceed `composer.json` constraints. +- Ignoring Windows path separators, executable assumptions, or shell-only syntax in tests and workflows. +- Removing dependency stability lanes because they are slower than a single happy path. diff --git a/.agents/skills/package-generate-skill/SKILL.md b/.agents/skills/package-generate-skill/SKILL.md new file mode 100644 index 0000000..5365689 --- /dev/null +++ b/.agents/skills/package-generate-skill/SKILL.md @@ -0,0 +1,55 @@ +--- +name: package-generate-skill +description: "Use this skill when creating or updating the bundled Laravel Boost skill under resources/boost/skills from the package implementation and package documentation. Trigger after public APIs, commands, config, routes, views, publish tags, README content, or examples change." +license: MIT +metadata: + author: laravel +--- + +# Package Generate Skill + +## Primary Goal + +Keep the package's bundled Boost skill accurate, concise, and focused on helping Laravel applications adopt the package. + +## Workflow + +1. Inspect the package implementation before editing the Boost skill: service provider, facades, public classes, commands, config, routes, migrations, events, views, publish tags, and tests. +2. Inspect package documentation: `README.md`, contributing docs, examples, and changelog entries that describe user-facing behavior. +3. Identify the public integration surface only. Include install, configure, publish, command, route, facade, helper, middleware, event, and testing guidance only when the package actually exposes it. +4. Update `resources/boost/skills/*/SKILL.md` with practical adoption steps, references, examples, and anti-patterns for Laravel app developers using the package. +5. Preserve front matter, package metadata, and the Boost skill structure: description, primary goal, workflow, references, examples, and anti-patterns. +6. Validate that the Boost skill does not describe internals as public API and does not document features that are not implemented. + +## Writing Rules + +- Write for consumers installing the package in a Laravel application, not for maintainers changing package internals. +- Prefer short, task-oriented steps over broad explanations. +- Point to concrete files only when they help the consumer understand package adoption. +- Keep examples runnable and aligned with documented package names, config keys, commands, and publish tags. +- Keep the skill small enough for an agent to load and apply quickly. + +## References + +- `resources/boost/skills/` +- `src/*ServiceProvider.php` +- `src/Facades/` +- `src/Console/Commands/` +- `config/*.php` +- `routes/*.php` +- `database/migrations/` +- `README.md` +- `tests/Feature/` and `tests/Unit/` + +## Examples + +- After adding a new Artisan command, update the Boost skill with when to run it, required options, expected output, and any related config. +- After adding config and publish tags, update the Boost skill with publish commands and the minimum config keys users need to set. +- After adding a facade or public class, update the Boost skill with one practical integration example and the test assertion a consuming app should use. + +## Anti-Patterns + +- Regenerating the Boost skill from documentation alone without checking implementation. +- Documenting private classes, test helpers, workbench-only routes, or implementation details as consumer API. +- Adding speculative examples for features the package does not provide. +- Removing package metadata that consuming agents need to identify and apply the skill. diff --git a/.agents/skills/package-release/SKILL.md b/.agents/skills/package-release/SKILL.md new file mode 100644 index 0000000..3179c8b --- /dev/null +++ b/.agents/skills/package-release/SKILL.md @@ -0,0 +1,41 @@ +--- +name: package-release +description: "Use this skill when preparing Laravel package releases: CHANGELOG.md updates, generated release notes, GitHub release workflows, version checks, tags, release validation, or release automation changes. Never publish autonomously." +license: MIT +metadata: + author: laravel +--- + +# Package Release + +## Primary Goal + +Prepare a safe package release checklist and implementation without tagging, pushing, or publishing unless the user explicitly approves that action. + +## Workflow + +1. Review `CHANGELOG.md`, generated release notes config, GitHub release workflows, open diff, and pending package changes. +2. Validate the release state with `composer test` before recommending a release. +3. Confirm whether version metadata needs to change; many Laravel packages rely on Git tags rather than a hardcoded package version. +4. Review tag naming, release branch, and GitHub release workflow behavior before any release command. +5. Do not tag, push, or publish without explicit user approval. + +## References + +- `CHANGELOG.md` +- `.github/release.yml` +- `.github/workflows/update-changelog.yml` +- `.github/workflows/tests.yml` +- `composer.json` + +## Examples + +- Prepare a release by checking changelog coverage, confirming generated release notes categories, running `composer test`, and drafting the tag command for user approval. +- Update release notes grouping in `.github/release.yml` when a new label convention is added. + +## Anti-Patterns + +- Creating tags, pushing branches, or publishing releases without explicit approval. +- Skipping `composer test` before a release recommendation. +- Treating generated release notes as a replacement for meaningful `CHANGELOG.md` entries. +- Changing release workflows without checking the supported matrix in `.github/workflows/tests.yml`. diff --git a/.agents/skills/package-scaffold/SKILL.md b/.agents/skills/package-scaffold/SKILL.md new file mode 100644 index 0000000..971c51c --- /dev/null +++ b/.agents/skills/package-scaffold/SKILL.md @@ -0,0 +1,63 @@ +--- +name: package-scaffold +description: "Use this skill when adding Laravel package capabilities or wiring them through the service provider: commands, migrations, routes, config merges, views, translations, assets, middleware, publish tags, workbench files, or console-only behavior." +license: MIT +metadata: + author: laravel +--- + +# Package Scaffold + +## Primary Goal + +Add package features in the right place and wire them through the service provider using explicit Laravel APIs, keeping package names, namespaces, config keys, and publish tags consistent. + +## Workflow + +1. Inspect the existing package structure, sibling examples, README setup notes, and the current service provider before creating files. +2. Identify whether the request touches commands, migrations, routes, config, views, translations, assets, middleware, tests, README/contributing docs, compatibility, or release flow. +3. Create the capability files under Laravel-native package paths and use the configured package names, namespaces, publish tags, URLs, and badges consistently. +4. Wire the capability through the service provider using the patterns in *Provider wiring* below. +5. Use `package-testing` for coverage, update README or contributing documentation when user-facing behavior changes, use `package-compatibility` for matrix-sensitive changes, and use `package-release` for release tasks. +6. Add only the files needed for the requested capability and validate with the narrowest relevant command before broader checks. + +## Provider Wiring + +1. Keep provider wiring in `register()` or `boot()` unless extracting a method makes a real repeated concern clearer. +2. Put container bindings and `mergeConfigFrom` calls in `register()` when the host app must be able to override configuration. +3. Put resource loading in boot-time methods with Laravel-native APIs such as `loadRoutesFrom`, `loadViewsFrom`, and `loadTranslationsFrom`. +4. Guard console-only publishing and command registration with `runningInConsole()` before calling `publishes`, `publishesMigrations`, or `commands`. +5. Name publish tags with the `simplepay-*` convention so consumers can target individual resource groups. +6. Add tests for the observable provider behavior: merged config, loaded routes, publish tags, or command registration. + +Provider wiring anti-patterns: + +- Loading host app state too early during provider registration. +- Calling `env()` outside config files; use config values after `mergeConfigFrom` instead. +- Registering web-only concerns unconditionally when the package can run in console contexts. +- Replacing explicit provider methods with `spatie/laravel-package-tools` by default. + +## References + +- `src/*ServiceProvider.php` +- `config/*.php` +- `routes/*.php` +- `resources/views/` +- `lang/` +- `database/migrations/` +- `public/` +- `src/Console/Commands/` +- `tests/Feature/` and `tests/Unit/` + +## Examples + +- Add an Artisan command: create the command class under `src/Console/Commands`, register it in the `commands` array inside the `runningInConsole()` guard, add a feature test for observable console output, and document the command if it is user-facing. +- Add a publishable migration: place the migration in `database/migrations`, wire it through a console-guarded `publishesMigrations` call with a `simplepay-migrations` tag, and test publish behavior with Testbench. +- Wire a new publish tag by adding a `publishes` map inside the existing console-guarded publishing method and naming the tag with `simplepay-*`. + +## Anti-Patterns + +- Adding unused files because a package might need them later. +- Mixing package names, namespaces, config keys, or publish tags. +- Changing dependencies without approval. +- Replacing explicit Laravel package code with a helper abstraction when one feature-specific change would do. diff --git a/.agents/skills/package-testing/SKILL.md b/.agents/skills/package-testing/SKILL.md new file mode 100644 index 0000000..321dfa5 --- /dev/null +++ b/.agents/skills/package-testing/SKILL.md @@ -0,0 +1,43 @@ +--- +name: package-testing +description: "Use this skill when writing, editing, fixing, or reviewing package tests with Pest 4/5 and Orchestra Testbench, including TDD, feature tests, unit tests, type coverage, arch tests, workbench behavior, commands, routes, config, migrations, and publishable resources." +license: MIT +metadata: + author: laravel +--- + +# Package Testing + +## Primary Goal + +Prove package behavior with Pest 4/5, Orchestra Testbench, and the local `tests/TestCase.php` setup before or alongside implementation. + +## Workflow + +1. Start with TDD: write the smallest failing package test for the requested behavior, then implement the smallest change that makes it pass. +2. Cover happy-path, unhappy-path, and edge-case behavior when the feature has meaningful failure modes. +3. Prefer focused feature tests for package integration behavior and arch tests for broad constraints. +4. Use `composer test:unit -- --filter ...` while iterating, `composer test:types` when type-sensitive tests or code changed, and `composer test` before finishing. +5. Keep real package tests in the suite and remove only throwaway tests that were explicitly created for local scaffolding experiments. + +## References + +- `tests/TestCase.php` +- `tests/Pest.php` +- `tests/Feature/` +- `tests/Unit/` +- `tests/ArchTest.php` +- `composer.json` scripts + +## Examples + +- Test config merge and config override by asserting default package config, then overriding the host config value in the Testbench app. +- Test publishable assets, migrations, views, lang files, or config by invoking vendor publish behavior and asserting the target path exists. +- Test routes with Testbench HTTP requests, commands with Artisan assertions, migrations with a SQLite test database, and workbench behavior after `composer build` when needed. + +## Anti-Patterns + +- Deleting real package tests because they are inconvenient. +- Relying only on smoke tests when behavior needs assertions. +- Testing implementation details when observable package behavior is available. +- Keeping throwaway scaffolding experiment tests in the package test suite. diff --git a/.claude b/.claude new file mode 120000 index 0000000..c0ca468 --- /dev/null +++ b/.claude @@ -0,0 +1 @@ +.agents \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..81162ca --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml,json}] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c2b57d0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,18 @@ +* text=auto eol=lf + +/.github export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.editorconfig export-ignore +/tests export-ignore +/phpunit.xml.dist export-ignore +/phpstan.neon.dist export-ignore +/pint.json export-ignore +/testbench.yaml export-ignore +/workbench export-ignore +/.claude export-ignore +/.agents export-ignore +/CLAUDE.md export-ignore +/AGENTS.md export-ignore +/art export-ignore +/official-docs export-ignore diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..a871547 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contribution Guide + +Thank you for considering contributing to SimplePay for Laravel! Please review the following guidelines before submitting a pull request. + +For significant changes, please open an issue first so we can discuss the approach. + +## Process + +1. Fork the project +2. Create a new branch +3. Code, test, commit, and push +4. Open a pull request detailing your changes + +## Guidelines + +- Ensure the coding style passes by running `composer lint`. +- Send a coherent commit history, making sure each commit in your pull request is meaningful. +- You may need to [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) to avoid merge conflicts. +- Please remember that we follow [SemVer](http://semver.org/). + +## Setup + +Clone your fork, then install the dev dependencies: + +```bash +composer install +``` + +## Lint + +Lint your code: + +```bash +composer lint +``` + +## Tests + +Run all tests: + +```bash +composer test +``` diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..2ec89f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,69 @@ +name: Bug Report +description: Report something that is not working as expected. +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug. Please complete the fields below so we can reproduce the issue quickly. + + - type: input + id: package-version + attributes: + label: Package Version + description: Provide the package version that you are using. + placeholder: 1.0.0 + validations: + required: true + + - type: input + id: laravel-version + attributes: + label: Laravel Version + description: Provide the Laravel version that you are using. + placeholder: 13.0.0 + validations: + required: true + + - type: input + id: php-version + attributes: + label: PHP Version + description: Provide the PHP version that you are using. + placeholder: 8.3.0 + validations: + required: true + + - type: input + id: operating-system + attributes: + label: Operating System + description: If relevant, provide the operating system and version where this occurs. + placeholder: macOS 15.0, Ubuntu 24.04, Windows 11 + validations: + required: false + + - type: textarea + id: description + attributes: + label: Description + description: Provide a clear description of the issue you are facing. + validations: + required: true + + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps To Reproduce + description: Provide detailed steps to reproduce the issue. A minimal reproduction repository is helpful when possible. + validations: + required: true + + - type: textarea + id: notes + attributes: + label: Notes + description: Add any other context that might help diagnose the issue. + validations: + required: false diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..670ca0b --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +**PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, [SEE BELOW](#reporting-a-vulnerability).** + +## Supported Versions + +Only the latest major version receives security fixes. + +## Reporting a Vulnerability + +If you discover a security vulnerability within SimplePay for Laravel, please send an email to NETipar at dev@netipar.hu. All security vulnerabilities will be promptly addressed. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f090fce --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "dependencies" + + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "weekly" + labels: + - "dependencies" diff --git a/.github/phpstan-windows.neon b/.github/phpstan-windows.neon new file mode 100644 index 0000000..dda11d0 --- /dev/null +++ b/.github/phpstan-windows.neon @@ -0,0 +1,15 @@ +# PHPStan configuration for the Windows CI lane. +# +# Larastan boots a Laravel application (via Testbench) in every PHPStan worker, +# and each boot rewrites the Testbench skeleton's bootstrap/cache/services.php +# through a write-to-temp-file followed by rename() onto the shared path. +# POSIX rename() tolerates concurrent writers; Windows does not, and the losing +# worker dies with "Access is denied (code: 5)". A single process serialises +# those writes; only the Windows lane pays the speed cost. + +includes: + - ../phpstan.neon.dist + +parameters: + parallel: + maximumNumberOfProcesses: 1 diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..fc53f3c --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,27 @@ +changelog: + exclude: + labels: + - skip-changelog + - duplicate + categories: + - title: Breaking Changes + labels: + - breaking + - title: Enhancements + labels: + - enhancement + - title: Bug Fixes + labels: + - bug + - title: Documentation + labels: + - documentation + - title: Dependencies + labels: + - dependencies + - title: Maintenance + labels: + - maintenance + - title: Other Changes + labels: + - "*" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8787bca..f1a3bf6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,66 +1,90 @@ -name: Tests +name: tests on: push: branches: - main + - '*.x' pull_request: - branches: - - main - -jobs: - pint: - runs-on: ubuntu-latest - name: Pint - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.4 - extensions: dom, libxml, simplexml - coverage: none - - name: Install dependencies - run: composer install --prefer-dist --no-interaction +permissions: + contents: read - - name: Run Pint - run: vendor/bin/pint --test +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: tests: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: - fail-fast: true + # Let every lane report, so a break shows whether it is matrix-wide or + # specific to one PHP/Laravel/stability combination. The concurrency + # group above still cancels superseded runs. + fail-fast: false matrix: - php: [8.2, 8.3, 8.4] - laravel: [11.*, 12.*] + os: [ubuntu-latest, windows-latest] + php: [8.3, 8.4, 8.5] + laravel: [12.*, 13.*] + stability: [prefer-lowest, prefer-stable] include: - - laravel: 11.* - testbench: 9.* - laravel: 12.* testbench: 10.* + - laravel: 13.* + testbench: 11.* - name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} + name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # v2 with: php-version: ${{ matrix.php }} - extensions: dom, libxml, simplexml + extensions: dom, curl, libxml, mbstring, zip, fileinfo, sqlite, pdo_sqlite, bcmath, intl + # setup-php seeds php.ini from php.ini-production, where + # variables_order="GPCS" leaves $_ENV empty; the type coverage step + # needs it populated, as in a normal local install. + ini-values: error_reporting=E_ALL, variables_order=EGPCS + tools: composer:v2 coverage: none - name: Install dependencies run: | - composer require "illuminate/support:${{ matrix.laravel }}" "illuminate/http:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --dev --no-interaction --no-update - composer update --prefer-dist --no-interaction + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update + composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress --no-scripts + composer run prepare + + - name: PHPStan + if: runner.os != 'Windows' + run: composer analyse + + # Larastan boots Laravel in every PHPStan worker, and each boot rewrites + # the Testbench skeleton's bootstrap/cache/services.php. Concurrent + # rename() onto that file fails on Windows, so this lane analyses in a + # single process. See .github/phpstan-windows.neon. + - name: PHPStan (Windows) + if: runner.os == 'Windows' + run: vendor/bin/phpstan analyse --configuration=.github/phpstan-windows.neon + + - name: Linter + run: composer lint:check + + # pest-plugin-type-coverage 4.x workers share one cache file without a + # blocking lock; __PEST_PLUGIN_ENV keeps the analysis in one process. + - name: Type Coverage + if: runner.os != 'Windows' + env: + __PEST_PLUGIN_ENV: '1' + run: composer test:types + + - name: Test Suite + if: runner.os != 'Windows' + run: composer test:unit - - name: Run tests + - name: Test Suite (Windows) + if: runner.os == 'Windows' run: vendor/bin/pest diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml new file mode 100644 index 0000000..9ba1e4e --- /dev/null +++ b/.github/workflows/update-changelog.yml @@ -0,0 +1,32 @@ +name: Update Changelog + +on: + release: + types: [released] + +permissions: + contents: write + +jobs: + update: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: main + + - name: Update changelog + uses: stefanzweifel/changelog-updater-action@a938690fad7edf25368f37e43a1ed1b34303eb36 # v1 + with: + latest-version: ${{ github.event.release.name }} + release-notes: ${{ github.event.release.body }} + + - name: Commit updated changelog + uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7 + with: + branch: main + commit_message: Update CHANGELOG + file_pattern: CHANGELOG.md diff --git a/.gitignore b/.gitignore index 3d0a5b5..d0ee595 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,11 @@ -/vendor/ -/.idea/ +/vendor +/build +/.phpunit.cache +/.phpunit.result.cache +/coverage composer.lock +.phpunit.cache .phpunit.result.cache +.DS_Store +.idea +.vscode diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1bad700 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,38 @@ +# SimplePay for Laravel + +This repository is a Laravel package. Keep the package focused, idiomatic, and easy for Laravel developers to install, test, and maintain. + +## Package Conventions + +- Use Laravel-native package APIs and the existing service provider shape before adding abstractions. +- Keep package names, namespaces, Composer metadata, publish tags, documentation, and examples aligned with `netipar/simplepay`. +- Add only the files and dependencies needed for the package behavior being implemented. +- Prefer explicit Laravel package code over helper abstractions unless the extension point is real. +- Keep tests focused on observable package behavior through public APIs, service provider wiring, commands, routes, published resources, and documentation promises. + +## Quick Commands + +- Full validation: `composer test` +- Formatting check: `composer lint:check` +- Static analysis: `composer analyse` +- Pest tests: `composer test:unit` +- Workbench build: `composer build` +- Workbench server: `composer serve` + +## Local Skills + +- `package-scaffold`: use when adding package capabilities or wiring them through the service provider, including commands, migrations, routes, config, views, translations, assets, middleware, publish tags, workbench files, and console-only behavior. +- `package-testing`: use when adding or changing package tests with Pest 4/5 and Orchestra Testbench. +- `package-release`: use when preparing changelog, release notes, tags, or GitHub release workflow changes. +- `package-compatibility`: use when reviewing code, dependencies, or CI against the PHP and Laravel support matrix. +- `package-generate-skill`: use when updating the bundled Boost skill from the package implementation, README, and examples. + +## SimplePay Architecture + +- The package follows the official SimplePay PHP SDKs (API v2, published at https://simplepay.hu/fejlesztoknek/). `official-docs/` holds the reference PDFs; it is not part of the dist archive. +- `src/SimplePay.php` sits behind the `SimplePay` facade and exposes the services in `src/Services`: `PaymentService` (start, finish, refund, query, cancel, qvik/EAM, Apple Pay), `CardStorageService` (OneClick `do`, recurring, card and token query/cancel), `AutoPaymentService` (merchant-side card data) and `RtpService` (Request to Pay). +- Request and response payloads are readonly DTOs in `src/Dto`: `toArray()` builds the API body including `salt` and `sdkVersion`, `fromArray()` maps the response. Enums live in `src/Enums`; `ErrorCode` mirrors the official error code list with Hungarian descriptions. +- `src/Support/Client.php` signs every request with HMAC-SHA384 (`Support/Signature`), verifies the response signature and throws `SimplePayApiException` when `errorCodes` is not empty; the exception keeps the response body (`getResponseData()`), e.g. the `redirectUrl` of a 3DS challenge (3004). +- Merchant credentials are configured per currency (`simplepay.merchants`) and resolved by `Support/MerchantResolver`; IPN bodies without `currency` are resolved by their `merchant` field. +- Routes: `POST simplepay/ipn` (`VerifySimplePaySignature` middleware; `SimplePayWebhookController` dispatches the events in `src/Events`) and `GET simplepay/back` (`SimplePayBackController`, response customisable through the `BackUrlResponse` contract). +- Consumer documentation is bilingual: keep `examples/en/` and `examples/hu/` in sync with each other and with the README. diff --git a/CHANGELOG.md b/CHANGELOG.md index f1e4afa..04ba82f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ -# Changelog +# Release Notes -All notable changes to `netipar/simplepay` will be documented in this file. +## [Unreleased](https://github.com/NETipar/simplepay/compare/v1.2.0...main) ## v1.2.0 - 2026-04-28 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index ddd0a16..1ff8791 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ # SimplePay for Laravel [![Latest Version on Packagist](https://img.shields.io/packagist/v/netipar/simplepay.svg?style=flat-square)](https://packagist.org/packages/netipar/simplepay) -[![Tests](https://img.shields.io/github/actions/workflow/status/NETipar/simplepay/tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/NETipar/simplepay/actions?query=workflow%3ATests+branch%3Amain) +[![Tests](https://img.shields.io/github/actions/workflow/status/NETipar/simplepay/tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/NETipar/simplepay/actions/workflows/tests.yml?query=branch%3Amain) +[![PHP Version](https://img.shields.io/packagist/php-v/netipar/simplepay.svg?style=flat-square)](https://packagist.org/packages/netipar/simplepay) [![Total Downloads](https://img.shields.io/packagist/dt/netipar/simplepay.svg?style=flat-square)](https://packagist.org/packages/netipar/simplepay) Modern Laravel package for [SimplePay](https://simplepay.hu) online payment integration. Start payments, handle IPN webhooks, manage card storage, process refunds, and more. @@ -45,8 +46,8 @@ return redirect($response->paymentUrl); ## Requirements -- PHP 8.2+ -- Laravel 11 or 12 +- PHP 8.3+ +- Laravel 12 or 13 - A [SimplePay](https://simplepay.hu) merchant account ## Installation @@ -61,6 +62,12 @@ Publish the config file: php artisan vendor:publish --tag=simplepay-config ``` +Or publish the config file and the logo assets at once: + +```bash +php artisan vendor:publish --tag=simplepay +``` + Add your merchant credentials to `.env`: ``` @@ -385,6 +392,23 @@ try { $e->getErrorCodes(); // [5083] $e->hasErrorCode(5083); // true $e->getResolvedCodes(); // [ErrorCode::TokenTimesRequired] + $e->getResponseData(); // The full API response body +} +``` + +When a card storage or auto payment requires a 3DS challenge, the API answers with error code `3004`, and the response body holds the `redirectUrl` where the customer has to be sent: + +```php +use Netipar\SimplePay\Enums\ErrorCode; + +try { + $result = SimplePay::cardStorage()->do($request); +} catch (SimplePayApiException $e) { + if ($e->hasErrorCode(ErrorCode::ThreeDsChallengeRedirect->value)) { + return redirect($e->getResponseData()['redirectUrl']); + } + + throw $e; } ``` @@ -468,9 +492,24 @@ This package is built based on the **SimplePay PHP SDK v2.1** (API v2). All API ## Testing ```bash -composer test +composer test # PHPStan, Pint check, type coverage and the Pest suite +composer test:unit # Pest suite only +composer analyse # PHPStan (Larastan, level 7) +composer lint # Fix code style with Pint ``` +## Changelog + +Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. + +## Contributing + +Please review the [contributing guide](.github/CONTRIBUTING.md) before opening a pull request. + +## Security Vulnerabilities + +Please review the [security policy](.github/SECURITY.md) on how to report security vulnerabilities. + ## Credits - [NETipar](https://netipar.hu) diff --git a/composer.json b/composer.json index f1effd6..bb9bb34 100644 --- a/composer.json +++ b/composer.json @@ -1,17 +1,43 @@ { + "$schema": "https://getcomposer.org/schema.json", "name": "netipar/simplepay", "description": "Modern Laravel package for SimplePay online payment integration", - "keywords": ["laravel", "simplepay", "payment", "hungary"], + "keywords": [ + "laravel", + "netipar", + "simplepay", + "payment", + "hungary" + ], + "homepage": "https://github.com/NETipar/simplepay", "license": "MIT", + "support": { + "issues": "https://github.com/NETipar/simplepay/issues", + "source": "https://github.com/NETipar/simplepay", + "security": "https://github.com/NETipar/simplepay/security/policy" + }, + "authors": [ + { + "name": "NETipar", + "email": "dev@netipar.hu", + "homepage": "https://netipar.hu", + "role": "Developer" + } + ], "require": { - "php": "^8.2", - "illuminate/support": "^11.0|^12.0", - "illuminate/http": "^11.0|^12.0" + "php": "^8.3", + "illuminate/http": "^12.0||^13.0", + "illuminate/support": "^12.0||^13.0" }, "require-dev": { - "laravel/pint": "^1.18", - "orchestra/testbench": "^9.0|^10.0", - "pestphp/pest": "^3.0" + "larastan/larastan": "^3.10", + "laravel/pao": "^1.0", + "laravel/pint": "^1.29", + "orchestra/testbench": "^10.0||^11.0", + "pestphp/pest": "^4.6||^5.0", + "pestphp/pest-plugin-laravel": "^4.1||^5.0", + "pestphp/pest-plugin-type-coverage": "^4.0||^5.0", + "phpstan/extension-installer": "^1.4" }, "autoload": { "psr-4": { @@ -20,9 +46,53 @@ }, "autoload-dev": { "psr-4": { - "Netipar\\SimplePay\\Tests\\": "tests/" + "Netipar\\SimplePay\\Tests\\": "tests/", + "Workbench\\App\\": "workbench/app/", + "Workbench\\Database\\Factories\\": "workbench/database/factories/", + "Workbench\\Database\\Seeders\\": "workbench/database/seeders/" } }, + "scripts": { + "post-autoload-dump": [ + "@clear", + "@prepare" + ], + "prepare": [ + "@php vendor/bin/testbench package:discover --ansi" + ], + "build": [ + "@php vendor/bin/testbench workbench:build --ansi" + ], + "serve": [ + "Composer\\Config::disableProcessTimeout", + "@build", + "@php vendor/bin/testbench serve --ansi" + ], + "lint": [ + "vendor/bin/pint --parallel" + ], + "lint:check": [ + "vendor/bin/pint --parallel --test" + ], + "analyse": [ + "vendor/bin/phpstan analyse" + ], + "test:types": [ + "vendor/bin/pest --type-coverage --min=100" + ], + "test:unit": [ + "vendor/bin/pest --parallel" + ], + "test": [ + "@analyse", + "@lint:check", + "@test:types", + "@test:unit" + ], + "clear": [ + "@php vendor/bin/testbench package:purge-skeleton --ansi" + ] + }, "extra": { "laravel": { "providers": [ @@ -33,17 +103,13 @@ } } }, - "scripts": { - "test": "vendor/bin/pest", - "test-coverage": "vendor/bin/pest --coverage", - "format": "vendor/bin/pint" - }, "config": { "sort-packages": true, "allow-plugins": { - "pestphp/pest-plugin": true + "pestphp/pest-plugin": true, + "phpstan/extension-installer": true } }, - "minimum-stability": "dev", + "minimum-stability": "stable", "prefer-stable": true } diff --git a/config/simplepay.php b/config/simplepay.php index b93d1ec..317ca87 100644 --- a/config/simplepay.php +++ b/config/simplepay.php @@ -1,5 +1,7 @@ - - - - tests/Unit - - - tests/Feature - - - - - src - - - diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..e2ecb23 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,34 @@ + + + + + tests/ArchTest.php + + + tests/Feature + + + tests/Unit + + + + + src + + + diff --git a/pint.json b/pint.json index 93061b6..1099652 100644 --- a/pint.json +++ b/pint.json @@ -1,3 +1,36 @@ { - "preset": "laravel" + "preset": "laravel", + "rules": { + "declare_strict_types": true, + "no_extra_blank_lines": { + "tokens": [ + "curly_brace_block", + "extra", + "throw", + "use" + ] + }, + "blank_line_before_statement": { + "statements": [ + "break", + "continue", + "declare", + "if", + "return", + "throw", + "try" + ] + }, + "global_namespace_import": { + "import_classes": true + }, + "trailing_comma_in_multiline": { + "elements": [ + "arguments", + "arrays", + "match", + "parameters" + ] + } + } } diff --git a/resources/assets/logos/Apple_Pay_Mark_RGB.png b/public/logos/Apple_Pay_Mark_RGB.png similarity index 100% rename from resources/assets/logos/Apple_Pay_Mark_RGB.png rename to public/logos/Apple_Pay_Mark_RGB.png diff --git a/resources/assets/logos/GPay_logo_color_new.png b/public/logos/GPay_logo_color_new.png similarity index 100% rename from resources/assets/logos/GPay_logo_color_new.png rename to public/logos/GPay_logo_color_new.png diff --git a/resources/assets/logos/OTPszepkartya_logo_color.png b/public/logos/OTPszepkartya_logo_color.png similarity index 100% rename from resources/assets/logos/OTPszepkartya_logo_color.png rename to public/logos/OTPszepkartya_logo_color.png diff --git a/resources/assets/logos/amex_logo_bw.png b/public/logos/amex_logo_bw.png similarity index 100% rename from resources/assets/logos/amex_logo_bw.png rename to public/logos/amex_logo_bw.png diff --git a/resources/assets/logos/amex_logo_color.png b/public/logos/amex_logo_color.png similarity index 100% rename from resources/assets/logos/amex_logo_color.png rename to public/logos/amex_logo_color.png diff --git a/resources/assets/logos/maestro_logo_bw_new.png b/public/logos/maestro_logo_bw_new.png similarity index 100% rename from resources/assets/logos/maestro_logo_bw_new.png rename to public/logos/maestro_logo_bw_new.png diff --git a/resources/assets/logos/maestro_logo_color_new.png b/public/logos/maestro_logo_color_new.png similarity index 100% rename from resources/assets/logos/maestro_logo_color_new.png rename to public/logos/maestro_logo_color_new.png diff --git a/resources/assets/logos/mastercard_logo_bw_new.png b/public/logos/mastercard_logo_bw_new.png similarity index 100% rename from resources/assets/logos/mastercard_logo_bw_new.png rename to public/logos/mastercard_logo_bw_new.png diff --git a/resources/assets/logos/mastercard_logo_color_new.png b/public/logos/mastercard_logo_color_new.png similarity index 100% rename from resources/assets/logos/mastercard_logo_color_new.png rename to public/logos/mastercard_logo_color_new.png diff --git a/resources/assets/logos/qvik_logo_color.png b/public/logos/qvik_logo_color.png similarity index 100% rename from resources/assets/logos/qvik_logo_color.png rename to public/logos/qvik_logo_color.png diff --git a/resources/assets/logos/simplepay_200x50.png b/public/logos/simplepay_200x50.png similarity index 100% rename from resources/assets/logos/simplepay_200x50.png rename to public/logos/simplepay_200x50.png diff --git a/resources/assets/logos/simplepay_horizontal_01-white.png b/public/logos/simplepay_horizontal_01-white.png similarity index 100% rename from resources/assets/logos/simplepay_horizontal_01-white.png rename to public/logos/simplepay_horizontal_01-white.png diff --git a/resources/assets/logos/simplepay_horizontal_01.png b/public/logos/simplepay_horizontal_01.png similarity index 100% rename from resources/assets/logos/simplepay_horizontal_01.png rename to public/logos/simplepay_horizontal_01.png diff --git a/resources/assets/logos/simplepay_horizontal_02-white.png b/public/logos/simplepay_horizontal_02-white.png similarity index 100% rename from resources/assets/logos/simplepay_horizontal_02-white.png rename to public/logos/simplepay_horizontal_02-white.png diff --git a/resources/assets/logos/simplepay_horizontal_02.png b/public/logos/simplepay_horizontal_02.png similarity index 100% rename from resources/assets/logos/simplepay_horizontal_02.png rename to public/logos/simplepay_horizontal_02.png diff --git a/resources/assets/logos/simplepay_top-white.png b/public/logos/simplepay_top-white.png similarity index 100% rename from resources/assets/logos/simplepay_top-white.png rename to public/logos/simplepay_top-white.png diff --git a/resources/assets/logos/simplepay_top.png b/public/logos/simplepay_top.png similarity index 100% rename from resources/assets/logos/simplepay_top.png rename to public/logos/simplepay_top.png diff --git a/resources/assets/logos/simplepay_vertical-wite.png b/public/logos/simplepay_vertical-wite.png similarity index 100% rename from resources/assets/logos/simplepay_vertical-wite.png rename to public/logos/simplepay_vertical-wite.png diff --git a/resources/assets/logos/simplepay_vertical.png b/public/logos/simplepay_vertical.png similarity index 100% rename from resources/assets/logos/simplepay_vertical.png rename to public/logos/simplepay_vertical.png diff --git a/resources/assets/logos/simplepay_w140-white.png b/public/logos/simplepay_w140-white.png similarity index 100% rename from resources/assets/logos/simplepay_w140-white.png rename to public/logos/simplepay_w140-white.png diff --git a/resources/assets/logos/simplepay_w140.png b/public/logos/simplepay_w140.png similarity index 100% rename from resources/assets/logos/simplepay_w140.png rename to public/logos/simplepay_w140.png diff --git a/resources/assets/logos/simplepay_w240-white.png b/public/logos/simplepay_w240-white.png similarity index 100% rename from resources/assets/logos/simplepay_w240-white.png rename to public/logos/simplepay_w240-white.png diff --git a/resources/assets/logos/simplepay_w240.png b/public/logos/simplepay_w240.png similarity index 100% rename from resources/assets/logos/simplepay_w240.png rename to public/logos/simplepay_w240.png diff --git a/resources/assets/logos/simplepay_w360-white.png b/public/logos/simplepay_w360-white.png similarity index 100% rename from resources/assets/logos/simplepay_w360-white.png rename to public/logos/simplepay_w360-white.png diff --git a/resources/assets/logos/simplepay_w360.png b/public/logos/simplepay_w360.png similarity index 100% rename from resources/assets/logos/simplepay_w360.png rename to public/logos/simplepay_w360.png diff --git a/resources/assets/logos/visa_logo_bw.png b/public/logos/visa_logo_bw.png similarity index 100% rename from resources/assets/logos/visa_logo_bw.png rename to public/logos/visa_logo_bw.png diff --git a/resources/assets/logos/visa_logo_color.png b/public/logos/visa_logo_color.png similarity index 100% rename from resources/assets/logos/visa_logo_color.png rename to public/logos/visa_logo_color.png diff --git a/resources/assets/variaciok/SimplePay_Logo+MC-Maestro-Visa/simplepay_bankcard_logos_left.png b/public/variaciok/SimplePay_Logo+MC-Maestro-Visa/simplepay_bankcard_logos_left.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo+MC-Maestro-Visa/simplepay_bankcard_logos_left.png rename to public/variaciok/SimplePay_Logo+MC-Maestro-Visa/simplepay_bankcard_logos_left.png diff --git a/resources/assets/variaciok/SimplePay_Logo+MC-Maestro-Visa/simplepay_bankcard_logos_top_horizontal.png b/public/variaciok/SimplePay_Logo+MC-Maestro-Visa/simplepay_bankcard_logos_top_horizontal.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo+MC-Maestro-Visa/simplepay_bankcard_logos_top_horizontal.png rename to public/variaciok/SimplePay_Logo+MC-Maestro-Visa/simplepay_bankcard_logos_top_horizontal.png diff --git a/resources/assets/variaciok/SimplePay_Logo+Qvik/bankcards_logos_new.png b/public/variaciok/SimplePay_Logo+Qvik/bankcards_logos_new.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo+Qvik/bankcards_logos_new.png rename to public/variaciok/SimplePay_Logo+Qvik/bankcards_logos_new.png diff --git a/resources/assets/variaciok/SimplePay_Logo+Qvik/simplepay_bankcard_logos_left_new.png b/public/variaciok/SimplePay_Logo+Qvik/simplepay_bankcard_logos_left_new.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo+Qvik/simplepay_bankcard_logos_left_new.png rename to public/variaciok/SimplePay_Logo+Qvik/simplepay_bankcard_logos_left_new.png diff --git a/resources/assets/variaciok/SimplePay_Logo+Qvik/simplepay_bankcard_logos_vertical.png b/public/variaciok/SimplePay_Logo+Qvik/simplepay_bankcard_logos_vertical.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo+Qvik/simplepay_bankcard_logos_vertical.png rename to public/variaciok/SimplePay_Logo+Qvik/simplepay_bankcard_logos_vertical.png diff --git a/resources/assets/variaciok/SimplePay_Logo+SZEP/simplepay_bankcard_logos_left_new.png b/public/variaciok/SimplePay_Logo+SZEP/simplepay_bankcard_logos_left_new.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo+SZEP/simplepay_bankcard_logos_left_new.png rename to public/variaciok/SimplePay_Logo+SZEP/simplepay_bankcard_logos_left_new.png diff --git a/resources/assets/variaciok/SimplePay_Logo+SZEP/simplepay_bankcard_logos_vertical.png b/public/variaciok/SimplePay_Logo+SZEP/simplepay_bankcard_logos_vertical.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo+SZEP/simplepay_bankcard_logos_vertical.png rename to public/variaciok/SimplePay_Logo+SZEP/simplepay_bankcard_logos_vertical.png diff --git a/resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/bankcards_logos_new.png b/public/variaciok/SimplePay_Logo-kartyak_with_Amex/bankcards_logos_new.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/bankcards_logos_new.png rename to public/variaciok/SimplePay_Logo-kartyak_with_Amex/bankcards_logos_new.png diff --git a/resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_left-hu.png b/public/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_left-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_left-hu.png rename to public/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_left-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_left_new-hu.png b/public/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_left_new-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_left_new-hu.png rename to public/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_left_new-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_top_horizontal-hu.png b/public/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_top_horizontal-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_top_horizontal-hu.png rename to public/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_top_horizontal-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_vertical-hu.png b/public/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_vertical-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_vertical-hu.png rename to public/variaciok/SimplePay_Logo-kartyak_with_Amex/simplepay_bankcard_logos_vertical-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/bankcards_logos_new.png b/public/variaciok/SimplePay_Logo_kartyak_wo_Amex/bankcards_logos_new.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/bankcards_logos_new.png rename to public/variaciok/SimplePay_Logo_kartyak_wo_Amex/bankcards_logos_new.png diff --git a/resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_left-hu.png b/public/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_left-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_left-hu.png rename to public/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_left-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_left_new-hu.png b/public/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_left_new-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_left_new-hu.png rename to public/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_left_new-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_top_horizontal-hu.png b/public/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_top_horizontal-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_top_horizontal-hu.png rename to public/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_top_horizontal-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_top_vertical-hu.png b/public/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_top_vertical-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_top_vertical-hu.png rename to public/variaciok/SimplePay_Logo_kartyak_wo_Amex/simplepay_bankcard_logos_top_vertical-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/bankcards_logos_new.png b/public/variaciok/SimplePay_Logo_with_Amex+Qvik/bankcards_logos_new.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/bankcards_logos_new.png rename to public/variaciok/SimplePay_Logo_with_Amex+Qvik/bankcards_logos_new.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_left-hu.png b/public/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_left-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_left-hu.png rename to public/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_left-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_left_new-hu.png b/public/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_left_new-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_left_new-hu.png rename to public/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_left_new-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_top_horizontal-hu.png b/public/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_top_horizontal-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_top_horizontal-hu.png rename to public/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_top_horizontal-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_top_vertical-hu.png b/public/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_top_vertical-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_top_vertical-hu.png rename to public/variaciok/SimplePay_Logo_with_Amex+Qvik/simplepay_bankcard_logos_top_vertical-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex/bankcards_logos_new.png b/public/variaciok/SimplePay_Logo_with_Amex/bankcards_logos_new.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex/bankcards_logos_new.png rename to public/variaciok/SimplePay_Logo_with_Amex/bankcards_logos_new.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_left-hu.png b/public/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_left-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_left-hu.png rename to public/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_left-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_left_new-hu.png b/public/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_left_new-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_left_new-hu.png rename to public/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_left_new-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_top_horizontal-hu.png b/public/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_top_horizontal-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_top_horizontal-hu.png rename to public/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_top_horizontal-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_vertical-hu.png b/public/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_vertical-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_vertical-hu.png rename to public/variaciok/SimplePay_Logo_with_Amex/simplepay_bankcard_logos_vertical-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/bankcards_logos_new.png b/public/variaciok/SimplePay_Logo_wo_Amex+Qvik/bankcards_logos_new.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/bankcards_logos_new.png rename to public/variaciok/SimplePay_Logo_wo_Amex+Qvik/bankcards_logos_new.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_left-hu.png b/public/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_left-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_left-hu.png rename to public/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_left-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_left_new-hu.png b/public/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_left_new-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_left_new-hu.png rename to public/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_left_new-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_top_horizontal-hu.png b/public/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_top_horizontal-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_top_horizontal-hu.png rename to public/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_top_horizontal-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_vertical-hu.png b/public/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_vertical-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_vertical-hu.png rename to public/variaciok/SimplePay_Logo_wo_Amex+Qvik/simplepay_bankcard_logos_vertical-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/bankcards_logos_new.png b/public/variaciok/SimplePay_Logo_wo_Amex/bankcards_logos_new.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/bankcards_logos_new.png rename to public/variaciok/SimplePay_Logo_wo_Amex/bankcards_logos_new.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_left-hu.png b/public/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_left-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_left-hu.png rename to public/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_left-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_left_new-hu.png b/public/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_left_new-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_left_new-hu.png rename to public/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_left_new-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_top_horizontal-hu.png b/public/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_top_horizontal-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_top_horizontal-hu.png rename to public/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_top_horizontal-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_vertical-hu.png b/public/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_vertical-hu.png similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_vertical-hu.png rename to public/variaciok/SimplePay_Logo_wo_Amex/simplepay_bankcard_logos_vertical-hu.png diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/bankcards_logos_new.psd b/public/variaciok/SimplePay_Logo_wo_Amex/szerk/bankcards_logos_new.psd similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/bankcards_logos_new.psd rename to public/variaciok/SimplePay_Logo_wo_Amex/szerk/bankcards_logos_new.psd diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_left-hu.ai b/public/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_left-hu.ai similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_left-hu.ai rename to public/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_left-hu.ai diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_left_new-hu.ai b/public/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_left_new-hu.ai similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_left_new-hu.ai rename to public/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_left_new-hu.ai diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_top_horizontal-hu.ai b/public/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_top_horizontal-hu.ai similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_top_horizontal-hu.ai rename to public/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_top_horizontal-hu.ai diff --git a/resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_vertical-hu.ai b/public/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_vertical-hu.ai similarity index 100% rename from resources/assets/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_vertical-hu.ai rename to public/variaciok/SimplePay_Logo_wo_Amex/szerk/simplepay_bankcard_logos_vertical-hu.ai diff --git a/resources/boost/skills/simplepay-development/SKILL.md b/resources/boost/skills/simplepay-development/SKILL.md index a79a687..ea3bf43 100644 --- a/resources/boost/skills/simplepay-development/SKILL.md +++ b/resources/boost/skills/simplepay-development/SKILL.md @@ -1,6 +1,9 @@ --- name: simplepay-development description: Build and integrate SimplePay online payment features using the netipar/simplepay Laravel package, including payment start, IPN webhook handling, back redirects, refunds, two-step payments, card storage (OneClick), recurring payments, auto payments, and Request to Pay (RTP) flows. +license: MIT +metadata: + author: NETipar --- # SimplePay Payment Integration Development diff --git a/routes/simplepay.php b/routes/simplepay.php index 30308f2..736f1b3 100644 --- a/routes/simplepay.php +++ b/routes/simplepay.php @@ -1,5 +1,7 @@ + */ public function toArray(): array { return array_filter([ @@ -28,6 +33,6 @@ public function toArray(): array 'address' => $this->address, 'address2' => $this->address2, 'phone' => $this->phone, - ], fn ($value) => $value !== null); + ], fn (mixed $value): bool => $value !== null); } } diff --git a/src/Dto/ApplePayDoRequest.php b/src/Dto/ApplePayDoRequest.php index a19af2e..6cc8e61 100644 --- a/src/Dto/ApplePayDoRequest.php +++ b/src/Dto/ApplePayDoRequest.php @@ -1,5 +1,7 @@ + */ public function toArray(): array { return [ diff --git a/src/Dto/ApplePayStartRequest.php b/src/Dto/ApplePayStartRequest.php index 3f0eb7f..defaec9 100644 --- a/src/Dto/ApplePayStartRequest.php +++ b/src/Dto/ApplePayStartRequest.php @@ -1,5 +1,7 @@ + */ public function toArray(): array { return [ diff --git a/src/Dto/AutoPaymentRequest.php b/src/Dto/AutoPaymentRequest.php index 8b60506..96cd241 100644 --- a/src/Dto/AutoPaymentRequest.php +++ b/src/Dto/AutoPaymentRequest.php @@ -1,5 +1,7 @@ + */ public function toArray(): array { return array_filter([ @@ -41,6 +46,6 @@ public function toArray(): array 'delivery' => $this->delivery?->toArray(), 'items' => $this->items ? array_map(fn (Item $item) => $item->toArray(), $this->items) : null, 'sdkVersion' => 'netipar-simplepay:laravel', - ], fn ($value) => $value !== null); + ], fn (mixed $value): bool => $value !== null); } } diff --git a/src/Dto/BackResponse.php b/src/Dto/BackResponse.php index af80854..0c69632 100644 --- a/src/Dto/BackResponse.php +++ b/src/Dto/BackResponse.php @@ -1,5 +1,7 @@ + */ public function toArray(): array { return array_filter([ @@ -30,6 +35,6 @@ public function toArray(): array 'timeZone' => $this->timeZone, 'windowSize' => $this->windowSize, 'challengePreference' => $this->challengePreference, - ], fn ($value) => $value !== null); + ], fn (mixed $value): bool => $value !== null); } } diff --git a/src/Dto/CardData.php b/src/Dto/CardData.php index ed03473..26e206c 100644 --- a/src/Dto/CardData.php +++ b/src/Dto/CardData.php @@ -1,5 +1,7 @@ + */ public function toArray(): array { return array_filter([ @@ -18,6 +23,6 @@ public function toArray(): array 'expiry' => $this->expiry, 'cvc' => $this->cvc, 'holder' => $this->cardHolder, - ], fn ($value) => $value !== null); + ], fn (mixed $value): bool => $value !== null); } } diff --git a/src/Dto/CardStorageDoRequest.php b/src/Dto/CardStorageDoRequest.php index 5e5945d..3e2934a 100644 --- a/src/Dto/CardStorageDoRequest.php +++ b/src/Dto/CardStorageDoRequest.php @@ -1,5 +1,7 @@ + * * @throws RandomException */ public function toArray(): array @@ -37,6 +41,6 @@ public function toArray(): array 'customerEmail' => $this->customerEmail, 'url' => $this->url, 'items' => $this->items ? array_map(fn (Item $item) => $item->toArray(), $this->items) : null, - ], fn ($value) => $value !== null); + ], fn (mixed $value): bool => $value !== null); } } diff --git a/src/Dto/EamStartRequest.php b/src/Dto/EamStartRequest.php index 4b91c7d..cbca652 100644 --- a/src/Dto/EamStartRequest.php +++ b/src/Dto/EamStartRequest.php @@ -1,5 +1,7 @@ + * * @throws RandomException */ public function toArray(): array @@ -33,6 +37,6 @@ public function toArray(): array 'customerEmail' => $this->customerEmail, 'url' => $this->url, 'items' => $this->items ? array_map(fn (Item $item) => $item->toArray(), $this->items) : null, - ], fn ($value) => $value !== null); + ], fn (mixed $value): bool => $value !== null); } } diff --git a/src/Dto/FinishRequest.php b/src/Dto/FinishRequest.php index add140a..be64627 100644 --- a/src/Dto/FinishRequest.php +++ b/src/Dto/FinishRequest.php @@ -1,5 +1,7 @@ + * * @throws RandomException */ public function toArray(): array diff --git a/src/Dto/FinishResponse.php b/src/Dto/FinishResponse.php index 8c8286c..e41c69f 100644 --- a/src/Dto/FinishResponse.php +++ b/src/Dto/FinishResponse.php @@ -1,5 +1,7 @@ $data + */ public static function fromArray(array $data): self { return new self( diff --git a/src/Dto/IpnMessage.php b/src/Dto/IpnMessage.php index 01fbfd2..df243a4 100644 --- a/src/Dto/IpnMessage.php +++ b/src/Dto/IpnMessage.php @@ -1,5 +1,7 @@ $data + */ public static function fromArray(array $data): self { return new self( diff --git a/src/Dto/Item.php b/src/Dto/Item.php index 76c85be..db73eda 100644 --- a/src/Dto/Item.php +++ b/src/Dto/Item.php @@ -1,5 +1,7 @@ + */ public function toArray(): array { return array_filter([ @@ -22,6 +27,6 @@ public function toArray(): array 'amount' => $this->quantity, 'price' => $this->price, 'tax' => $this->tax, - ], fn ($value) => $value !== null); + ], fn (mixed $value): bool => $value !== null); } } diff --git a/src/Dto/PaymentRequest.php b/src/Dto/PaymentRequest.php index d056265..e585229 100644 --- a/src/Dto/PaymentRequest.php +++ b/src/Dto/PaymentRequest.php @@ -1,5 +1,7 @@ $methods * @param array|null $items + * @param array|null $recurring */ public function __construct( public Currency $currency, @@ -36,6 +39,8 @@ public function __construct( ) {} /** + * @return array + * * @throws RandomException */ public function toArray(): array diff --git a/src/Dto/PaymentResponse.php b/src/Dto/PaymentResponse.php index 421f245..9c7f2cd 100644 --- a/src/Dto/PaymentResponse.php +++ b/src/Dto/PaymentResponse.php @@ -1,5 +1,7 @@ $data + */ public static function fromArray(array $data): self { return new self( - transactionId: $data['transactionId'], + transactionId: (int) $data['transactionId'], paymentUrl: $data['paymentUrl'], timeout: $data['timeout'], ); diff --git a/src/Dto/QueryResponse.php b/src/Dto/QueryResponse.php index ee488d9..bf5f677 100644 --- a/src/Dto/QueryResponse.php +++ b/src/Dto/QueryResponse.php @@ -1,5 +1,7 @@ $data + */ public static function fromArray(array $data): self { $transaction = $data['transactions'][0] ?? null; diff --git a/src/Dto/RecurringRequest.php b/src/Dto/RecurringRequest.php index 2034eef..81fce12 100644 --- a/src/Dto/RecurringRequest.php +++ b/src/Dto/RecurringRequest.php @@ -1,5 +1,7 @@ + * * @throws RandomException */ public function toArray(): array @@ -38,6 +42,6 @@ public function toArray(): array 'url' => $this->url, 'type' => $this->type->value, 'items' => $this->items ? array_map(fn (Item $item) => $item->toArray(), $this->items) : null, - ], fn ($value) => $value !== null); + ], fn (mixed $value): bool => $value !== null); } } diff --git a/src/Dto/RefundRequest.php b/src/Dto/RefundRequest.php index c921f4a..238f0ea 100644 --- a/src/Dto/RefundRequest.php +++ b/src/Dto/RefundRequest.php @@ -1,5 +1,7 @@ + * * @throws RandomException */ public function toArray(): array diff --git a/src/Dto/RefundResponse.php b/src/Dto/RefundResponse.php index ae1b4b0..1b28e63 100644 --- a/src/Dto/RefundResponse.php +++ b/src/Dto/RefundResponse.php @@ -1,5 +1,7 @@ $data + */ public static function fromArray(array $data): self { return new self( diff --git a/src/Dto/RtpDoRequest.php b/src/Dto/RtpDoRequest.php index 9cf8a5c..341fe70 100644 --- a/src/Dto/RtpDoRequest.php +++ b/src/Dto/RtpDoRequest.php @@ -1,5 +1,7 @@ + * * @throws RandomException */ public function toArray(): array diff --git a/src/Dto/RtpPayment.php b/src/Dto/RtpPayment.php index 1641f8e..9f86201 100644 --- a/src/Dto/RtpPayment.php +++ b/src/Dto/RtpPayment.php @@ -1,5 +1,7 @@ + */ public function toArray(): array { return [ diff --git a/src/Dto/RtpStartRequest.php b/src/Dto/RtpStartRequest.php index db5ddde..4fcc6f4 100644 --- a/src/Dto/RtpStartRequest.php +++ b/src/Dto/RtpStartRequest.php @@ -1,5 +1,7 @@ + * * @throws RandomException */ public function toArray(): array diff --git a/src/Enums/BackEvent.php b/src/Enums/BackEvent.php index 6e61fc1..e5658ee 100644 --- a/src/Enums/BackEvent.php +++ b/src/Enums/BackEvent.php @@ -1,5 +1,7 @@ */ private array $resolvedCodes; + /** @var array */ + private array $responseData; + /** * @param array $errorCodes + * @param array $responseData */ public function __construct(array $errorCodes, array $responseData = []) { $this->errorCodes = $errorCodes; + $this->responseData = $responseData; $this->resolvedCodes = array_filter( array_map(fn (int $code) => ErrorCode::tryFrom($code), $errorCodes), ); @@ -56,4 +63,14 @@ public function hasErrorCode(int $code): bool { return in_array($code, $this->errorCodes, true); } + + /** + * The full API response body, e.g. the `redirectUrl` of a 3DS challenge (error code 3004). + * + * @return array + */ + public function getResponseData(): array + { + return $this->responseData; + } } diff --git a/src/Facades/SimplePay.php b/src/Facades/SimplePay.php index 528320f..aa6da72 100644 --- a/src/Facades/SimplePay.php +++ b/src/Facades/SimplePay.php @@ -1,5 +1,7 @@ $data + */ private function resolveSecretKey(array $data): ?string { if (isset($data['currency'])) { diff --git a/src/Http/Responses/DefaultBackUrlResponse.php b/src/Http/Responses/DefaultBackUrlResponse.php index 3da3273..b5541da 100644 --- a/src/Http/Responses/DefaultBackUrlResponse.php +++ b/src/Http/Responses/DefaultBackUrlResponse.php @@ -1,5 +1,7 @@ + * * @throws ConnectionException */ public function auto(AutoPaymentRequest $request): array diff --git a/src/Services/CardStorageService.php b/src/Services/CardStorageService.php index cf75456..5b51c00 100644 --- a/src/Services/CardStorageService.php +++ b/src/Services/CardStorageService.php @@ -1,5 +1,7 @@ + * * @throws RandomException|ConnectionException */ public function do(CardStorageDoRequest $request): array @@ -32,6 +36,8 @@ public function do(CardStorageDoRequest $request): array } /** + * @return array + * * @throws RandomException|ConnectionException */ public function doRecurring(RecurringRequest $request): array @@ -46,6 +52,8 @@ public function doRecurring(RecurringRequest $request): array } /** + * @return array + * * @throws RandomException|ConnectionException */ public function cardQuery(Currency $currency, string $cardId, bool $history = false): array @@ -64,6 +72,8 @@ public function cardQuery(Currency $currency, string $cardId, bool $history = fa } /** + * @return array + * * @throws RandomException|ConnectionException */ public function cardCancel(Currency $currency, string $cardId): array @@ -81,6 +91,8 @@ public function cardCancel(Currency $currency, string $cardId): array } /** + * @return array + * * @throws RandomException|ConnectionException */ public function tokenQuery(Currency $currency, string $token): array @@ -98,6 +110,8 @@ public function tokenQuery(Currency $currency, string $token): array } /** + * @return array + * * @throws RandomException|ConnectionException */ public function tokenCancel(Currency $currency, string $token): array diff --git a/src/Services/PaymentService.php b/src/Services/PaymentService.php index 3815f9f..6e874b3 100644 --- a/src/Services/PaymentService.php +++ b/src/Services/PaymentService.php @@ -1,5 +1,7 @@ + * * @throws RandomException * @throws ConnectionException */ @@ -62,6 +66,8 @@ public function startEam(EamStartRequest $request): array } /** + * @return array + * * @throws ConnectionException */ public function startApplePay(ApplePayStartRequest $request): array @@ -76,6 +82,8 @@ public function startApplePay(ApplePayStartRequest $request): array } /** + * @return array + * * @throws ConnectionException */ public function doApplePay(ApplePayDoRequest $request): array @@ -174,6 +182,8 @@ public function query(Currency $currency, ?int $transactionId = null, ?string $o } /** + * @return array + * * @throws RandomException * @throws ConnectionException */ diff --git a/src/Services/RtpService.php b/src/Services/RtpService.php index 1b2cd02..2d0f369 100644 --- a/src/Services/RtpService.php +++ b/src/Services/RtpService.php @@ -1,5 +1,7 @@ + * * @throws RandomException * @throws ConnectionException */ @@ -47,6 +51,8 @@ public function do(RtpDoRequest $request): array } /** + * @return array + * * @throws RandomException|ConnectionException */ public function query(Currency $currency, ?string $transactionId = null, ?string $orderRef = null): array @@ -70,6 +76,8 @@ public function query(Currency $currency, ?string $transactionId = null, ?string } /** + * @return array + * * @throws RandomException|ConnectionException */ public function refund(Currency $currency, string $transactionId, float $amount): array @@ -87,6 +95,8 @@ public function refund(Currency $currency, string $transactionId, float $amount) } /** + * @return array + * * @throws RandomException|ConnectionException */ public function reverse(Currency $currency, string $transactionId): array diff --git a/src/SimplePay.php b/src/SimplePay.php index 946c935..2f94470 100644 --- a/src/SimplePay.php +++ b/src/SimplePay.php @@ -1,5 +1,7 @@ mergeConfigFrom(__DIR__.'/../config/simplepay.php', 'simplepay'); @@ -20,18 +25,23 @@ public function register(): void $this->app->singleton(BackUrlResponse::class, DefaultBackUrlResponse::class); } + /** + * Bootstrap any application services. + */ public function boot(): void { - if ($this->app->runningInConsole()) { - $this->publishes([ - __DIR__.'/../config/simplepay.php' => config_path('simplepay.php'), - ], 'simplepay-config'); - - $this->publishes([ - __DIR__.'/../resources/assets' => public_path('vendor/simplepay'), - ], 'simplepay-assets'); + $this->loadRoutesFrom(__DIR__.'/../routes/simplepay.php'); + + if (! $this->app->runningInConsole()) { + return; } - $this->loadRoutesFrom(__DIR__.'/../routes/simplepay.php'); + $this->publishes([ + __DIR__.'/../config/simplepay.php' => config_path('simplepay.php'), + ], ['simplepay', 'simplepay-config']); + + $this->publishes([ + __DIR__.'/../public' => public_path('vendor/simplepay'), + ], ['simplepay', 'simplepay-assets']); } } diff --git a/src/Support/Client.php b/src/Support/Client.php index 35c20e3..9ec0df8 100644 --- a/src/Support/Client.php +++ b/src/Support/Client.php @@ -1,5 +1,7 @@ $data + * @return array + * * @throws ConnectionException */ public function post(string $endpoint, array $data, string $merchantKey): array { $url = $this->buildUrl($endpoint); - $json = json_encode($data); + $json = json_encode($data, JSON_THROW_ON_ERROR); $signature = Signature::generate($merchantKey, $json); $this->log('SimplePay request', ['url' => $url, 'data' => $data]); @@ -36,12 +41,15 @@ public function post(string $endpoint, array $data, string $merchantKey): array } /** + * @param array $data + * @return array + * * @throws ConnectionException */ public function postSecurePay(string $endpoint, array $data, string $merchantKey): array { $url = $this->buildSecurePayUrl($endpoint); - $json = json_encode($data); + $json = json_encode($data, JSON_THROW_ON_ERROR); $signature = Signature::generate($merchantKey, $json); $this->log('SimplePay securepay request', ['url' => $url, 'data' => $data]); @@ -97,6 +105,9 @@ private function validateResponse(Response $response, string $merchantKey): void } } + /** + * @param array $context + */ private function log(string $message, array $context = []): void { $channel = config('simplepay.log_channel'); diff --git a/src/Support/MerchantResolver.php b/src/Support/MerchantResolver.php index ff314cb..9914acf 100644 --- a/src/Support/MerchantResolver.php +++ b/src/Support/MerchantResolver.php @@ -1,5 +1,7 @@ preset()->php(); + +arch()->preset()->security(); + +arch('it will not use dd(), ddd(), env(), or exit()') + ->expect(['dd', 'ddd', 'env', 'exit']) + ->each->not->toBeUsed(); + +arch('the package source declares strict types') + ->expect('Netipar\SimplePay') + ->toUseStrictTypes(); diff --git a/tests/Feature/ConfigTest.php b/tests/Feature/ConfigTest.php index 8c80e11..3e94aca 100644 --- a/tests/Feature/ConfigTest.php +++ b/tests/Feature/ConfigTest.php @@ -1,5 +1,7 @@ toBeInstanceOf(SimplePay::class) + ->and(app(SimplePay::class))->toBe(app(SimplePay::class)); +}); + +it('registers the IPN and back routes', function () { + expect(Route::has('simplepay.ipn'))->toBeTrue() + ->and(Route::has('simplepay.back'))->toBeTrue(); +}); + +it('publishes the config file under its own tag', function () { + $paths = ServiceProvider::pathsToPublish(SimplePayServiceProvider::class, 'simplepay-config'); + + expect(array_values($paths))->toBe([config_path('simplepay.php')]) + ->and(array_keys($paths)[0])->toBeFile(); +}); + +it('publishes the logo assets under its own tag', function () { + $paths = ServiceProvider::pathsToPublish(SimplePayServiceProvider::class, 'simplepay-assets'); + + expect(array_values($paths))->toBe([public_path('vendor/simplepay')]) + ->and(array_keys($paths)[0].'/logos/simplepay_200x50.png')->toBeFile(); +}); + +it('publishes every resource under the package tag', function () { + $paths = ServiceProvider::pathsToPublish(SimplePayServiceProvider::class, 'simplepay'); + + expect(array_values($paths))->toBe([ + config_path('simplepay.php'), + public_path('vendor/simplepay'), + ]); +}); diff --git a/tests/Pest.php b/tests/Pest.php index f114f14..28b3657 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,7 @@ in('Unit', 'Feature'); +uses(TestCase::class)->in(__DIR__); diff --git a/tests/TestCase.php b/tests/TestCase.php index 1fa3893..deb6d9f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,11 +1,13 @@ Http::response([ + 'errorCodes' => [3004], + 'transactionId' => 123456, + 'redirectUrl' => 'https://sandbox.simplepay.hu/3ds/challenge', + ]), + ]); + + $request = new CardStorageDoRequest( + currency: Currency::HUF, + transactionId: 123456, + cardId: 'card-abc-123', + orderRef: 'ORDER-001', + total: 1500, + customerEmail: 'test@example.com', + url: 'https://example.com/back', + ); + + expect(fn () => createCardStorageService()->do($request)) + ->toThrow(function (SimplePayApiException $exception) { + expect($exception->hasErrorCode(3004))->toBeTrue() + ->and($exception->getResponseData()) + ->toHaveKey('redirectUrl', 'https://sandbox.simplepay.hu/3ds/challenge'); + }); +}); + it('sends a do request with items', function () { Http::fake([ 'sandbox.simplepay.hu/payment/v2/do' => Http::response([ diff --git a/tests/Unit/MerchantResolverTest.php b/tests/Unit/MerchantResolverTest.php index bd0685b..e4d0665 100644 --- a/tests/Unit/MerchantResolverTest.php +++ b/tests/Unit/MerchantResolverTest.php @@ -1,5 +1,7 @@ '502272810', + 'paymentUrl' => 'https://sandbox.simplepay.hu/pay/rtp/pspHU/abc', + 'timeout' => '2026-10-17T12:00:00+02:00', + ]); + + expect($response->transactionId)->toBe(502272810); +}); diff --git a/tests/Unit/PaymentServiceTest.php b/tests/Unit/PaymentServiceTest.php index 1637a48..4a24f17 100644 --- a/tests/Unit/PaymentServiceTest.php +++ b/tests/Unit/PaymentServiceTest.php @@ -1,5 +1,7 @@ */ + use HasFactory, Notifiable; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + ]; + } +} diff --git a/workbench/app/Providers/WorkbenchServiceProvider.php b/workbench/app/Providers/WorkbenchServiceProvider.php new file mode 100644 index 0000000..d09c191 --- /dev/null +++ b/workbench/app/Providers/WorkbenchServiceProvider.php @@ -0,0 +1,26 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + ) + ->withMiddleware(function (Middleware $middleware): void { + // + }) + ->withExceptions(function (Exceptions $exceptions): void { + // + })->create(); diff --git a/workbench/database/factories/.gitkeep b/workbench/database/factories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workbench/database/factories/UserFactory.php b/workbench/database/factories/UserFactory.php new file mode 100644 index 0000000..6268042 --- /dev/null +++ b/workbench/database/factories/UserFactory.php @@ -0,0 +1,54 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * The name of the factory's corresponding model. + * + * @var class-string + */ + protected $model = User::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/workbench/database/migrations/.gitkeep b/workbench/database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workbench/database/seeders/DatabaseSeeder.php b/workbench/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..4105579 --- /dev/null +++ b/workbench/database/seeders/DatabaseSeeder.php @@ -0,0 +1,27 @@ +times(10)->create(); + + UserFactory::new()->create([ + 'name' => 'Test User', + 'email' => 'test@example.com', + ]); + } +} diff --git a/workbench/resources/views/.gitkeep b/workbench/resources/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workbench/routes/console.php b/workbench/routes/console.php new file mode 100644 index 0000000..7b00eec --- /dev/null +++ b/workbench/routes/console.php @@ -0,0 +1,10 @@ +comment(Inspiring::quote()); +// })->purpose('Display an inspiring quote'); diff --git a/workbench/routes/web.php b/workbench/routes/web.php new file mode 100644 index 0000000..7bceeaf --- /dev/null +++ b/workbench/routes/web.php @@ -0,0 +1,9 @@ +