Skip to content

Wrap PSR-18 transport failures in a TransportException - #18

Merged
loevgaard merged 1 commit into
1.xfrom
dx/transport-exception
Aug 17, 2026
Merged

Wrap PSR-18 transport failures in a TransportException#18
loevgaard merged 1 commit into
1.xfrom
dx/transport-exception

Conversation

@loevgaard

Copy link
Copy Markdown
Member

Part of #10 (finding 9).

Why

QuickpayException's docblock promises that catch (QuickpayException $e) nets all SDK-thrown exceptions — but a DNS failure, refused connection or timeout came straight through as the HTTP client's ClientExceptionInterface, so consumers either missed them or had to know PSR-18 internals to catch them.

What

  • New TransportException (final, extends \RuntimeException) implements both QuickpayException and Psr\Http\Client\ClientExceptionInterface — so an existing catch (ClientExceptionInterface $e) still matches. It exposes getRequest() (with the SDK headers applied), isNetworkError() (previous is a NetworkExceptionInterface), and the original exception as getPrevious(). Message: The request could not be sent [GET https://api.quickpay.net/payments]: connection refused (query string stripped, like the other exceptions).
  • Client::request() wraps the PSR-18 exception; getLastRequest() records the attempt and getLastResponse() is reset to null for it.
  • ClientInterface @throws updated (TransportException instead of ClientExceptionInterface); QuickpayException docblock now true; README error-handling section covers it (with the retry-safety hint that pairs with findByOrderId() from Add PaymentsEndpoint::findByOrderId() and typed list filters (PaymentsQuery) #12).

BC note

Only code that catches NetworkExceptionInterface / RequestExceptionInterface specifically (rather than ClientExceptionInterface) would no longer match — it should check $e->getPrevious() (or isNetworkError()) instead. Flagging so you can decide; I think fulfilling the documented "one catch nets everything" contract is worth it a week after 1.0.0.

Tests: network vs request failures (a small in-test throwing PSR-18 client), message/URI sanitizing, last request/response bookkeeping, catch (QuickpayException) netting it, and the hierarchy test covers both interfaces.

@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.58%. Comparing base (c992a67) to head (771d2dd).

Additional details and impacted files
@@             Coverage Diff              @@
##                1.x      #18      +/-   ##
============================================
+ Coverage     98.51%   98.58%   +0.06%     
- Complexity      148      152       +4     
============================================
  Files            24       25       +1     
  Lines           404      423      +19     
============================================
+ Hits            398      417      +19     
  Misses            6        6              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

QuickpayException's docblock promises that catch (QuickpayException) nets all
SDK-thrown exceptions — but a DNS failure, refused connection or timeout came
straight through as the HTTP client's ClientExceptionInterface, so consumers
either missed them or had to know about PSR-18 to catch them.

- New TransportException (final) extends RuntimeException implements
  QuickpayException AND Psr\Http\Client\ClientExceptionInterface — so an
  existing catch (ClientExceptionInterface) still matches. Exposes
  getRequest() (with the SDK headers applied), isNetworkError() (previous is
  a NetworkExceptionInterface) and the original as getPrevious(). Message:
  "The request could not be sent [METHOD sanitized-URL]: <original>".
- Client::request() wraps; lastRequest is recorded and lastResponse reset to
  null for the failed attempt.
- ClientInterface @throws updated; README error-handling section covers it.

BC note: only code that catches Psr\Http\Client\NetworkExceptionInterface /
RequestExceptionInterface SPECIFICALLY (rather than ClientExceptionInterface)
would no longer match — check $e->getPrevious() instead.

Refs #10 (finding 9).
@loevgaard
loevgaard force-pushed the dx/transport-exception branch from b9efa0b to 771d2dd Compare August 17, 2026 09:48
@loevgaard
loevgaard merged commit a6e3ff2 into 1.x Aug 17, 2026
35 checks passed
@loevgaard
loevgaard deleted the dx/transport-exception branch August 17, 2026 09: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.

1 participant