Skip to content

[FIX] payment_mollie: don't acknowledge webhook on API failure - #290812

Closed
Josuesp1620 wants to merge 2 commits into
odoo:19.0from
Josuesp1620:19.0-fix-mollie-webhook-retry
Closed

Josuesp1620 wants to merge 2 commits into
odoo:19.0from
Josuesp1620:19.0-fix-mollie-webhook-retry

Conversation

@Josuesp1620

Copy link
Copy Markdown
Contributor

When fetching the payment from Mollie failed (connection error, timeout, 503, 429), the webhook still answered 200. Mollie only retries a webhook on a non-2xx response, so it stopped retrying and the transaction stayed in draft.

The webhook now answers 503 in that case so that Mollie retries later. Unknown references are still acknowledged with a 200, as Mollie recommends, and the return route is unchanged.

Fixes #290678


I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr

@robodoo

robodoo commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

Pull request status dashboard

@C3POdoo
C3POdoo requested review from a team September 28, 2026 14:44
@Josuesp1620
Josuesp1620 force-pushed the 19.0-fix-mollie-webhook-retry branch from f5d26fd to c4d6d77 Compare September 28, 2026 15:28
When fetching the payment from Mollie failed, the webhook still answered
200, so Mollie stopped retrying and the transaction stayed in draft.

Answer 503 instead so that Mollie retries later. Unknown references are
still acknowledged and the return route is unchanged.

Fixes odoo#290678
@AntoineVDV
AntoineVDV force-pushed the 19.0-fix-mollie-webhook-retry branch from c4d6d77 to 418b24a Compare October 6, 2026 11:21

@AntoineVDV AntoineVDV left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Josuesp1620, and thank you for the fix; it's indeed patching a gap in payment_mollie 🙂

To save time in back-and-forths, I allowed myself to push some small changes directly onto your branch, mainly to clarify the behavior to adopt in case of API request failure from the controllers' perspective.

Would you mind checking the diff below and letting me know if that's fine for you? Then we can merge this PR.

diff --git a/addons/payment_mollie/controllers/main.py b/addons/payment_mollie/controllers/main.py
index fa77054b8748..b4e4efeed9a8 100755
--- a/addons/payment_mollie/controllers/main.py
+++ b/addons/payment_mollie/controllers/main.py
@@ -37,7 +37,10 @@ class MollieController(http.Controller):
                           embedded in the return URL.
         """
         _logger.info("handling redirection from Mollie with data:\n%s", pprint.pformat(data))
-        self._verify_and_process(data)
+        try:
+            self._verify_and_process(data)
+        except ValidationError:
+            _logger.error("Unable to process the payment data")
         return request.redirect('/payment/status')
 
     @http.route(_webhook_url, type='http', auth='public', methods=['POST'], csrf=False)
@@ -48,34 +51,27 @@ class MollieController(http.Controller):
                           embedded in the return URL
         :return: An empty string to acknowledge the notification
         :rtype: str
+        :raise ServiceUnavailable: If the payment data could not be fetched from Mollie
         """
         _logger.info("notification received from Mollie with data:\n%s", pprint.pformat(data))
-        self._verify_and_process(data, from_webhook=True)
+        try:
+            self._verify_and_process(data)
+        except ValidationError as error:
+            _logger.error("Unable to process the payment data")
+            raise ServiceUnavailable from error
         return ''  # Acknowledge the notification
 
     @staticmethod
-    def _verify_and_process(data, from_webhook=False):
+    def _verify_and_process(data):
         """Verify and process the payment data sent by Mollie.
 
         :param dict data: The payment data.
-        :param bool from_webhook: Whether the data were sent to the webhook.
         :return: None
-        :raise ServiceUnavailable: If the payment data could not be fetched from Mollie while
-                                   handling a webhook notification, so that Mollie retries later.
+        :raise ValidationError: If the payment data could not be fetched from Mollie
         """
         tx_sudo = request.env['payment.transaction'].sudo()._search_by_reference('mollie', data)
         if not tx_sudo:
             return
 
-        try:
-            verified_data = tx_sudo._send_api_request(
-                'GET', f'/payments/{tx_sudo.provider_reference}'
-            )
-        except ValidationError:
-            _logger.error("Unable to process the payment data")
-            if from_webhook:
-                # Don't acknowledge the notification: Mollie only retries calling the webhook when
-                # it doesn't receive a 2xx response.
-                raise ServiceUnavailable()
-        else:
-            tx_sudo._process('mollie', verified_data)
+        verified_data = tx_sudo._send_api_request("GET", f"/payments/{tx_sudo.provider_reference}")
+        tx_sudo._process("mollie", verified_data)
diff --git a/addons/payment_mollie/tests/test_mollie.py b/addons/payment_mollie/tests/test_mollie.py
index 2fb97fb66575..07bc5bae704a 100644
--- a/addons/payment_mollie/tests/test_mollie.py
+++ b/addons/payment_mollie/tests/test_mollie.py
@@ -40,45 +40,24 @@ class MollieTest(MollieCommon, PaymentHttpCommon):
             self._make_http_post_request(url, data=self.payment_data)
         self.assertEqual(tx.state, 'done')
 
