diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 43ef948a626..40098f5f16e 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -88,7 +88,8 @@ class StockMove(models.Model): @api.multi def action_cancel(self): if any(move.quantity_done for move in self): #TODO: either put in stock, or check there is a production order related to it - raise exceptions.UserError(_('You cannot cancel a stock move having already consumed material')) + raise exceptions.UserError(_('You cannot cancel a manufacturing order if you have already consumed material.\ + If you want to cancel this MO, please change the consumed quantities to 0.')) return super(StockMove, self).action_cancel() @api.multi diff --git a/addons/payment/controllers/portal.py b/addons/payment/controllers/portal.py index 93d33151d0a..5b170662f0e 100644 --- a/addons/payment/controllers/portal.py +++ b/addons/payment/controllers/portal.py @@ -18,16 +18,9 @@ class WebsitePayment(http.Controller): } return_url = request.params.get('redirect', '/my/payment_method') for acquirer in acquirers: - acquirer.form = acquirer.sudo()._registration_render(request.env.user.partner_id.id, {'error': {}, 'error_message': [], 'return_url': return_url, 'json': False, 'bootstrap_formatting': True, 'verify_validity': True}) + acquirer.form = acquirer.sudo()._registration_render(request.env.user.partner_id.id, {'error': {}, 'error_message': [], 'return_url': return_url, 'json': False, 'bootstrap_formatting': True}) return request.render("payment.pay_methods", values) - @http.route(['/website_payment/delete/'], methods=['POST'], type='http', auth="user", website=True) - def delete(self, delete_pm_id=None): - if delete_pm_id: - pay_meth = request.env['payment.token'].browse(int(delete_pm_id)) - pay_meth.unlink() - return request.redirect('/my/payment_method') - @http.route(['/website_payment/pay'], type='http', auth='public', website=True) def pay(self, reference='', amount=False, currency_id=None, acquirer_id=None, **kw): env = request.env @@ -90,25 +83,3 @@ class WebsitePayment(http.Controller): return request.render('payment.confirm', {'tx': tx, 'status': status, 'message': message}) else: return request.redirect('/my/home') - - @http.route(['/website_payment/get_linked_records'], type='json', auth='user', website=True) - def get_linked_records(self, payment_token_id): - payment_token_id = int(payment_token_id) - - res = [] - token = request.env['payment.token'].search([('id','=',payment_token_id)]) - # if the payment token is valid - if token: - # then we retrieve the description for the model sale.subscription - subscription_description = request.env['sale.subscription']._description - # we retrieve all the linked records for the token - records = token._get_linked_records() - # transform the list of tuple into a dict so we can search easily - records = dict(records[payment_token_id]) - # if there's subscriptions linked to that token - if records.get(subscription_description): - subscriptions = records[subscription_description] - # we create our json with all the info needed - for r in subscriptions: - res.append({'id': r.id, 'name': r.name}) - return res \ No newline at end of file diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index 80c84094c30..623676c7a9c 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -786,7 +786,7 @@ class PaymentToken(models.Model): return False reference = "VALIDATION-%s-%s" % (self.id, datetime.datetime.now().strftime('%y%m%d_%H%M%S')) - tx = self.env['payment.transaction'].create({ + tx = self.env['payment.transaction'].sudo().create({ 'amount': amount, 'acquirer_id': self.acquirer_id.id, 'type': 'validation', @@ -816,6 +816,17 @@ class PaymentToken(models.Model): token.short_name = token.name.replace('XXXXXXXXXXXX', '***') @api.multi - def _get_linked_records(self): - """ Returns the list of records currently using the records in self as payment token. """ - return {} \ No newline at end of file + def get_linked_records(self): + """ This method returns a dict containing all the records linked to the payment.token (e.g Subscriptions), + the key is the id of the payment.token and the value is an array that must follow the scheme below. + + { + token_id: [ + 'description': The model description (e.g 'Sale Subscription'), + 'id': The id of the record, + 'name': The name of the record, + 'url': The url to access to this record. + ] + } + """ + return {r.id:[] for r in self} diff --git a/addons/payment/static/src/css/portal_payment.css b/addons/payment/static/src/css/portal_payment.css index 1892700fdd0..e9db1fd8d28 100644 --- a/addons/payment/static/src/css/portal_payment.css +++ b/addons/payment/static/src/css/portal_payment.css @@ -8,7 +8,7 @@ div.card_placeholder { width: 32px; height: 20px; position: absolute; - top: 34px; + top: 8px; right: 20px; -webkit-transition: 0.4s cubic-bezier(0.455, 0.03, 0.515, 0.955); transition: 0.4s cubic-bezier(0.455, 0.03, 0.515, 0.955); diff --git a/addons/payment/static/src/css/portal_payment.sass b/addons/payment/static/src/css/portal_payment.sass index a396bae99c1..e7de8c55cd5 100644 --- a/addons/payment/static/src/css/portal_payment.sass +++ b/addons/payment/static/src/css/portal_payment.sass @@ -10,7 +10,7 @@ div.card_placeholder width: 32px height: 20px position: absolute - top: 34px + top: 8px right: 20px -webkit-transition: .4s cubic-bezier(0.455,0.03,0.515,0.955) transition: .4s cubic-bezier(0.455,0.03,0.515,0.955) diff --git a/addons/payment/static/src/js/payment_form.js b/addons/payment/static/src/js/payment_form.js new file mode 100644 index 00000000000..ae2d44c1cb7 --- /dev/null +++ b/addons/payment/static/src/js/payment_form.js @@ -0,0 +1,261 @@ +odoo.define('payment.payment_form', function (require){ + "use strict"; + + var ajax = require('web.ajax'); + var core = require('web.core'); + var Dialog = require("web.Dialog"); + var Widget = require("web.Widget"); + var rpc = require("web.rpc"); + var _t = core._t; + + var PaymentForm = Widget.extend({ + + events: { + 'click #o_payment_form_pay': 'payEvent', + 'click #o_payment_form_add_pm': 'addPmEvent', + 'click button[name="delete_pm"]': 'deletePmEvent', + 'click input[type="radio"]': 'radioClickEvent' + }, + + payEvent: function(ev) { + ev.stopPropagation(); + ev.preventDefault(); + var form = this.el; + var checked_radio = this.$('input[type="radio"]:checked'); + var self = this; + var button = ev.target; + + // first we check that the user has selected a payment method + if(checked_radio.length == 1) { + checked_radio = checked_radio[0]; + // if the user is adding a new payment + if(this.isNewPaymentRadio(checked_radio)) { + var acquirer_id = this.getAcquirerIdFromRadio(checked_radio); + var acquirer_form = this.$('#o_payment_add_token_acq_' + acquirer_id); + // we retrieve all the input inside the acquirer form and 'serialize' them to an indexed array + var form_data = this.getFormData($('input', acquirer_form)); + var ds = $('input[name="data_set"]', acquirer_form)[0]; + $(button).attr('disabled', true); + $(button).prepend(' '); + + var verify_validity = this.$el.find('input[name="verify_validity"]'); + + if(verify_validity.length>0) + form_data.verify_validity = verify_validity[0].value === "1"; + + // do the call to the route stored in the 'data_set' input of the acquirer form, the data must be called 'create-route' + ajax.jsonRpc(ds.dataset.createRoute, 'call', form_data).then(function(data) { + // if the server has returned true + if(data.result) { + // and it need a 3DS authentication + if(data['3d_secure'] !== false) { + // then we display the 3DS page to the user + $("body").html(data['3d_secure']); + } + else { + checked_radio.value = data.id; // set the radio value to the new card id + form.submit(); + } + } + // if the server has returned false, we display an error + else { + self.error(_t('Error'), + _t("

