3.4.0 - token lifecycle, 401 retry, cart floats
Works through the remaining findings from the audit of 3.0.0. Requires tomsommer/oauth2-onpay ^2.1.
Token handling
- A stored token carrying
expires_inbut no issue time never expired.expires_inis relative to the moment of issue, which a stored blob no longer records, so league recomputed the expiry from the current time on every read — leaving the token perpetually an hour fresh however old it really was. It is now treated as spent, and renewed. - An unparsable
issued_atis likewise treated as spent rather than silently dropped. - Tokens are renewed 30 seconds early, so one that is technically alive but will be dead by the time it reaches OnPay does not cost a wasted round trip.
expiresof0means no expiry in league's own terms; askinghasExpired()in that state raised aRuntimeException.- A non-string
access_tokenno longer reaches the header, where it rendered asBearer Array. - A malformed token response raised league's
InvalidArgumentException, outside the documented exception contract. It is aTokenExceptionnow.
One retry after a 401
OnPay is the authority on whether a token is good. A 401 with a refresh token available is now worth exactly one refresh and retry, rather than an immediate failure over clock drift or a token revoked underneath us. With nothing to renew with, behaviour is unchanged — and a second 401 is final.
Cart totals
round(19.99 * 100) is 1999.0, and the total check compared it !== against int 1999, producing:
Cart total does not match amount for payment, cart total was calculated to: 1999, amount provided is: 1999
Amounts are normalised on the way into CartItem, as they already were for shipping and handling.
Also
SimplePayment indexed the response envelope unguarded — the same fault already fixed in the service classes. Converter returned false for anything but the documented date format, putting a bool in properties typed \DateTime; it now returns null and understands ISO-8601. A numeric-string currency code is as valid as an int. And "0" is a legitimate identifier that empty() was calling absent.
PaymentService, GatewayService, PaymentInfo and Converter had no tests at all; they do now. 164 tests, up from 140.