""" web/routers/verify.py --------------------- Verification endpoints: send test envelopes, poll status, void. Uses DocuSign Envelopes API to confirm migrated templates work end-to-end. """ from typing import Optional import httpx from fastapi import APIRouter, Request from fastapi.responses import JSONResponse from pydantic import BaseModel from web.config import settings from web.session import get_session router = APIRouter() class SendRequest(BaseModel): template_id: str recipient_name: str recipient_email: str class VoidRequest(BaseModel): reason: str = "Test envelope — voided after verification" def _require_docusign(session: dict) -> Optional[JSONResponse]: if not session.get("docusign_access_token"): return JSONResponse({"error": "not authenticated to DocuSign"}, status_code=401) return None @router.post("/send") async def send_test_envelope(body: SendRequest, request: Request): """Send a test envelope using a migrated DocuSign template.""" session = get_session(request) err = _require_docusign(session) if err: return err payload = { "templateId": body.template_id, "status": "sent", "templateRoles": [ { "email": body.recipient_email, "name": body.recipient_name, "roleName": "Signer", } ], "emailSubject": f"[Verification Test] Please sign this document", } async with httpx.AsyncClient() as client: resp = await client.post( f"{settings.docusign_base_url}/v2.1/accounts/{settings.docusign_account_id}/envelopes", headers={ "Authorization": f"Bearer {session['docusign_access_token']}", "Content-Type": "application/json", }, json=payload, ) if not resp.is_success: return JSONResponse( {"error": "DocuSign API error", "detail": resp.text}, status_code=502, ) data = resp.json() return {"envelope_id": data.get("envelopeId")} @router.get("/status/{envelope_id}") async def envelope_status(envelope_id: str, request: Request): """Get the current status of a test envelope.""" session = get_session(request) err = _require_docusign(session) if err: return err async with httpx.AsyncClient() as client: resp = await client.get( f"{settings.docusign_base_url}/v2.1/accounts/{settings.docusign_account_id}/envelopes/{envelope_id}", headers={"Authorization": f"Bearer {session['docusign_access_token']}"}, ) if not resp.is_success: return JSONResponse( {"error": "DocuSign API error", "detail": resp.text}, status_code=502, ) data = resp.json() return { "envelope_id": envelope_id, "status": data.get("status"), "completed_at": data.get("completedDateTime"), "sent_at": data.get("sentDateTime"), } @router.post("/void/{envelope_id}") async def void_envelope(envelope_id: str, body: VoidRequest, request: Request): """Void a test envelope after verification is complete.""" session = get_session(request) err = _require_docusign(session) if err: return err async with httpx.AsyncClient() as client: resp = await client.put( f"{settings.docusign_base_url}/v2.1/accounts/{settings.docusign_account_id}/envelopes/{envelope_id}", headers={ "Authorization": f"Bearer {session['docusign_access_token']}", "Content-Type": "application/json", }, json={"status": "voided", "voidedReason": body.reason}, ) if not resp.is_success: return JSONResponse( {"error": "DocuSign API error", "detail": resp.text}, status_code=502, ) return {"voided": True, "envelope_id": envelope_id}