A payment field should state its decimal separator and digit rules instead of inheriting process-wide locale.
Make this comfortable
Python amount parsing: define a wire format before Decimal
Operation contract
The parser accepts a nonnegative canonical amount with exactly two ASCII fractional digits. It returns Decimal for 47.25 and rejects a comma-formatted value. The rule is explicit and independent of the machine's current locale setting.
Failure boundary
A canonical API field is not a localized display string. If users enter localized amounts, parse under a deliberate locale-specific interface before converting to the canonical contract. Do not call setlocale for one request in a multi-threaded server; it changes process-wide state. Currency scale and maximum amount remain separate business rules.
Working program
from decimal import Decimal
ASCII_DIGITS = set("0123456789")
def parse_payment_amount(raw_amount):
whole, separator, cents = raw_amount.partition(".")
if (separator != "." or not whole or len(cents) != 2
or (len(whole) > 1 and whole.startswith("0"))
or any(character not in ASCII_DIGITS for character in whole + cents)):
raise ValueError("amount must use canonical decimal format")
return Decimal(raw_amount)
print("accepted", parse_payment_amount("47.25"))
try:
parse_payment_amount("47,25")
except ValueError:
print("comma_rejected", True)Output
accepted 47.25
comma_rejected TrueCosts and limits
Validation scans O(n) characters before Decimal conversion and retains an O(n) numeric representation. A separate maximum-length check is needed for hostile input.
Common Mistakes
- Do not use the server locale as a payment wire format.
- Decimal does not decide which localized input shape is allowed.
- A syntax check does not enforce currency scale or maximum value.
Connected lessons
python
locale-independent-money-input
