Leave an abandoned Worldline checkout to its deadline, instead of recording it failed

Reconciling an abandoned Worldline checkout recorded the payment failed. Letting the expiry sweep write the same non-event off without reconciling recorded it expired. Which one a site got depended on the reconciliation budget and on whether the provider answered, not on anything the payer did.

resolveFromProvider() handed finalize() the created payment, which for an abandoned checkout is NULL. finalize() found it not accepted, found paymentCancelledByConsumer() false (it needs a created payment), and fell through to fail(). Because that always resolved the payment, the NothingHappened branch below it was unreachable, and the notice naming the hosted checkout status never fired.

The fix: a checkout that created no provider payment is not settled here at all. Nothing was charged, and a real refusal arrives as a created payment in the REJECTED status, through the branch that already handles it. So the finalizer reports NothingHappened and the payment's own deadline decides, through the reaper, exactly as it does for a gateway with no provider to ask. One event, one answer.

finalize() now takes a non-nullable created payment: the webhook, its only other caller, already returns early without one.

This is not the resolution the issue summary proposes. That proposed recording cancelled before the deadline and expired after it, using the hosted checkout status. Dropped, because CANCELLED_BY_CONSUMER does not mean the payer pressed cancel: a hosted session can lapse while the payment still has time left (the session is clamped to be no longer than the deadline, but it may be shorter). Recording that as cancelled would terminate a payment the payer could still have completed with a fresh session. Deferring to the deadline gives the same answer for the abandonment case without inventing a distinction the provider cannot support.

Tests. Five new assertions in WorldlineFinalizerTest, including one that drives the whole sweep (expireDue()) and asserts the payment ends expired. Run against the unfixed code first: it records failed, so the test reproduces the reported bug rather than ratifying the new implementation.

Docs updated in docs/worldline.md (the outcome table) and docs/architecture.md (what a gateway may and may not record when reconciling), plus the webhook warning that described the old settling behaviour. No translatable strings change, so no .po churn. Metrics regenerated.

Merge request reports

Loading