Add PaymentsEndpoint::findByOrderId() and typed list filters (PaymentsQuery) - #12
Conversation
…sQuery) Every integration needs "look up the payment for this order": order_id is unique per Quickpay account (a second create fails with "already exists on another payment"), so creating payments idempotently means find-or-create — and the SDK had no typed way to search. GET /payments supports order_id, state, accepted, min_time/max_time, acquirer, fraud_suspected, id, sort_by/sort_dir and operations_size, but CollectionRequestOptions only carried page/page_size. - New `PaymentsQuery extends CollectionRequestOptions` with typed, optional filters (state accepts the PaymentState enum or a plain string; booleans are sent as true/false; min/max time formatted as the API documents; an `extra` hash for anything unmodeled) — usable with getPage() and paginate(), which carries the filters across pages. - New `PaymentsEndpoint::findByOrderId(string): ?Payment` — exact match, with a defensive client-side equality check. - CollectionRequestOptions is no longer final (BC-safe) so resource-specific query classes can extend it; withPage()/withPageSize() are overridable. - README: filtering example + a "Finding the payment for an order (idempotent create)" recipe. Filter semantics (exact/case-sensitive order_id, boolean encoding, time formats, order_id uniqueness) were verified against the live API. Refs #10 (finding 3).
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 1.x #12 +/- ##
============================================
+ Coverage 98.50% 98.69% +0.19%
- Complexity 147 163 +16
============================================
Files 24 26 +2
Lines 401 461 +60
============================================
+ Hits 395 455 +60
Misses 6 6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… overrides withPage()/withPageSize() now clone and set, returning static — so a subclass's filters come along for free and PaymentsQuery no longer needs its own withers or a 14-argument copy constructor. Only page/pageSize lose readonly (reinitializing readonly properties during clone requires PHP 8.3; the SDK supports 8.1); the filters on PaymentsQuery stay readonly. The withers validate like the constructor does.
|
Follow-up per review: the withers are now The one trade-off, and why the first version didn't do this: reinitializing a |
Part of #10 (finding 3).
Why
Every integration needs "look up the payment for this order":
order_idis unique per Quickpay account (a secondcreate()fails withValidationException— "already exists on another payment"), so creating payments idempotently means find-or-create — and the SDK had no typed way to search.GET /paymentssupportsorder_id,state,accepted,min_time/max_time,acquirer,fraud_suspected,id,sort_by/sort_dirandoperations_size, butCollectionRequestOptionsonly carriedpage/page_size.What
PaymentsQuery extends CollectionRequestOptions— typed, optional filters, usable withgetPage()andpaginate()(filters are carried across pages):stateaccepts the enum or a plain string (non-exhaustive enum), booleans go out astrue/false,minTime/maxTimeare formatted as the API documents (Y-m-d H:i:s O), and anextrahash passes anything unmodeled through verbatim (page/page_sizealways come from the typed options).PaymentsEndpoint::findByOrderId(string $orderId): ?Payment—GET /payments?order_id=…&page_size=1, exact match, with a defensive client-side equality check so it can never hand back a different order.CollectionRequestOptionsis no longerfinal(BC-safe) so resource-specific query classes can extend it;withPage()/withPageSize()are documented as overridable andPaymentsQueryoverrides them.Verified against the live API (read-only probes + two throwaway unpaid payments)
order_idfilter is an exact, case-sensitive match (a prefix or a different casing returns nothing).order_idis unique per account even for unpaidinitialpayments →findByOrderId(): ?Paymentis well-defined.accepted=true|falseand1|0are both accepted;accepted=maybe→ 400.sort_dirother thanasc/desc→ 400.min_time/max_timeaccept the documented%Y-%m-%d %H:%M:%S %z(and ISO 8601). Default order is newest first.Tests: 12 new (query mapping, boolean/time encoding,
extraprecedence, filters survivingwithPage(), filteredgetPage(), filteredpaginate()across pages,findByOrderId()found / not found / never a different order).