We are not able to add your payment method at the moment.

")); + } + // here we remove the 'processing' icon from the 'add a new payment' button + $(button).attr('disabled', false); + $(button).find('span.o_loader').remove(); + }).fail(function(message, data) { + // if the rpc fails, pretty obvious + $(button).attr('disabled', false); + $(button).find('span.o_loader').remove(); + + self.error(_t('Error'), + _t("

We are not able to add your payment method at the moment.

") + (core.debug ? data.data.message : '')); + }); + } + // if the user is using an old payment + else { + // then we just submit the form + form.submit(); + } + } + else { + this.error(_t('Error'), + _t('

Please select a payment method

')); + } + }, + // event handler when clicking on the button to add a new payment method + addPmEvent: function(ev) { + ev.stopPropagation(); + ev.preventDefault(); + var form = this.el; + var checked_radio = this.$('input[type="radio"]:checked'); + var self = this; + var button = ev.target; + + // we check if the user has selected a 'add a new payment' option + if(checked_radio.length == 1 && this.isNewPaymentRadio(checked_radio[0])) { + // then we add a 'processing' icon into the 'add a new payment' button + $(button).attr('disabled', true); + $(button).prepend(' '); + // we retrieve which acquirer is used + checked_radio = checked_radio[0]; + var acquirer_id = this.getAcquirerIdFromRadio(checked_radio); + var acquirer_form = this.$('#o_payment_add_token_acq_' + acquirer_id); + // we retrieve all the input inside the acquirer form and 'serialize' them to an indexed array + var form_data = this.getFormData($('input', acquirer_form)); + var ds = $('input[name="data_set"]', acquirer_form)[0]; + + // we force the check when adding a card trough here + form_data.verify_validity = true; + + // do the call to the route stored in the 'data_set' input of the acquirer form, the data must be called 'create-route' + ajax.jsonRpc(ds.dataset.createRoute, 'call', form_data).then(function(data) { + // if the server has returned true + if(data.result) { + // and it need a 3DS authentication + if(data['3d_secure'] !== false) { + // then we display the 3DS page to the user + $("body").html(data['3d_secure']); + } + // if it doesn't require 3DS + else { + // we just go to the return_url or reload the page + if(form_data.return_url) { + window.location = form_data.return_url; + } + else { + window.location.reload(); + } + } + } + // if the server has returned false, we display an error + else { + self.error(_t('Error'), + _t("

