diff --git a/backend/elena_app.py b/backend/elena_app.py index 72e2007..1999786 100644 --- a/backend/elena_app.py +++ b/backend/elena_app.py @@ -117,6 +117,19 @@ def init_db(): created_at TEXT, FOREIGN KEY(project_id) REFERENCES projects(id) ); + CREATE TABLE IF NOT EXISTS acceptances ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + project_id INTEGER NOT NULL, + doc TEXT NOT NULL, -- 'offer' | 'pep' | 'pdn' + doc_version TEXT NOT NULL, + doc_hash TEXT NOT NULL, -- SHA-256 текста документа (что подписано) + identifier TEXT, -- телефон/email подписанта + code TEXT, -- код подтверждения + ip TEXT, + user_agent TEXT, + payment_id TEXT, -- акцепт оплатой + accepted_at TEXT NOT NULL -- append-only, не редактируется + ); """) con.commit() con.close() @@ -631,6 +644,56 @@ def update_crm(): save_artifact(proj["id"], "crm", crm) return jsonify({"ok": True, "crm": crm}) +import hashlib + +# Версии и тексты юридических документов (хеш фиксируется при акцепте) +LEGAL_DOCS = {"offer": "1.0", "pep": "1.0", "pdn": "1.0"} +def _doc_hash(doc): + """SHA-256 текста документа — для доказательства что подписано.""" + path = os.path.join(BASE, "legal", {"offer": "dogovor_oferta.md", "pep": "soglashenie_pep.md", "pdn": "politika_pdn.md"}.get(doc, "")) + try: + return hashlib.sha256(open(path, "rb").read()).hexdigest() + except Exception: + return "" + +@app.route("/api/accept", methods=["POST"]) +def accept_documents(): + """Фиксация акцепта (ПЭП) в append-only журнал с хешем документов.""" + data = request.get_json(force=True) or {} + proj = get_project(data.get("token")) + if not proj: + return jsonify({"error": "project not found"}), 404 + docs = data.get("docs", ["offer", "pep"]) # какие документы акцептованы + identifier = data.get("identifier", "") # телефон/email + code = data.get("code", "") + payment_id = data.get("payment_id", "") + ip = request.headers.get("X-Real-IP") or request.headers.get("X-Forwarded-For", request.remote_addr or "") + ua = request.headers.get("User-Agent", "")[:300] + con = db() + recorded = [] + for doc in docs: + if doc not in LEGAL_DOCS: + continue + h = _doc_hash(doc) + con.execute( + "INSERT INTO acceptances (project_id, doc, doc_version, doc_hash, identifier, code, ip, user_agent, payment_id, accepted_at) VALUES (?,?,?,?,?,?,?,?,?,?)", + (proj["id"], doc, LEGAL_DOCS[doc], h, identifier, code, ip, ua, payment_id, now()) + ) + recorded.append({"doc": doc, "version": LEGAL_DOCS[doc], "hash": h[:16] + "..."}) + con.commit() + return jsonify({"ok": True, "accepted": recorded, "at": now()}) + +@app.route("/api/acceptances/") +def get_acceptances(token): + """Выписка из журнала акцептов (доказательная база).""" + proj = get_project(token) + if not proj: + return jsonify({"error": "not found"}), 404 + rows = db().execute( + "SELECT doc, doc_version, doc_hash, identifier, code, ip, payment_id, accepted_at FROM acceptances WHERE project_id=? ORDER BY id", (proj["id"],) + ).fetchall() + return jsonify({"acceptances": [dict(r) for r in rows]}) + @app.route("/api/payment/create", methods=["POST"]) def payment_create(): """Создаёт платёж. method: card | sbp | cash."""