1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import psycopg2
from odoo import http
from odoo.http import request
_logger = logging.getLogger(__name__)
class PaymentPostProcessing(http.Controller):
"""
This controller is responsible for the monitoring and finalization of the post-processing of
transactions.
It exposes the route `/payment/status`: All payment flows must go through this route at some
point to allow the user checking on the transactions' status, and to trigger the finalization of
their post-processing.
"""
MONITORED_TX_ID_KEY = '__payment_monitored_tx_id__'
@http.route('/payment/status', type='http', auth='public', website=True, sitemap=False)
def display_status(self, **kwargs):
""" Fetch the transaction and display it on the payment status page.
:param dict kwargs: Optional data. This parameter is not used here
:return: The rendered status page
:rtype: str
"""
monitored_tx = self._get_monitored_transaction()
# The session might have expired, or the transaction never existed.
values = {'tx': monitored_tx} if monitored_tx else {'payment_not_found': True}
return request.render('payment.payment_status', values)
@http.route('/payment/status/poll', type='json', auth='public')
def poll_status(self, **_kwargs):
""" Fetch the transaction and trigger its post-processing.
:return: The post-processing values of the transaction.
:rtype: dict
"""
# We only poll the payment status if a payment was found, so the transaction should exist.
monitored_tx = self._get_monitored_transaction()
# Post-process the transaction before redirecting the user to the landing route and its
# document.
if not monitored_tx.is_post_processed:
try:
monitored_tx._post_process()
except (
psycopg2.OperationalError, psycopg2.IntegrityError
): # The database cursor could not be committed.
request.env.cr.rollback() # Rollback and try later.
raise Exception('retry')
except Exception as e:
request.env.cr.rollback()
_logger.exception(
"Encountered an error while post-processing transaction with id %s:\n%s",
monitored_tx.id, e
)
raise
return {
'provider_code': monitored_tx.provider_code,
'state': monitored_tx.state,
'landing_route': monitored_tx.landing_route,
}
@classmethod
def monitor_transaction(cls, transaction):
""" Make the provided transaction id monitored.
:param payment.transaction transaction: The transaction to monitor.
:return: None
"""
request.session[cls.MONITORED_TX_ID_KEY] = transaction.id
def _get_monitored_transaction(self):
""" Retrieve the user's last transaction from the session (the transaction being monitored).
:return: the user's last transaction
:rtype: payment.transaction
"""
return request.env['payment.transaction'].sudo().browse(
request.session.get(self.MONITORED_TX_ID_KEY)
).exists()
|