We are not able to add your payment method at the moment.

")); + } + // here we remove the 'processing' icon from the 'add a new payment' button + $(button).attr('disabled', false); + $(button).find('span.o_loader').remove(); + }).fail(function(message, data) { + // if the rpc fails, pretty obvious + $(button).attr('disabled', false); + $(button).find('span.o_loader').remove(); + + self.error(_t('Error'), + _t("

We are not able to add your payment method at the moment.

") + (core.debug ? data.data.message : '')); + }); + } + else { + this.error(_t('Error'), + _t('

Please select the option to add a new payment method

')); + } + }, + // event handler when clicking on a button to delete a payment method + deletePmEvent: function (ev) { + ev.stopPropagation(); + ev.preventDefault(); + var self = this; + var pm_id = parseInt(ev.target.value); + + var Delete = function() { + rpc.query({ + model: 'payment.token', + method: 'unlink', + args: [pm_id], + }) + .then(function(result){ + if(result === true) { + ev.target.closest('div').remove(); + } + },function(type,err){ + self.error(_t('Error'), + _t("

We are not able to delete your payment method at the moment.

")); + }); + }; + + rpc.query({ + model: 'payment.token', + method: 'get_linked_records', + args: [pm_id], + }).then(function(result){ + if(result[pm_id].length > 0) { + // if there's records linked to this payment method + var content = ''; + result[pm_id].forEach(function(sub) { + content += '

' + _.str.escapeHTML(sub.name) + '

'; + }); + + content = $('

').html(_t('

This card is currently linked to the following records:

