Files
odoo_source/addons/payment/static/src/js/post_processing.js
T
Antoine Vandevenne (anv) dbefe34af5 [IMP] payment(_authorize): rework validation transactions refund flow
Before this commit, the validation flow with verification (payment of a
small amount with immediate refund) was performed with the use of
validation routes: after payment, the customer was redirected to the
validation route stored on the transaction to trigger the refund. This
implementation had an issue: if the customer never reached the
validation route, they were not refunded their validation amount. This
could happen if the customer closed the tab after paying with an
acquirer offering payments with redirection, or if the validation
payment was asynchronously confirmed through a webhook notification.

This commit gets rid of validation routes and requires acquirers to
immediately refund the validation amount when the payment is confirmed.
This way, a payment confirmation coming from a webhook can trigger the
refund too.

As the only acquirer that implements the validation with verification
flow, Authorize.net now voids validation transactions as soon as they
are authorized.

While we're at it, the logging of processing values is adapted to only
log specific rendering values if a redirect form is rendered.

task-2612977

closes odoo/odoo#74707

Related: odoo/enterprise#20060
Related: odoo/upgrade#2710
Signed-off-by: Antoine Vandevenne (anv) <AntoineVDV@users.noreply.github.com>
2021-08-04 13:03:27 +00:00

133 lines
4.9 KiB
JavaScript

odoo.define('payment.post_processing', function (require) {
'use strict';
var publicWidget = require('web.public.widget');
var core = require('web.core');
const {Markup} = require('web.utils');
var _t = core._t;
$.blockUI.defaults.css.border = '0';
$.blockUI.defaults.css["background-color"] = '';
$.blockUI.defaults.overlayCSS["opacity"] = '0.9';
publicWidget.registry.PaymentPostProcessing = publicWidget.Widget.extend({
selector: 'div[name="o_payment_status"]',
xmlDependencies: ['/payment/static/src/xml/payment_post_processing.xml'],
_pollCount: 0,
start: function() {
this.displayLoading();
this.poll();
return this._super.apply(this, arguments);
},
/* Methods */
startPolling: function () {
var timeout = 3000;
//
if(this._pollCount >= 10 && this._pollCount < 20) {
timeout = 10000;
}
else if(this._pollCount >= 20) {
timeout = 30000;
}
//
setTimeout(this.poll.bind(this), timeout);
this._pollCount ++;
},
poll: function () {
var self = this;
this._rpc({
route: '/payment/status/poll',
params: {
'csrf_token': core.csrf_token,
}
}).then(function(data) {
if(data.success === true) {
self.processPolledData(data.display_values_list);
}
else {
switch(data.error) {
case "tx_process_retry":
break;
case "no_tx_found":
self.displayContent("payment.no_tx_found", {});
break;
default: // if an exception is raised
self.displayContent("payment.exception", {exception_msg: data.error});
break;
}
}
self.startPolling();
}).guardedCatch(function() {
self.displayContent("payment.rpc_error", {});
self.startPolling();
});
},
processPolledData: function (display_values_list) {
var render_values = {
'tx_draft': [],
'tx_pending': [],
'tx_authorized': [],
'tx_done': [],
'tx_cancel': [],
'tx_error': [],
};
if (display_values_list.length > 0) {
// In almost every cases there will be a single transaction to display. If there are
// more than one transaction, the last one will most likely be the one that was
// confirmed. We use this one to redirect the user to the final page.
window.location = display_values_list[0].landing_route;
return;
}
// group the transaction according to their state
display_values_list.forEach(function (display_values) {
var key = 'tx_' + display_values.state;
if(key in render_values) {
if (display_values["display_message"]) {
display_values.display_message = Markup(display_values.display_message)
}
render_values[key].push(display_values);
}
});
function countTxInState(states) {
var nbTx = 0;
for (var prop in render_values) {
if (states.indexOf(prop) > -1 && render_values.hasOwnProperty(prop)) {
nbTx += render_values[prop].length;
}
}
return nbTx;
}
// if there's only one tx to manage
if(countTxInState(['tx_done', 'tx_error', 'tx_pending', 'tx_authorized']) === 1) {
var tx = render_values['tx_done'][0] || render_values['tx_authorized'][0] || render_values['tx_error'][0];
if (tx) {
window.location = tx.landing_route;
return;
}
}
this.displayContent("payment.display_tx_list", render_values);
},
displayContent: function (xmlid, render_values) {
var html = core.qweb.render(xmlid, render_values);
$.unblockUI();
this.$el.find('div[name="o_payment_status_content"]').html(html);
},
displayLoading: function () {
var msg = _t("We are processing your payment, please wait ...");
$.blockUI({
'message': '<h2 class="text-white"><img src="/web/static/img/spin.png" class="fa-pulse"/>' +
' <br />' + msg +
'</h2>'
});
},
});
});