"""Контрольная сумма запросов к CRM API (см. документацию "My CRM API 1.1").""" from __future__ import annotations import hashlib from typing import Any def _collect_values_sorted(obj: Any) -> list[Any]: """Рекурсивно сортирует ключи на каждом уровне и собирает значения depth-first.""" values: list[Any] = [] if isinstance(obj, dict): for key in sorted(obj.keys()): values.extend(_collect_values_sorted(obj[key])) elif isinstance(obj, list): for item in obj: values.extend(_collect_values_sorted(item)) else: values.append(obj) return values def _to_str(value: Any) -> str: # Булевы значения CRM ожидает в контрольной сумме как "1"/"" (PHP-style # truthy-приведение) — это нигде не задокументировано и подобрано опытным # путём: "True"/"False" и "true"/"false" оба дают "Checksum Error". if isinstance(value, bool): return "1" if value else "" return str(value) def compute_md5sum(payload: dict[str, Any], secret_key: str) -> str: """Считает md5sum по алгоритму из документации CRM: отсортировать все ключи (включая вложенные), конкатенировать все значения, добавить приватный ключ, взять MD5. """ values = _collect_values_sorted(payload) concatenated = "".join(_to_str(v) for v in values) return hashlib.md5((concatenated + secret_key).encode("utf-8")).hexdigest()