From 0fe7fa0f95b7393d8829ba4b216d6c41d0c0dc3d Mon Sep 17 00:00:00 2001
From: "jerome hanke (jhk)" <jhk@odoo.com>
Date: Mon, 4 May 2020 13:01:12 +0000
Subject: [PATCH] [FIX] payment: float consistency enforcement in access_token
 generation

Steps to reproduce:
- install sales, ecommerce and payment_authorize
- setup authorize.net (test mode)
- go to sales and select the quotation S00007 (demo data) or create a quotation
with multiple items that add up to a float
- select action > generate a payment link > go to the link > select pay with authorize
- you are redirected to authorize.net use 4111 1111 1111 1111 as card number and 1223
as expiration date > pay
- you are redirected to the odoo payment process page
- wait for the result

Previous behavior:
the user is returned to a 404 error page but the payment went trough

Current behavior:
access_token generation is consistent and will not fail because of
float representation
the user is returned to the "payment confirmed" page

WARNINGS:
- watching the values in vscode prevents bug reproduction
- when setting up authorize.net, do not forget to add your test url to
the account's allowed return urls
- use https for authorize.net connection

opw-2223135

closes odoo/odoo#50577

Signed-off-by: mightyjol <jhk-odoo@users.noreply.github.com>
---
 addons/payment/controllers/portal.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/addons/payment/controllers/portal.py b/addons/payment/controllers/portal.py
index 823b254879aa..23a6fe3b69ed 100644
--- a/addons/payment/controllers/portal.py
+++ b/addons/payment/controllers/portal.py
@@ -11,6 +11,7 @@ import werkzeug
 from odoo import http, _
 from odoo.http import request
 from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, consteq, ustr
+from odoo.tools.float_utils import float_repr
 from datetime import datetime, timedelta
 
 
@@ -262,7 +263,7 @@ class WebsitePayment(http.Controller):
         values['reference'] = request.env['payment.transaction']._compute_reference(values=reference_values, prefix=reference)
         tx = request.env['payment.transaction'].sudo().with_context(lang=None).create(values)
         secret = request.env['ir.config_parameter'].sudo().get_param('database.secret')
-        token_str = '%s%s%s' % (tx.id, tx.reference, tx.amount)
+        token_str = '%s%s%s' % (tx.id, tx.reference, float_repr(tx.amount, precision_digits=tx.currency_id.decimal_places))
         token = hmac.new(secret.encode('utf-8'), token_str.encode('utf-8'), hashlib.sha256).hexdigest()
         tx.return_url = '/website_payment/confirm?tx_id=%d&access_token=%s' % (tx.id, token)
 
@@ -304,7 +305,7 @@ class WebsitePayment(http.Controller):
         try:
             tx.s2s_do_transaction()
             secret = request.env['ir.config_parameter'].sudo().get_param('database.secret')
-            token_str = '%s%s%s' % (tx.id, tx.reference, tx.amount)
+            token_str = '%s%s%s' % (tx.id, tx.reference, float_repr(tx.amount, precision_digits=tx.currency_id.decimal_places))
             token = hmac.new(secret.encode('utf-8'), token_str.encode('utf-8'), hashlib.sha256).hexdigest()
             tx.return_url = return_url or '/website_payment/confirm?tx_id=%d&access_token=%s' % (tx.id, token)
         except Exception as e:
@@ -319,7 +320,7 @@ class WebsitePayment(http.Controller):
             if access_token:
                 tx = request.env['payment.transaction'].sudo().browse(tx_id)
                 secret = request.env['ir.config_parameter'].sudo().get_param('database.secret')
-                valid_token_str = '%s%s%s' % (tx.id, tx.reference, tx.amount)
+                valid_token_str = '%s%s%s' % (tx.id, tx.reference, float_repr(tx.amount, precision_digits=tx.currency_id.decimal_places))
                 valid_token = hmac.new(secret.encode('utf-8'), valid_token_str.encode('utf-8'), hashlib.sha256).hexdigest()
                 if not consteq(ustr(valid_token), access_token):
                     raise werkzeug.exceptions.NotFound
-- 
GitLab