From 429fd96c5c1ddf454c2e2cade450bd2b4aefe489 Mon Sep 17 00:00:00 2001 From: momegahed Date: Mon, 21 Nov 2022 08:46:26 +0000 Subject: [PATCH] [FIX] payment: use iso 4217 currency minor units in payments Steps to reproduce: 1- install ecommerce - payment_stripe 2- configure stripe testing credentials 3- edit main currency to have more decimal places (assume 4 instead of 2 for USD) 4- add product p to cart (assume p.price = 100 USD) 5- checkout with stripe 6- the price is 10,000 USD instead of 100 USD Bug: Stripe expects the amount in the smallest unit possible for the currency. https://stripe.com/docs/currencies#zero-decimal This differs from a currency to another according to https://en.wikipedia.org/wiki/ISO_4217#Minor_unit_fractions `to_minor_currency_units` uses the decimal places of the currency by default which will produce wrong values Fix: use currency exponent table built from https://en.wikipedia.org/wiki/ISO_4217#Minor_unit_fractions default behavior (for a currency that is not available) is not changed OPW-3058173 closes odoo/odoo#106936 X-original-commit: 703ed2b4e162ff0a7b621e3cf8b92b8eb7fddff8 Signed-off-by: Antoine Vandevenne (anv) --- addons/payment/const.py | 281 ++++++++++++++++++++++++++++++++++++++++ addons/payment/utils.py | 6 +- 2 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 addons/payment/const.py diff --git a/addons/payment/const.py b/addons/payment/const.py new file mode 100644 index 00000000000..42f8a0c6220 --- /dev/null +++ b/addons/payment/const.py @@ -0,0 +1,281 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +# According to https://en.wikipedia.org/wiki/ISO_4217#Minor_unit_fractions +CURRENCY_MINOR_UNITS = { + 'ADF': 2, + 'ADP': 0, + 'AED': 2, + 'AFA': 2, + 'AFN': 2, + 'ALL': 2, + 'AMD': 2, + 'ANG': 2, + 'AOA': 2, + 'AOK': 0, + 'AON': 0, + 'AOR': 0, + 'ARA': 2, + 'ARL': 2, + 'ARP': 2, + 'ARS': 2, + 'ATS': 2, + 'AUD': 2, + 'AWG': 2, + 'AYM': 0, + 'AZM': 2, + 'AZN': 2, + 'BAD': 2, + 'BAM': 2, + 'BBD': 2, + 'BDS': 2, + 'BDT': 2, + 'BEF': 2, + 'BGL': 2, + 'BGN': 2, + 'BHD': 3, + 'BIF': 0, + 'BMD': 2, + 'BND': 2, + 'BOB': 2, + 'BOP': 2, + 'BOV': 2, + 'BRB': 2, + 'BRC': 2, + 'BRE': 2, + 'BRL': 2, + 'BRN': 2, + 'BRR': 2, + 'BSD': 2, + 'BTN': 2, + 'BWP': 2, + 'BYB': 2, + 'BYN': 2, + 'BYR': 0, + 'BZD': 2, + 'CAD': 2, + 'CDF': 2, + 'CHC': 2, + 'CHE': 2, + 'CHF': 2, + 'CHW': 2, + 'CLF': 4, + 'CLP': 0, + 'CNH': 2, + 'CNT': 2, + 'CNY': 2, + 'COP': 2, + 'COU': 2, + 'CRC': 2, + 'CSD': 2, + 'CUC': 2, + 'CUP': 2, + 'CVE': 2, + 'CYP': 2, + 'CZK': 2, + 'DEM': 2, + 'DJF': 0, + 'DKK': 2, + 'DOP': 2, + 'DZD': 2, + 'ECS': 0, + 'ECV': 2, + 'EEK': 2, + 'EGP': 2, + 'ERN': 2, + 'ESP': 0, + 'ETB': 2, + 'EUR': 2, + 'FIM': 2, + 'FJD': 2, + 'FKP': 2, + 'FRF': 2, + 'GBP': 2, + 'GEK': 0, + 'GEL': 2, + 'GGP': 2, + 'GHC': 2, + 'GHP': 2, + 'GHS': 2, + 'GIP': 2, + 'GMD': 2, + 'GNF': 0, + 'GTQ': 2, + 'GWP': 2, + 'GYD': 2, + 'HKD': 2, + 'HNL': 2, + 'HRD': 2, + 'HRK': 2, + 'HTG': 2, + 'HUF': 2, + 'IDR': 2, + 'IEP': 2, + 'ILR': 2, + 'ILS': 2, + 'IMP': 2, + 'INR': 2, + 'IQD': 3, + 'IRR': 2, + 'ISJ': 2, + 'ISK': 0, + 'ITL': 0, + 'JEP': 2, + 'JMD': 2, + 'JOD': 3, + 'JPY': 0, + 'KES': 2, + 'KGS': 2, + 'KHR': 2, + 'KID': 2, + 'KMF': 0, + 'KPW': 2, + 'KRW': 0, + 'KWD': 3, + 'KYD': 2, + 'KZT': 2, + 'LAK': 2, + 'LBP': 2, + 'LKR': 2, + 'LRD': 2, + 'LSL': 2, + 'LTL': 2, + 'LTT': 2, + 'LUF': 2, + 'LVL': 2, + 'LVR': 2, + 'LYD': 3, + 'MAD': 2, + 'MAF': 2, + 'MCF': 2, + 'MDL': 2, + 'MGA': 2, + 'MGF': 0, + 'MKD': 2, + 'MMK': 2, + 'MNT': 2, + 'MOP': 2, + 'MRO': 2, + 'MRU': 2, + 'MTL': 2, + 'MUR': 2, + 'MVR': 2, + 'MWK': 2, + 'MXN': 2, + 'MXV': 2, + 'MYR': 2, + 'MZE': 2, + 'MZM': 2, + 'MZN': 2, + 'NAD': 2, + 'NGN': 2, + 'NIC': 2, + 'NIO': 2, + 'NIS': 2, + 'NLG': 2, + 'NOK': 2, + 'NPR': 2, + 'NTD': 2, + 'NZD': 2, + 'OMR': 3, + 'PAB': 2, + 'PEN': 2, + 'PES': 2, + 'PGK': 2, + 'PHP': 2, + 'PKR': 2, + 'PLN': 2, + 'PLZ': 2, + 'PRB': 2, + 'PTE': 0, + 'PYG': 0, + 'QAR': 2, + 'RHD': 2, + 'RMB': 2, + 'ROL': 0, + 'RON': 2, + 'RSD': 2, + 'RUB': 2, + 'RUR': 2, + 'RWF': 0, + 'SAR': 2, + 'SBD': 2, + 'SCR': 2, + 'SDD': 2, + 'SDG': 2, + 'SEK': 2, + 'SGD': 2, + 'SHP': 2, + 'SIT': 2, + 'SKK': 2, + 'SLE': 2, + 'SLL': 2, + 'SLS': 2, + 'SML': 0, + 'SOS': 2, + 'SRD': 2, + 'SRG': 2, + 'SSP': 2, + 'STD': 2, + 'STG': 2, + 'STN': 2, + 'SVC': 2, + 'SYP': 2, + 'SZL': 2, + 'THB': 2, + 'TJR': 0, + 'TJS': 2, + 'TMM': 2, + 'TMT': 2, + 'TND': 3, + 'TOP': 2, + 'TPE': 0, + 'TRL': 0, + 'TRY': 2, + 'TTD': 2, + 'TVD': 2, + 'TWD': 2, + 'TZS': 2, + 'UAH': 2, + 'UAK': 2, + 'UGX': 0, + 'USD': 2, + 'USN': 2, + 'USS': 2, + 'UYI': 0, + 'UYN': 2, + 'UYU': 2, + 'UYW': 4, + 'UZS': 2, + 'VAL': 0, + 'VEB': 2, + 'VED': 2, + 'VEF': 2, + 'VES': 2, + 'VND': 0, + 'VUV': 0, + 'WST': 2, + 'XAF': 0, + 'XCD': 2, + 'XEU': 0, + 'XOF': 0, + 'XPF': 0, + 'YER': 2, + 'YUD': 2, + 'YUG': 2, + 'YUM': 2, + 'YUN': 2, + 'YUO': 2, + 'YUR': 2, + 'ZAL': 2, + 'ZAR': 2, + 'ZMK': 2, + 'ZMW': 2, + 'ZRN': 2, + 'ZRZ': 2, + 'ZWB': 2, + 'ZWC': 2, + 'ZWD': 2, + 'ZWL': 2, + 'ZWN': 2, + 'ZWR': 2 +} diff --git a/addons/payment/utils.py b/addons/payment/utils.py index f66edcb9db1..a724984d239 100644 --- a/addons/payment/utils.py +++ b/addons/payment/utils.py @@ -7,6 +7,8 @@ from odoo.http import request from odoo.tools import consteq, float_round, ustr from odoo.tools.misc import hmac as hmac_tool +from odoo.addons.payment.const import CURRENCY_MINOR_UNITS + # Access token management @@ -87,7 +89,7 @@ def to_major_currency_units(minor_amount, currency, arbitrary_decimal_number=Non currency.ensure_one() if arbitrary_decimal_number is None: - decimal_number = currency.decimal_places + decimal_number = CURRENCY_MINOR_UNITS.get(currency.name, currency.decimal_places) else: decimal_number = arbitrary_decimal_number return float_round(minor_amount, precision_digits=0) / (10**decimal_number) @@ -113,7 +115,7 @@ def to_minor_currency_units(major_amount, currency, arbitrary_decimal_number=Non decimal_number = arbitrary_decimal_number else: currency.ensure_one() - decimal_number = currency.decimal_places + decimal_number = CURRENCY_MINOR_UNITS.get(currency.name, currency.decimal_places) return int(float_round(major_amount * (10**decimal_number), precision_digits=0))