Skip to content

Python

import hashlib
import hmac
import json

import requests

# 1. Build the body ONCE
payload = json.dumps(request_data)

# 2. Sign that exact string -> lowercase hex
signature = hmac.new(
    sign_key.encode("utf-8"),
    payload.encode("utf-8"),
    hashlib.sha256,
).hexdigest()

# 3. Send the SAME string you signed.
#    Use data=..., NOT json=... — json= re-serializes and breaks the signature.
response = requests.post(
    "https://api.napoleonpay.com/api/v1/payments",
    data=payload.encode("utf-8"),
    headers={
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}",
        "Signature": signature,
    },
)

Python

json.dumps(..., indent=4) matches on indentation and \uXXXX escaping, but renders 100.0 instead of 100 and keeps {} — both are corrected below.

import hashlib
import hmac
import json


def _canonicalise(value):
    if isinstance(value, bool):
        return value
    if isinstance(value, float) and value.is_integer():
        return int(value)                       # 100.0 -> 100
    if isinstance(value, dict):
        return {k: _canonicalise(v) for k, v in value.items()} if value else []
    if isinstance(value, list):
        return [_canonicalise(v) for v in value]
    return value


def verify_callback(raw_body: str, signature_header: str, sign_key: str) -> bool:
    canonical = json.dumps(_canonicalise(json.loads(raw_body)), indent=4)
    expected = hmac.new(
        sign_key.encode("utf-8"),
        canonical.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header or "")