Files
Solo_bot/handlers/payments/robokassa/service.py
T

88 lines
3.1 KiB
Python

import hashlib
import json
import uuid
from decimal import ROUND_DOWN, Decimal
from urllib.parse import quote_plus, urlencode
from sqlalchemy.ext.asyncio import AsyncSession
from config import ROBOKASSA_LOGIN, ROBOKASSA_PASSWORD1, ROBOKASSA_PASSWORD2, ROBOKASSA_TEST_MODE
from database import add_payment
def _build_receipt(amount: float, sno: str = "usn_income") -> dict:
return {
"items": [
{
"name": "Пополнение баланса",
"quantity": 1,
"sum": float(amount),
"payment_method": "full_payment",
"payment_object": "payment",
"tax": "none",
}
],
"sno": sno,
}
def _format_amount(amount: float | int) -> str:
s = str(Decimal(str(amount)).quantize(Decimal("0.01"), rounding=ROUND_DOWN))
return s.rstrip("0").rstrip(".") if "." in s else s
def generate_payment_link(amount: int | float, inv_id: int, description: str, tg_id: int) -> tuple[str, str]:
out_sum = _format_amount(amount)
receipt_json = json.dumps(_build_receipt(amount), ensure_ascii=False, separators=(",", ":"))
receipt_enc = quote_plus(receipt_json, safe="")
pid = str(uuid.uuid4())
shp = {"Shp_id": str(tg_id), "Shp_pid": pid}
base = f"{ROBOKASSA_LOGIN}:{out_sum}:{inv_id}:{receipt_enc}:{ROBOKASSA_PASSWORD1}"
for k in sorted(shp.keys(), key=str.lower):
base += f":{k}={shp[k]}"
signature = hashlib.md5(base.encode("utf-8")).hexdigest().upper()
query = {
"MrchLogin": ROBOKASSA_LOGIN,
"OutSum": out_sum,
"InvId": inv_id,
"Description": description,
"Receipt": receipt_enc,
"SignatureValue": signature,
**shp,
}
if ROBOKASSA_TEST_MODE:
query["IsTest"] = 1
return "https://auth.robokassa.ru/Merchant/Index.aspx?" + urlencode(query), pid
async def create_and_store_robokassa_payment(
session: AsyncSession, tg_id: int, amount: int | float, description: str, inv_id: int = 0
) -> tuple[str, str]:
url, pid = generate_payment_link(amount, inv_id, description, tg_id)
await add_payment(
session=session,
tg_id=tg_id,
amount=float(amount),
payment_system="robokassa",
status="pending",
currency="RUB",
payment_id=pid,
metadata=None,
)
return url, pid
def check_payment_signature(params) -> bool:
out_sum = params.get("OutSum") or params.get("out_summ") or params.get("outsumm")
inv_id = params.get("InvId") or params.get("inv_id") or params.get("invid")
received_sig = (params.get("SignatureValue") or params.get("signaturevalue") or "").upper()
if not out_sum or not inv_id or not received_sig:
return False
shp_items = [(k, params[k]) for k in params.keys() if k.lower().startswith("shp_")]
shp_items.sort(key=lambda kv: kv[0].lower())
shp_suffix = "".join(f":{k}={v}" for k, v in shp_items)
base = f"{out_sum}:{inv_id}:{ROBOKASSA_PASSWORD2}{shp_suffix}"
expected_sig = hashlib.md5(base.encode("utf-8")).hexdigest().upper()
return received_sig == expected_sig