You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[8.1.10] REST webhook answers 200 when the payment status can't be fetched, so Mollie stops retrying and paid orders stay pending #1304
A Mollie webhook carries only the payment id, and the plugin then fetches the status from the API. On the default REST webhook URL (/wp-json/mollie/v1/webhook), when that fetch fails, the callback still answers 200. The fetch can fail on a timeout, on a 5xx or 429 from the API, or on a DNS or TLS problem on the store's server. Mollie then treats the webhook as handled and doesn't retry it, so the paid order stays in Pending payment.
The WC-API webhook answers 400 in the same situation (MollieOrderService.php#L137-L138). The REST callback's own fallback branch answers 500 for an ApiException (RestApi.php#L172-L174). Only the main branch acknowledges the failure.
I ran both webhook entry points with the plugin's own code: from RestApi::callback() and onWebhookAction() down to WordPressHttpAdapter and mollie-api-php v2.79.1. WordPress and WooCommerce were stubbed, and wp_remote_request() played the Mollie API:
Mollie API answer
REST webhook
WC-API webhook
Order afterwards
200, payment paid
200
200
processing
timeout (cURL error 28)
200
400
pending
503
200
400
pending
429
200
400
pending
404
200
400
pending
The only trace is two debug lines: Could not load payment tr_… (live): … cURL error 28 …, then doPaymentForOrder: payment tr_… not found.
Why it matters
Mollie retries a webhook up to 10 times over about 26 hours until it gets a 200 (docs). A non-200 here would let timeouts, rate limits and short outages fix themselves on a later attempt. With a 200, the only second chance is the check before WooCommerce cancels an unpaid order (GatewayModule.php#L216-L232). That check runs only when stock management is on, and if its request fails too, a paid order is cancelled. #792 shows the same symptom: cURL error 6: Could not resolve host: api.mollie.com, and paid orders left waiting for payment.
Suggested fix
Answer non-200 (e.g. 503) when the status could not be fetched, and keep 200 for ids Mollie doesn't know, as the docs recommend. doPaymentForOrder() also returns false for other reasons, for example a status without a handler, so the callback needs to know why. One way is to keep the ApiException code (0 for transport errors, the HTTP status otherwise) instead of turning it into null.
Thanks for your report! This seems an oversight when introducing the new wp-json route. As this is a pretty delicate part of the system, we'll create a patch on our end and have to go through QA in one go - but thanks for your offer to provide a PR.
Activity
fjbender commentedon Sep 28, 2026
Thanks for your report! This seems an oversight when introducing the new
wp-jsonroute. As this is a pretty delicate part of the system, we'll create a patch on our end and have to go through QA in one go - but thanks for your offer to provide a PR.We're tracking this as PIWOO-951
terminalstate commentedon Sep 28, 2026
Thanks, Florian, sounds good. If it helps QA: the harness in https://github.com/terminalstate/callback-audit/tree/main/repro/mollie-woocommerce runs the plugin's own webhook code against a fake Mollie API, on both webhook entry points, for a timeout, a 503, a 429 and an unknown payment id. Once the fix is public, I'm happy to run it against the patch and post the results here.
docs(mollie case): confirmed upstream (mollie/WooCommerce#1304, PIWOO…