Skip to content

fix(callbacks): confirm every payment method on a single endpoint - #98

Merged
jfrosorio merged 5 commits into
masterfrom
fix-94
Sep 26, 2026
Merged

jfrosorio merged 5 commits into
masterfrom
fix-94

Conversation

@jfrosorio

Copy link
Copy Markdown
Contributor

Description

Eupago's backoffice takes a single notification URL per channel and sends every payment method's Realtime 1.0 notification to it, but each per-method route only looked for the reference in its own table. A merchant taking more than one method only got confirmations for the method whose URL was set.

  • A single GET /eupago/callback endpoint picks the handler from mp (PC:PT, MW:PT, PS:PT, PF:PT, CC:PT, now a PaymentMethod enum).
  • The per-method routes are deprecated aliases of it, to be removed in v4. Each falls back to its own method when mp is unknown, so a URL already set in the backoffice confirms every method after the upgrade.
  • The callback routes no longer run the web middleware group, which started a session storing the URL, chave_api included.
  • The channel and API key are checked before any other rule, so a caller without them no longer learns which references exist.
  • Marking a reference as paid locks its row and fires the event after the commit, so simultaneous deliveries fire it once and queued listeners always find the payment stored. A redelivered notification gets a 200 without firing the event again.
  • Multibanco references with an amount range or repeat payments can now be confirmed. Each payment is recorded in a new mb_reference_payments table, backfilled for references paid since v3.9.0, and passed to MBReferencePaid as $payment.
  • The docs and UPGRADE.md cover the new endpoint, custom route mounts, the event timing and range payments.

Fixes

This fixes #94.

Copilot AI lite review requested due to automatic review settings September 26, 2026 11:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The shared confirmPayment() helper can return an incorrect 404 under concurrent duplicate deliveries due to an out-of-transaction idempotency check combined with a state = 0 locked lookup.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

This PR updates EuPago Realtime 1.0 webhook handling so merchants can configure a single notification URL per channel (as required by Eupago’s backoffice) while still confirming all payment methods, improving security (no web sessions; validate caller first) and reliability (idempotent acknowledgements; post-commit event dispatch).

Changes:

  • Add a single /eupago/callback endpoint that dispatches to the correct handler based on mp, and keep per-method endpoints as deprecated aliases.
  • Refactor callback processing to validate channel/API key first and to make confirmations idempotent (avoid re-firing events on redelivery).
  • Add Multibanco payment recording via a new mb_reference_payments table/model and extend MBReferencePaid to include the specific payment.
File Description
UPGRADE.md Documents the new unified callback endpoint, deprecations, and migration steps.
tests/​Pest.php Updates test helpers to use new mp codes and default MB ranges.
tests/​Feature/​RoutesDisabledTest.php Asserts unified callback route is not registered when routes are disabled.
tests/​Feature/​PackageBootTest.php Asserts unified callback route is registered by default.
tests/​Feature/​MbReferencePaymentsMigrationTest.php Adds coverage for the new MB payments table backfill behavior.
tests/​Feature/​MbCallbackTest.php Expands MB callback tests for idempotency, ranges, repeat payments, and post-commit events.
tests/​Feature/​CallbackTest.php Adds end-to-end tests for unified endpoint dispatch, validation ordering, aliases, and idempotency.
src/​Providers/​EuPagoServiceProvider.php Removes web middleware from callback route registration.
src/​Models/​MbReferencePayment.php Introduces a model for MB payments linked to references.
src/​Models/​MbReference.php Adds accepting() scope and payments() relationship for MB payments/ranges.
src/​Http/​Requests/​CallbackRequest.php Splits caller validation rules so channel/API key are checked first.
src/​Http/​Controllers/​PayShopController.php Refactors to shared confirmation logic.
src/​Http/​Controllers/​PaysafeCardController.php Refactors to shared confirmation logic.
src/​Http/​Controllers/​MBWayController.php Refactors to shared confirmation logic.
src/​Http/​Controllers/​MBController.php Implements MB-specific payment recording + idempotent event behavior.
src/​Http/​Controllers/​CreditCardController.php Refactors to shared confirmation logic while preserving identifier matching.
src/​Http/​Controllers/​Controller.php Adds shared validation ordering and shared confirmation helper for non-MB methods.
src/​Http/​Controllers/​CallbackController.php New unified callback controller that dispatches by mp / route default.
src/​Events/​MBReferencePaid.php Extends event payload to optionally include the recorded MB payment.
src/​Enums/​PaymentMethod.php Adds enum for supported mp codes.
routes/​web.php Registers /eupago/callback and deprecated per-method aliases with defaults.
docs/​multibanco.md Documents MB range/repeat payment behavior and payments relationship.
docs/​configuration.md Updates routing docs to reference the unified callback controller/endpoint.
docs/​callbacks.md Rewrites callback docs around the unified endpoint, validation ordering, and idempotency.
database/​migrations/​2026_09_26_000000_create_mb_reference_payments_table.php Adds MB payments table and backfills existing paid references.
config/​eupago.php Updates route documentation to reflect the unified endpoint.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Http/Controllers/Controller.php Outdated
@jfrosorio jfrosorio self-assigned this Sep 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Callback validation still allows non-numeric valor values that can trigger database errors during numeric comparisons, and the upgrade docs’ custom per-method route example omits the route default needed to preserve the promised fallback behavior.

Review effort: Lite
Findings: None

Resolved since last review (1)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Validate valor as numeric before SQL comparisons

src/​Http/​Requests/​CallbackRequest.php:36

valor is only validated as required, but it is later used in numeric SQL comparisons (e.g. MbReference::accepting() uses min_value <= valor <= max_value). If a valid caller sends a non-numeric value (or an array), this can cause a database error (notably on PostgreSQL) instead of a clean 422 response. Consider validating valor as numeric to ensure predictable behavior.

Low severity Set default_payment_method in per-method route example

UPGRADE.md:64

The custom per-method route example does not set a default_payment_method. Without that route default, CallbackController will reject callbacks where mp is missing or uses an unknown/legacy code (the package’s built-in deprecated aliases explicitly set this default). Updating the snippet keeps behavior consistent with the deprecated per-method endpoints.

@jfrosorio
jfrosorio merged commit 4b8fafd into master Sep 26, 2026
13 checks passed
@jfrosorio
jfrosorio deleted the fix-94 branch September 26, 2026 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Realtime 1.0 callbacks only confirm payments for one payment method

2 participants