Skip to content

PRE-3715: Allow Oney refunds based on the PayPlug API refund window - #336

Open
hdelaforce-payplug wants to merge 4 commits into
developfrom
feature/PRE-3715_remove-oney-48h-refund-delay
Open

hdelaforce-payplug wants to merge 4 commits into
developfrom
feature/PRE-3715_remove-oney-48h-refund-delay

Conversation

@hdelaforce-payplug

@hdelaforce-payplug hdelaforce-payplug commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Description

Oney refunds are no longer held back by a hardcoded 48h delay. The refund window returned by the PayPlug API (refundable_after / refundable_until) is now the only rule, which matches the PrestaShop and WooCommerce plugins.

  • Removed the local 48h rule. RefundPaymentGeneratedHandler blocked a refund for 48h after the last payment and after the last processed refund, so successive partial refunds were blocked too. The private checks, the now-unused $orderRepository / $translator constructor arguments and OneyGatewayFactory::REFUND_WAIT_TIME_IN_HOURS are gone.
  • RefundUnitsCommandCreatorDecorator::canOneyRefundBeMade() is now the single local check, run on the admin refund form before the refund is dispatched:
    • refused when now < refundable_after, with "The Oney refund will only be possible from %date%."
    • refused when now > refundable_until, with "The Oney refund period ended on %date%…"
    • the exact boundary instants are now accepted (they were rejected before);
    • a missing or null bound means no restriction on that side. Before, it raised an undefined-property warning and showed the misleading 48h message. The PayPlug API stays the final authority and still rejects an out-of-window refund, with nothing recorded locally.
    • %date% is formatted with IntlDateFormatter (medium date, short time) in the translator locale and the PHP default timezone.
  • Translations (en/fr/it): ui.oney_transaction_less_than_forty_eight_hours is replaced by ui.oney_refund_not_available_before and ui.oney_refund_period_expired.
  • Sylius 2.3 compatibility (second commit): Sylius 2.3 changed the first parameter of HttpResponseProviderInterface::supports() / getResponse() from RequestConfiguration to Request, so CaptureHttpResponseProvider failed PHPStan and would fatal at class load on 2.3. The parameter is widened to Request|RequestConfiguration, which is valid against both interface versions. Sylius 2.2 stays supported, the parameter is unused, and there is no behaviour change. The other UPGRADE-2.3.md items were checked and none of them affect the plugin.

Motivation: merchants had to wait an arbitrary 48h before refunding an Oney order, and again 48h between two partial refunds, even when the API already allowed the refund. composer.json allows sylius/sylius: ^2.0, so a merchant on Sylius 2.3 would also have hit the provider signature break.

Related issue(s): PRE-3715. Possibly related to PRE-3710 (multi-line Oney refund error), but not claimed as fixed: see Notes for reviewer.


Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue) [x]
  • ✨ New feature (non-breaking change that adds functionality) [x]
  • 💥 Breaking change (fix or feature that causes existing functionality to change and that could impact other libs) [x]
  • 🔧 Refactor (no functional changes, code improvement only) [ ]
  • 📦 Dependency update [ ]
  • 🔒 Security fix [ ]
  • 📝 Documentation update [ ]

Checklist

Code Quality

  • Code is linted and formatted
  • No unnecessary commented-out code or debug logs
  • No hardcoded values (use env variables or config)

Testing

  • Unit tests added / updated
  • New/changed code is covered by tests — SonarCloud Quality Gate (coverage on new code) passes on the sonarcloud CI job

Security & Ops

  • No sensitive data or secrets introduced
  • Logging and error handling are appropriate

Notes for reviewer

Breaking changes. All of these are documented in the CHANGELOG 2.0.0 "Breaking changes for anyone extending the plugin" table. 2.0.0 is still unreleased.

  • OneyGatewayFactory::REFUND_WAIT_TIME_IN_HOURS is removed, not deprecated. The class is final and nothing reads the constant any more.
  • Translation key ui.oney_transaction_less_than_forty_eight_hours is removed. It is replaced by the two new keys, which take a %date% parameter.
  • RefundPaymentGeneratedHandler::__construct() drops $orderRepository and $translator. The class is final and container-built.

The behaviour change is intended: a refund that was blocked for less than 48h is now allowed whenever the API allows it.

Testing.

Check (PHP 8.3) Result
PHPUnit Pass. Commit 1 run: 619 tests, 1163 assertions (601 baseline + 18 new).
PHPStan (level max) No errors, no new baseline entries
ECS No errors
PHPMD No new violations (43 pre-existing)
GrumPHP pre-commit hooks Passed on both commits

New tests:

  • tests/PHPUnit/Creator/RefundUnitsCommandCreatorDecoratorTest.php (16 tests) covers:
    • inside the window, and the exact refundable_after boundary;
    • before and after the window, with the dated messages;
    • every null or missing bound combination;
    • fr locale with the Europe/Paris timezone;
    • two refunds in a row;
    • the existing 10-cent minimum;
    • non-Oney gateways, which never call the API.
  • tests/PHPUnit/MessageHandler/RefundPaymentGeneratedHandlerTest.php: an Oney refund on a just-created payment reaches the API, and an API refusal records nothing.
  • CaptureHttpResponseProviderTest runs every case with both a Request (2.3) and a RequestConfiguration (2.2), plus a reflection guard on the union type.

Not run:

  • Behat. It cannot boot locally (Lakion\Behat\MinkDebugExtension not found). The four 48h scenarios in features/admin/refunding_oney_payment.feature were replaced by window-based ones, and the context, mocker and ui.xml were updated, but they were never executed. The date assertions assume an English admin locale.
  • Sylius 2.2 runtime. The fix was reasoned from the 2.2.9 lock and the 2.2 interface signature, and the union was compiled against both interface shapes. It was not run on an installed 2.2. CI should cover it if the matrix includes 2.2.
  • Manual TEST environment. Oney 3x/4x full and partial refunds, with and without fees, still need checking (acceptance criterion 4). In particular, check whether the API itself moves refundable_after after a first partial refund. If it does, successive refunds stay blocked by the API, now with an accurate date. That is why PRE-3710 is not claimed as fixed.

Unticked checklist items:

  • SonarCloud: CI had not finished when this was written.
  • Logging and error handling: this PR does not change it. A failing PayPlugApiClient::retrieve() (timeout, 404) in the decorator still surfaces as an admin 500, as before. It is out of scope for this story and a possible follow-up.

Scope notes:

  • The local window check guards the admin refund-units form only. The Sylius refund payment transition and refunds made from the PayPlug portal never had a local Oney check, and that is unchanged.
  • RefundHistoryRepositoryInterface::findLastProcessedRefundForPayment() no longer has a caller. It is kept on purpose because it is on a public interface.
  • The Request|RequestConfiguration union is a bridge. Narrow it to Request once Sylius 2.2 support is dropped.

Changelog: CHANGELOG.md 2.0.0, with entries under Changed, Removed and Fixed, plus three rows in the BC table.

@hdelaforce-payplug hdelaforce-payplug changed the title Feature/pre 3715 remove oney 48h refund delay PRE-3715: Allow Oney refunds based on the PayPlug API refund window Oct 8, 2026
@hdelaforce-payplug
hdelaforce-payplug force-pushed the feature/PRE-3715_remove-oney-48h-refund-delay branch from e54438b to a311322 Compare October 8, 2026 15:52
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hdelaforce-payplug
hdelaforce-payplug force-pushed the feature/PRE-3715_remove-oney-48h-refund-delay branch from a311322 to d239231 Compare October 8, 2026 16:09
hdelaforce-payplug and others added 2 commits October 8, 2026 18:40
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant