diff --git a/backend/elena_app.py b/backend/elena_app.py index f186ebc..72e2007 100644 --- a/backend/elena_app.py +++ b/backend/elena_app.py @@ -49,6 +49,18 @@ def _key(): env = open("/opt/zashita-api/.env").read() return re.search(r'ANTHROPIC_API_KEY=(\S+)', env).group(1) +def _yookassa_creds(): + """shopId и secret из .env. Если нет — None (демо-режим).""" + try: + env = open(os.path.join(BASE, ".env")).read() + sid = re.search(r'YOOKASSA_SHOP_ID=(\S+)', env) + sec = re.search(r'YOOKASSA_SECRET=(\S+)', env) + if sid and sec: + return sid.group(1), sec.group(1) + except Exception: + pass + return None, None + client = anthropic.Anthropic(api_key=_key()) SYSTEM_PROMPT = open(PROMPT_PATH, encoding="utf-8").read() @@ -619,6 +631,92 @@ def update_crm(): save_artifact(proj["id"], "crm", crm) return jsonify({"ok": True, "crm": crm}) +@app.route("/api/payment/create", methods=["POST"]) +def payment_create(): + """Создаёт платёж. method: card | sbp | cash.""" + import urllib.request, urllib.parse + data = request.get_json(force=True) or {} + proj = get_project(data.get("token")) + if not proj: + return jsonify({"error": "project not found"}), 404 + amount = float(data.get("amount", 0)) + method = data.get("method", "card") # card | sbp | cash + desc = data.get("description", f"Оплата консалтинга — {proj['client_name'] or 'клиент'}") + if amount <= 0: + return jsonify({"error": "сумма должна быть больше 0"}), 400 + + # Наличные — не онлайн: фиксируем намерение, Руслан подтвердит вручную + if method == "cash": + crm = latest_artifact(proj["id"], "crm") or {} + pending = crm.get("pending_cash", []) + pending.append({"amount": amount, "desc": desc, "at": now()}) + crm["pending_cash"] = pending + save_artifact(proj["id"], "crm", crm) + return jsonify({"method": "cash", "instructions": "Оплата наличными при встрече. Консультант подтвердит получение.", "amount": amount}) + + sid, sec = _yookassa_creds() + return_url = data.get("return_url", "https://wasrusgen1.ru/consulting/cabinet.html") + + # Демо-режим если ключей ЮKassa нет + if not sid: + return jsonify({ + "method": method, "demo": True, + "confirmation_url": return_url + "?demo_paid=" + str(int(amount)), + "note": "ДЕМО: ключи ЮKassa не настроены. Реальная оплата заработает после добавления YOOKASSA_SHOP_ID/SECRET." + }) + + # Реальный вызов ЮKassa API + import base64 as b64m, json as jsonm + payload = { + "amount": {"value": f"{amount:.2f}", "currency": "RUB"}, + "capture": True, + "description": desc, + "metadata": {"token": proj["token"]}, + "confirmation": {"type": "redirect", "return_url": return_url} + } + if method == "sbp": + payload["payment_method_data"] = {"type": "sbp"} + payload["confirmation"] = {"type": "qr"} + try: + body = jsonm.dumps(payload).encode() + req = urllib.request.Request("https://api.yookassa.ru/v3/payments", data=body, method="POST") + auth = b64m.b64encode(f"{sid}:{sec}".encode()).decode() + req.add_header("Authorization", "Basic " + auth) + req.add_header("Content-Type", "application/json") + req.add_header("Idempotence-Key", secrets.token_hex(16)) + resp = urllib.request.urlopen(req, timeout=20) + result = jsonm.loads(resp.read()) + conf = result.get("confirmation", {}) + return jsonify({ + "method": method, "payment_id": result.get("id"), + "confirmation_url": conf.get("confirmation_url"), + "qr": conf.get("confirmation_data") # для СБП — QR-данные + }) + except Exception as e: + return jsonify({"error": "ЮKassa: " + str(e)}), 500 + +@app.route("/api/payment/webhook", methods=["POST"]) +def payment_webhook(): + """ЮKassa шлёт уведомление о статусе. При succeeded — платёж в реестр.""" + import json as jsonm + data = request.get_json(force=True) or {} + event = data.get("event") + obj = data.get("object", {}) + if event == "payment.succeeded": + token = obj.get("metadata", {}).get("token") + proj = get_project(token) if token else None + if proj: + amount = float(obj.get("amount", {}).get("value", 0)) + method = obj.get("payment_method", {}).get("type", "") + crm = latest_artifact(proj["id"], "crm") or {"payments": []} + crm.setdefault("payments", []).append({ + "date": now()[:10], "amount": amount, + "note": f"ЮKassa ({method})", "auto": True + }) + crm["paid_amount"] = sum(p.get("amount", 0) for p in crm["payments"]) + save_artifact(proj["id"], "crm", crm) + return jsonify({"ok": True}) # ЮKassa требует 200 + @app.route("/api/project/delete", methods=["POST"]) def delete_project(): data = request.get_json(force=True) or {}