') + content); + // Then we display the list of the records and ask the user if he really want to remove the ppayment method. + new Dialog(self, { + title: _t('Warning!'), + size: 'medium', + $content: content, + buttons: [ + {text: _t('Confirm Deletion'), classes: 'btn-primary', close: true, click: Delete}, + {text: _t('Cancel'), close: true}]}).open(); + } + else { + // if there's no records linked to this payment method, then we delete it + Delete(); + } + }, function(type, err){ + self.error(_t('Error'), + _t("

We are not able to delete your payment method at the moment.

") + (core.debug ? err.data.message : '')); + }); + }, + // event handler when clicking on a radio button + radioClickEvent: function(ev) { + this.updateNewPaymentDisplayStatus(); + }, + updateNewPaymentDisplayStatus: function() + { + var checked_radio = this.$('input[type="radio"]:checked'); + // we hide all the acquirers form + this.$('[id*="o_payment_add_token_acq_"]').addClass('hidden'); + // if we clicked on an add new payment radio + if(checked_radio.length == 1 && this.isNewPaymentRadio(checked_radio[0])) + { + // then we retrieve the acquirer name + var acquirer_id = this.getAcquirerIdFromRadio(checked_radio[0]); + // and display its form + this.$('#o_payment_add_token_acq_' + acquirer_id).removeClass('hidden'); + } + }, + isNewPaymentRadio: function(element) { + return $(element).data('acquirer-id') !== undefined; + }, + getAcquirerIdFromRadio: function(element) { + return $(element).data('acquirer-id'); + }, + error: function(title, message, url) { + return new Dialog(null, { + title: title || "", + $content: $(core.qweb.render('website.error_dialog', { + message: message || "", + })), + }).open(); + }, + getFormData: function($form){ + var unindexed_array = $form.serializeArray(); + var indexed_array = {}; + + $.map(unindexed_array, function(n, i){ + indexed_array[n.name] = n.value; + }); + return indexed_array; + }, + start: function() { + this.updateNewPaymentDisplayStatus(); + }, + }); + + + + $(document).ready(function (){ + var form = new PaymentForm(); + form.attachTo($('.o_payment_form')); + }); +}); diff --git a/addons/payment/static/src/js/payment_portal.js b/addons/payment/static/src/js/payment_portal.js index 74b4cb95282..d7397f1b04d 100644 --- a/addons/payment/static/src/js/payment_portal.js +++ b/addons/payment/static/src/js/payment_portal.js @@ -60,64 +60,3 @@ $(document).ready(function () { }); }); - - -odoo.define('payment.payment_acquirer', function (require) { -"use strict"; - -var ajax = require('web.ajax'); -var Dialog = require('web.Dialog'); - -$(document).ready(function () { - - if( !$('#payment_tokens_list').length ) { - return $.Deferred().reject("DOM doesn't contain '.payment_tokens_list'"); - } - - var payment_methods = $('#payment_tokens_list'); - - payment_methods.find('button[name="delete_pm_id"]').on('click', function(ev){ - ev.stopPropagation(); - ev.preventDefault(); - var self = this; - - // we retrieve the selected payment method id - var payment_method_id = ev.target.value; - - var Delete = function() - { - payment_methods.find('button[name="delete_pm_id"]').off("click"); - ev.target.click(); - }; - - ajax.jsonRpc('/website_payment/get_linked_records', 'call', { - 'payment_token_id': payment_method_id, - }).done(function (response) { - // if there's records linked to the payment method we're trying to delete - if(response.length > 0) - { - var content = ''; - - response.forEach(function(sub) { - content += '

' + sub.name + '

'; - }); - - content = $('

').html('

This card is currently linked to the following records:

' + content); - // Then we display the list of the records and ask the user if he really want to remove the ppayment method. - new Dialog(self, { - title: 'Warning!', - size: 'medium', - $content: content, - buttons: [ - {text: 'Confirm Deletion', classes: 'btn-primary', close: true, click: Delete}, - {text: 'Cancel', close: true}]}).open(); - } - // otherwise if the user has no records linked to this payment method, then we just delete it. - else - { - Delete(); - } - }); - }); -}); -}); diff --git a/addons/payment/static/src/less/payment_form.less b/addons/payment/static/src/less/payment_form.less new file mode 100644 index 00000000000..ce6b2aeab6e --- /dev/null +++ b/addons/payment/static/src/less/payment_form.less @@ -0,0 +1,32 @@ +.o_payment_form { + label > input[type="radio"]{ + vertical-align: bottom; + margin-right: 5px; + } + + label { + font-size: 14px; + font-weight: normal !important; + font-family: Helvetica Neue, sans-serif; + line-height: 1.3em; + color: #4d4d4d; + } + + .panel-body:first-child { + border-top: 0px; + } + + .panel { + border-radius: 10px; + } + + .panel-footer:last-child { + border-bottom-right-radius: 10px !important; + border-bottom-left-radius: 10px !important; + } + + .panel-body { + border-top: 1px solid #ddd; + padding: 1.14em !important; + } +} \ No newline at end of file diff --git a/addons/payment/views/assets.xml b/addons/payment/views/assets.xml index da3d93ee378..2c1c6e5a072 100644 --- a/addons/payment/views/assets.xml +++ b/addons/payment/views/assets.xml @@ -7,6 +7,8 @@ + + - \ No newline at end of file + diff --git a/addons/payment/views/payment_portal_templates.xml b/addons/payment/views/payment_portal_templates.xml index c898a7374a3..3c8a5e3f5d3 100644 --- a/addons/payment/views/payment_portal_templates.xml +++ b/addons/payment/views/payment_portal_templates.xml @@ -1,45 +1,28 @@ - diff --git a/addons/payment_ogone/controllers/main.py b/addons/payment_ogone/controllers/main.py index 503f1de1419..9c81761a70e 100644 --- a/addons/payment_ogone/controllers/main.py +++ b/addons/payment_ogone/controllers/main.py @@ -33,6 +33,40 @@ class OgoneController(http.Controller): new_id = request.env['payment.acquirer'].browse(int(kwargs.get('acquirer_id'))).s2s_process(kwargs) return new_id.id + @http.route(['/payment/ogone/s2s/create_json_3ds'], type='json', auth='public', csrf=False) + def ogone_s2s_create_json_3ds(self, verify_validity=False, **kwargs): + token = request.env['payment.acquirer'].browse(int(kwargs.get('acquirer_id'))).s2s_process(kwargs) + + if not token: + res = { + 'result': False, + } + return res + + res = { + 'result': True, + 'id': token.id, + 'short_name': token.short_name, + '3d_secure': False, + 'verified': False, + } + + if verify_validity != False: + baseurl = request.env['ir.config_parameter'].sudo().get_param('web.base.url') + params = { + 'accept_url': baseurl + '/payment/ogone/validate/accept', + 'decline_url': baseurl + '/payment/ogone/validate/decline', + 'exception_url': baseurl + '/payment/ogone/validate/exception', + 'return_url': kwargs.get('return_url', baseurl) + } + tx = token.validate(**params) + res['verified'] = token.verified + + if tx and tx.html_3ds: + res['3d_secure'] = tx.html_3ds + + return res + @http.route(['/payment/ogone/s2s/create'], type='http', auth='public', methods=["POST"], csrf=False) def ogone_s2s_create(self, **post): error = '' diff --git a/addons/payment_ogone/models/payment.py b/addons/payment_ogone/models/payment.py index 0702d38c8bb..9ac90f9478f 100644 --- a/addons/payment_ogone/models/payment.py +++ b/addons/payment_ogone/models/payment.py @@ -302,10 +302,11 @@ class PaymentTxOgone(models.Model): }) vals.update(payment_token_id=pm.id) self.write(vals) - self.payment_token_id.verified = True + if self.payment_token_id: + self.payment_token_id.verified = True self.execute_callback() # if this transaction is a validation one, then we refund the money we just withdrawn - if self.type == 'verification': + if self.type == 'validation': self.s2s_do_refund() return True @@ -454,7 +455,8 @@ class PaymentTxOgone(models.Model): 'name': tree.get('CARDNO'), }) self.write({'payment_token_id': pm.id}) - self.payment_token_id.verified = True + if self.payment_token_id: + self.payment_token_id.verified = True self.execute_callback() return True elif status in self._ogone_cancel_tx_status: @@ -463,7 +465,7 @@ class PaymentTxOgone(models.Model): 'acquirer_reference': tree.get('PAYID'), }) elif status in self._ogone_pending_tx_status: - new_state = 'refunding' if self.type == 'validation' else 'pending' + new_state = 'refunding' if self.state == 'refunding' else 'pending' self.write({ 'state': new_state, 'acquirer_reference': tree.get('PAYID'), diff --git a/addons/payment_ogone/views/payment_ogone_templates.xml b/addons/payment_ogone/views/payment_ogone_templates.xml index 63b4af07142..c70d461d0a7 100644 --- a/addons/payment_ogone/views/payment_ogone_templates.xml +++ b/addons/payment_ogone/views/payment_ogone_templates.xml @@ -54,40 +54,27 @@ - diff --git a/addons/payment_stripe/controllers/main.py b/addons/payment_stripe/controllers/main.py index b4627157a9e..55cad84fd9d 100644 --- a/addons/payment_stripe/controllers/main.py +++ b/addons/payment_stripe/controllers/main.py @@ -15,7 +15,7 @@ class StripeController(http.Controller): def stripe_s2s_create_json(self, **kwargs): acquirer_id = int(kwargs.get('acquirer_id')) acquirer = request.env['payment.acquirer'].browse(acquirer_id) - return acquirer.s2s_process(kwargs) + return acquirer.s2s_process(kwargs).id @http.route(['/payment/stripe/s2s/create'], type='http', auth='public') def stripe_s2s_create(self, **post): @@ -24,6 +24,30 @@ class StripeController(http.Controller): acquirer.s2s_process(post) return werkzeug.utils.redirect(post.get('return_url', '/')) + @http.route(['/payment/stripe/s2s/create_json_3ds'], type='json', auth='public', csrf=False) + def stripe_s2s_create_json_3ds(self, verify_validity=False, **kwargs): + token = request.env['payment.acquirer'].browse(int(kwargs.get('acquirer_id'))).s2s_process(kwargs) + + if not token: + res = { + 'result': False, + } + return res + + res = { + 'result': True, + 'id': token.id, + 'short_name': token.short_name, + '3d_secure': False, + 'verified': False, + } + + if verify_validity != False: + token.validate() + res['verified'] = token.verified + + return res + @http.route(['/payment/stripe/create_charge'], type='json', auth='public') def stripe_create_charge(self, **post): """ Create a payment transaction diff --git a/addons/payment_stripe/models/payment.py b/addons/payment_stripe/models/payment.py index e43e0cbd71f..1bb4ea9aad8 100644 --- a/addons/payment_stripe/models/payment.py +++ b/addons/payment_stripe/models/payment.py @@ -71,7 +71,7 @@ class PaymentAcquirerStripe(models.Model): 'acquirer_id': int(data['acquirer_id']), 'partner_id': int(data['partner_id']) }) - return payment_token.id + return payment_token @api.multi def stripe_s2s_form_validate(self, data): @@ -83,6 +83,21 @@ class PaymentAcquirerStripe(models.Model): return False return True + def _get_feature_support(self): + """Get advanced feature support by provider. + + Each provider should add its technical in the corresponding + key for the following features: + * fees: support payment fees computations + * authorize: support authorizing payment (separates + authorization and capture) + * tokenize: support saving payment data in a payment.tokenize + object + """ + res = super(PaymentAcquirerStripe, self)._get_feature_support() + res['tokenize'].append('stripe') + return res + class PaymentTransactionStripe(models.Model): _inherit = 'payment.transaction' @@ -112,6 +127,29 @@ class PaymentTransactionStripe(models.Model): result = self._create_stripe_charge(acquirer_ref=self.payment_token_id.acquirer_ref) return self._stripe_s2s_validate_tree(result) + + def _create_stripe_refund(self): + api_url_refund = 'https://%s/refunds' % (self.acquirer_id._get_stripe_api_url()) + + refund_params = { + 'charge': self.acquirer_reference, + 'amount': int(self.amount*100), # by default, stripe refund the full amount (we don't really need to specify the value) + 'metadata[reference]': self.reference, + } + + r = requests.post(api_url_refund, + auth=(self.acquirer_id.stripe_secret_key, ''), + params=refund_params, + headers=STRIPE_HEADERS) + return r.json() + + @api.multi + def stripe_s2s_do_refund(self, **kwargs): + self.ensure_one() + self.state = 'refunding' + result = self._create_stripe_refund() + return self._stripe_s2s_validate_tree(result) + @api.model def _stripe_form_get_tx_from_data(self, data): """ Given a data dict coming from stripe, verify it and find the related @@ -135,18 +173,21 @@ class PaymentTransactionStripe(models.Model): @api.multi def _stripe_s2s_validate_tree(self, tree): self.ensure_one() - if self.state not in ('draft', 'pending'): + if self.state not in ('draft', 'pending', 'refunding'): _logger.info('Stripe: trying to validate an already validated tx (ref %s)', self.reference) return True status = tree.get('status') if status == 'succeeded': + new_state = 'refunded' if self.state == 'refunding' else 'done' self.write({ - 'state': 'done', + 'state': new_state, 'date_validate': fields.datetime.now(), 'acquirer_reference': tree.get('id'), }) self.execute_callback() + if self.payment_token_id: + self.payment_token_id.verified = True return True else: error = tree['error']['message'] diff --git a/addons/payment_stripe/views/payment_stripe_templates.xml b/addons/payment_stripe/views/payment_stripe_templates.xml index 9e8e28c4d07..8f7f8edd0eb 100644 --- a/addons/payment_stripe/views/payment_stripe_templates.xml +++ b/addons/payment_stripe/views/payment_stripe_templates.xml @@ -32,38 +32,29 @@