When developing a custom integration with Montonio payments, you must properly handle our payment statuses. In addition to webhook notifications, we include the payment status in the "order token" when redirecting the customer back to the specified returnUrl, as described here in our API documentation. You can read more about the different payment statuses of an Order in the "What is the lifecycle of an Order?" chapter of our API documentation.
Why do I get the PENDING payment status?
When a customer decides to cancel a payment or is unable to complete the flow, we redirect them back to the specified returnUrl. Because the Order was not paid for, we don't change the Order status. The status stays in PENDING until we set it to ABANDONED after the specified expiry (30 minutes by default).
We do not send webhooks with the PENDING status, because the status has not changed. However, it is included in the token in the returnUrl, in case payment was not completed.
Why don't we change the status to ABANDONED immediately?
When a customer cancels the flow, we don't change it to ABANDONED immediately because it is still technically possible that the payment is being finalized in the background by the banks. Imagine a scenario where the customer starts a payment with Swedbank and proceeds to sign with Smart-ID. However, there’s a delay with the Smart-ID notification, and the customer returns to the payment gateway to exit the flow. The notification then pops up and the customer seizes the opportunity to finish the payment. Our system detects the payment and changes the status from PENDING to PAID.
There are a couple of more instances where a payment may take a long time to clear and while the customer may have exited the flow, the payment is still being processed in the background. Due to the nature of Open Banking APIs, we often cannot stop such processes and must expect that a payment can be completed even after the customer exits the flow. For data consistency, we keep the payment in PENDING status until we are confident it cannot be finalized anymore.
However, we offer the ABANDONED status for when the payment has been idle for a longer time and it is not feasible that it would be completed in the background. By default, Orders get the ABANDONED status 30 minutes after starting the flow. You can configure this using the expiresIn parameter, as per our API documentation. We recommend keeping it for 15 minutes or more.
This type of “background finalization” happens very rarely and you will be notified of status changes via webhook.
How to handle the PENDING status?
When a customer returns with the PENDING status, you can safely display to the customer that the payment was not finished and let them try again.
We recommend using the same merchantReference for any retries with the same order details. This way we don't create a new Order in our systems. Crucially, in case a previous payment of the same merchantReference was finalized in the background (the edge cases described above), we can smoothly handle the user flow and change the Order status to PAID, avoiding a double payment by the user.
However, if the grand total of the order changes, you should create a new Order with a completely new merchantReference. Otherwise, an Order could be marked as PAID with a smaller payment amount than the grand total.
In our experience, implementing the above flow provides the best user experience, avoiding any edge-case double payments. Our WooCommerce plugin also implements the API with this exact same logic. It has been field-tested extensively with thousands of merchants and the UX has proven to be top-notch!