Nothing checks the constraint policies before payment, so a violating order can be charged and only refused afterwards
Closes the gap between the form and the confirmation, which is exactly where the money moves.
Why the gap is real
The policies were checked when the booker registered and are checked again when the order is confirmed. An order can comply at the first and not at the second: a per-booker limit cannot be counted until the order names a booker, so every basket the same booker fills afterwards lands in that gap. The booker paid, was refused at confirmation, and a workflow cancelling the order behind them had taken money for a booking the rules never allowed.
What it adds
OrderManager::checkoutViolations(), running the same policies at thePolicyCheckpoint::Checkoutcheckpoint, which had no caller until now. A dedicated method rather than a checkpoint argument onvalidateOrder(), whose callers all mean the confirmation and whose contract says so.CheckoutPolicySubscriber, answering Orchestra'sCheckoutEvents::CHECKING(added in orchestra [#3614892]) with one reason per violation. Separate fromCheckoutLockSubscriber: that one holds the order still, this one judges it. They meet only in the order the step dispatches them, so what is judged is an order nothing can add to.
The booker is stopped twice over, both times before any money moves: on the payment page the reasons are listed above the amount and Go to payment is drawn inert, and on the click the checkout is closed and they land back on that page with the reasons.
Every violation is reported, not the first. A booker fixes what they are told about, so one rule at a time means editing the basket once per rule to buy nothing.
An operator waiver needs no code here: ConstraintPolicyManager::evaluate() already reports nothing for a waived order at every checkpoint but the hold.
What deliberately does not reach the booker
Deferrals. A policy that could not be asked is not a policy that refused, and "this limit could not be checked" is an operator's concern: the Order review step shows it and the payment page does not.
Tests
New CheckoutPolicyTest: an order over a per-booker limit refused in the policy's own words, a compliant one not, two attachments giving two reasons, a waived order not refused, a policy still waiting for a booker refusing nothing, and a non-booking run left alone.
Both refusal cases were run against the code without the subscriber first and seen to fail. yoyaku_order and yoyaku_payment kernel suites green (24/24). phpcs (Drupal, DrupalPractice, warnings included), the CI phpstan ruleset and cspell all clean.
docs/constraint-policies.md gains a The checkout section and its checkpoint table row is corrected: the order is pending while the page is drawn and locked on the click. docs/payment.md gains the subscription. No new interface strings, so no .po change: the sentences the booker reads are the policies' own, already translated.