-    @mute_logger('odoo.addons.payment_mollie.controllers.main')
-    def test_webhook_notification_not_acknowledged_when_payment_data_unavailable(self):
-        """ Test that the webhook doesn't acknowledge the notification when the payment data can't
-        be fetched from Mollie, so that Mollie retries later. """
-        tx = self._create_transaction('redirect')
-        url = self._build_url(MollieController._webhook_url)
+    @mute_logger("odoo.addons.payment_mollie.controllers.main")
+    def test_return_from_checkout_redirects_when_payment_data_unavailable(self):
+        self._create_transaction("redirect")
+        url = self._build_url(MollieController._return_url)
         with patch(
-            'odoo.addons.payment.models.payment_provider.PaymentProvider._send_api_request',
+            "odoo.addons.payment.models.payment_provider.PaymentProvider._send_api_request",
             side_effect=ValidationError("Service Unavailable"),
         ):
-            response = self._make_http_post_request(url, data=self.payment_data)
-        self.assertEqual(response.status_code, 503)
-        self.assertEqual(tx.state, 'draft')
+            response = self._make_http_get_request(url, params=self.payment_data)
+        self.assertTrue(response.url.endswith('/payment/status'))
 
-    @mute_logger('odoo.addons.payment.models.payment_transaction')
-    def test_webhook_notification_acknowledged_for_unknown_reference(self):
-        """ Test that the webhook acknowledges the notification when no transaction matches the
-        reference, as Mollie recommends for unknown ids. """
+    @mute_logger("odoo.addons.payment_mollie.controllers.main")
+    def test_webhook_notification_is_rejected_when_payment_data_unavailable(self):
+        self._create_transaction("redirect")
         url = self._build_url(MollieController._webhook_url)
         with patch(
-            'odoo.addons.payment.models.payment_provider.PaymentProvider._send_api_request',
-        ) as send_api_request_mock:
-            response = self._make_http_post_request(
-                url, data=dict(self.payment_data, ref='unknown reference'),
-            )
-        self.assertEqual(response.status_code, 200)
-        send_api_request_mock.assert_not_called()
-
-    @mute_logger('odoo.addons.payment_mollie.controllers.main')
-    def test_return_redirects_to_status_page_when_payment_data_unavailable(self):
-        """ Test that the customer is redirected to the status page when the payment data can't be
-        fetched from Mollie. """
-        tx = self._create_transaction('redirect')
-        url = self._build_url(MollieController._return_url)
-        with patch(
-            'odoo.addons.payment.models.payment_provider.PaymentProvider._send_api_request',
+            "odoo.addons.payment.models.payment_provider.PaymentProvider._send_api_request",
             side_effect=ValidationError("Service Unavailable"),
         ):
-            response = self._make_http_get_request(url, params=self.payment_data)
-        self.assertEqual(response.status_code, 200)
-        self.assertTrue(response.url.endswith('/payment/status'))
-        self.assertEqual(tx.state, 'draft')
+            response = self._make_http_post_request(url, data=self.payment_data)
+        self.assertEqual(response.status_code, 503)

@Josuesp1620

Copy link
Copy Markdown
Contributor Author

Thanks @AntoineVDV LGTM, and handling the errors in the controllers is definitely cleaner. Good to go from my side 👍

@AntoineVDV AntoineVDV left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robodoo r+ rebase-ff

@robodoo

robodoo commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Merge method set to rebase and fast-forward.

robodoo pushed a commit that referenced this pull request Oct 8, 2026
Part-of: #290812
Signed-off-by: Antoine Vandevenne (anv) <[email protected]>
robodoo pushed a commit that referenced this pull request Oct 8, 2026
When fetching the payment from Mollie failed, the webhook still answered
200, so Mollie stopped retrying and the transaction stayed in draft.

Answer 503 instead so that Mollie retries later. Unknown references are
still acknowledged and the return route is unchanged.

Fixes #290678

closes #290812

Signed-off-by: Antoine Vandevenne (anv) <[email protected]>
@robodoo robodoo closed this Oct 8, 2026
terminalstate added a commit to terminalstate/terminalstate.github.io that referenced this pull request Oct 8, 2026
terminalstate added a commit to terminalstate/callback-audit that referenced this pull request Oct 8, 2026
terminalstate added a commit to terminalstate/callback-audit that referenced this pull request Oct 9, 2026
mekot-odoo pushed a commit to odoo-dev/odoo that referenced this pull request Oct 26, 2026
Part-of: odoo#290812
Signed-off-by: Antoine Vandevenne (anv) <[email protected]>
mekot-odoo pushed a commit to odoo-dev/odoo that referenced this pull request Oct 26, 2026
When fetching the payment from Mollie failed, the webhook still answered
200, so Mollie stopped retrying and the transaction stayed in draft.

Answer 503 instead so that Mollie retries later. Unknown references are
still acknowledged and the return route is unchanged.

Fixes odoo#290678

closes odoo#290812

Signed-off-by: Antoine Vandevenne (anv) <[email protected]>

This branch was previously deployed

1 inactive deployment
merge — 418b24aa Deployed Oct 8, 2026 by robodoo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants