""" Симулятор вебхука Pally для локального тестирования. Использование: python tests/send_test_webhook.py --user-id 123456789 --amount 300 --bill-id 1 Предварительно: - API должен быть запущен: uvicorn api.main:app --port 8000 - Пользователь с --user-id должен существовать в БД - Счёт с --bill-id должен существовать в БД и принадлежать этому пользователю """ import argparse import hashlib import os import httpx from dotenv import load_dotenv load_dotenv() ENDPOINT = "/pally/result" def calculate_signature( out_sum: str, inv_id: str, pally_token: str, ) -> str: raw = f"{out_sum}:{inv_id}:{pally_token}" return hashlib.md5(raw.encode()).hexdigest().upper() def main(): parser = argparse.ArgumentParser() parser.add_argument("--user-id", type=int, required=True) parser.add_argument("--amount", type=int, required=True) parser.add_argument("--bill-id", type=int, required=True) parser.add_argument("--host", default="http://127.0.0.1:8000") parser.add_argument( "--status", default="SUCCESS", choices=["SUCCESS", "FAIL", "UNDERPAID", "OVERPAID"], help="Статус платежа (по умолчанию SUCCESS)", ) parser.add_argument( "--customer-pays-fees", action="store_true", help="Симулировать сценарий 'клиент платит комиссию'", ) args = parser.parse_args() pally_token = os.environ["PALLY_TOKEN"] # ИСПРАВЛЕНО: InvId должен содержать ID счета из БД (это order_id при создании счета) # Именно InvId используется в webhook handler для поиска счета в БД inv_id = str(args.bill_id) # InvId = bill ID из нашей БД (order_id при создании) trs_id = f"LXZv3R{args.bill_id}" # TrsId - уникальный идентификатор счета в Pally currency = "RUB" custom = None # custom field не используется в production (остается None) # Симуляция комиссии if args.customer_pays_fees: # Симулируем комиссию ~10% (как в вашем продакшене: 50 -> 55.4) commission_rate = 0.108 # ~10.8% gross_amount = args.amount * (1 + commission_rate) commission = gross_amount - args.amount print("customer pays fee:") print(f" Сумма заказа: {args.amount} RUB") print(f" Комиссия: {commission:.2f} RUB") print(f" Итого к оплате: {gross_amount:.2f} RUB") print(f" К зачислению: {args.amount} RUB") else: gross_amount = args.amount commission = 0.00 # Сигнатура рассчитывается с использованием InvId (который содержит bill ID) signature = calculate_signature(f"{gross_amount:.2f}", inv_id, pally_token) # Используем правильные имена полей согласно документации pally.info payload = { "InvId": inv_id, # Содержит ID счета из БД "OutSum": f"{gross_amount:.2f}", # Сумма, которую заплатил клиент (с комиссией) "Commission": f"{commission:.2f}", "TrsId": trs_id, "Status": args.status, "CurrencyIn": currency, "custom": custom, # None в production "SignatureValue": signature, } # Добавляем BalanceAmount только если клиент платит комиссию if args.customer_pays_fees: payload["BalanceAmount"] = str(args.amount) # Сумма к зачислению (без комиссии) payload["BalanceCurrency"] = currency print(f"{args.host}{ENDPOINT}") print(f"Отправляю вебхук: {payload}\n") response = httpx.post(f"{args.host}{ENDPOINT}", data=payload) print(f"Статус ответа: {response.status_code}") print(f"Тело ответа: {response.text}") if __name__ == "__main__": main()