Pre-release audit since alpha12: a checkout left open by a throw, an outcome named wrong, a query on every node left, an inert control with no role, dead code, stale docs and misfiled translations

Fixes for the pre-release audit of 1.x since 1.0.0-alpha12. See [#3615875] for the findings.

Correctness

  • PaymentInteraction::pay() closes the checkout on a throw. Every deliberate exit after the opening event already announced the closing; a raise from resolve(), preview() or the payment creation did not, leaving a subscriber holding its subject for a checkout that will never charge. The closing is idempotent, so an exit that already closed does not announce a second one.
  • The payment timeout description named the timeout outcome. It is expired. Fixed in the setting and in fr.po.

Performance

  • RelativeDeadline::forget() returns early unless the anchor is node. The executor forgets deadline state on every token leaving a node that carries any timeout, and only that anchor ever records any, so the ordinary wait was paying a variable lookup on the way out that could only find nothing.
  • The click to pay looks the reusable pending payment up once instead of three times: pendingAsking() returns what survived rather than nothing. PinnedPayment::existsInAnyState() answers "already paid" in one query instead of two.

Accessibility

  • The inert payment control takes role="link", without which aria-disabled is dropped and a screen-reader user is told nothing about why it will not act.
  • The refusal id moved from the list to a wrapper, so the sentence framing the reasons is read out with the control that points at them.
  • orchestra_payment ships a component stylesheet, so the inert control no longer looks identical to the live one under a theme that says nothing about is-disabled. The disabled state is never carried by color alone: the reasons are listed above it in words.

Dead code

  • DeadlineCalculator::resolveDuration() and its WorkflowEngine delegate are gone (no callers anywhere; the provider refactor had copied the body into RelativeDeadline). DeadlineCalculator no longer takes VariableResolver.
  • TimeoutSweeper no longer takes the config factory it stopped reading.
  • isVariableName() moved to DeadlineProviderBase instead of living twice.

Documentation and translations

  • PaymentOrchestraHooks described a two-pin design that does not exist; rewritten to match docs/payment.md and the code.
  • docs/concepts.md and docs/roadmap.md describe the deadline provider and the checkout events; docs/metrics.md regenerated.
  • The two payment_lapsed strings moved to orchestra_payment's own fr.po; 80 orphaned msgids removed across 17 files; 36 previously untranslated strings added.
  • drupal/kessai is pinned to ^1.0@alpha rather than 1.x-dev.

Tests

  • CheckoutOrderingTest::testThrowOnTheWayToTheGatewayClosesTheCheckout and InstanceTimeoutAnchorTest::testLeavingParkAnchoredNodeReadsNoAnchor are new; both were run against the unfixed code and seen to fail first. The anchor one compares the query argument with the LIKE escaping stripped, because an entity query escapes every underscore in a variable name and a plain comparison passes for the wrong reason.
  • CheckoutRefusalTest asserts the control's role and the new refusal structure.

Ran locally against MySQL: the payment kernel suite (10 classes) and the engine timeout classes. phpcs (drupal.org CI ruleset, severity 1), phpstan (phpstan-drupal level 1, only the new.static findings CI ignores), stylelint and cspell are clean.

Merge request reports

Loading
Loading