Add Playwright automation for BOC-3 filing on processagent.com

- boc3_playwright.py: 4-step form automation (company info, contact,
  account, payment) using patchright/undetected Playwright
- Payment with PW company card ($25/filing), credentials from env
- CAPTCHA detection — falls back to admin todo if reCAPTCHA triggers
- boc3_filing.py: process() tries Playwright first, falls back to
  manual admin todo on failure
- Env vars needed: PW_CARD_NUMBER, PW_CARD_CVC, PW_CARD_EXP_MONTH,
  PW_CARD_EXP_YEAR, BOC3_ACCOUNT_PASSWORD

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-05-29 13:24:12 -05:00
parent 584f887f82
commit 46411c09c6
2 changed files with 489 additions and 5 deletions

View file

@ -35,6 +35,7 @@ Filing flow:
from __future__ import annotations
import asyncio
import json
import logging
import os
@ -63,8 +64,53 @@ class BOC3FilingHandler:
SERVICE_NAME = "BOC-3 Process Agent Filing"
async def process(self, order_data: dict) -> list[str]:
"""Entry point called by job_server. Delegates to handle()."""
"""Entry point called by job_server. Tries Playwright, falls back to handle()."""
order_number = order_data.get("order_number", order_data.get("name", ""))
intake = order_data.get("intake_data") or {}
if isinstance(intake, str):
intake = json.loads(intake)
dot_number = intake.get("dot_number", "")
customer_email = order_data.get("customer_email", "")
# Try Playwright automation if credentials are configured
if dot_number and os.environ.get("PW_CARD_NUMBER") and os.environ.get("BOC3_ACCOUNT_PASSWORD"):
try:
from .boc3_playwright import BOC3ProcessAgent
adapter = BOC3ProcessAgent()
result = await adapter.file_boc3({
"dot_number": dot_number,
"docket_number": intake.get("docket_number", ""),
"legal_name": intake.get("entity_name", order_data.get("customer_name", "")),
"entity_type": intake.get("entity_type", "carrier"),
"contact": {
"first_name": (order_data.get("customer_name") or "").split()[0] if order_data.get("customer_name") else "",
"last_name": " ".join((order_data.get("customer_name") or "").split()[1:]),
"phone": intake.get("phone", ""),
"street": intake.get("address_street", ""),
"city": intake.get("address_city", ""),
"state": intake.get("address_state", ""),
"zip": intake.get("address_zip", ""),
},
"email": customer_email,
})
if result.success:
LOG.info("[%s] BOC-3 filed via Playwright! Order: %s", order_number, result.order_id)
self._send_confirmation_email(
order_number,
intake.get("entity_name", order_data.get("customer_name", "")),
dot_number, customer_email,
)
return []
elif result.captcha_hit:
LOG.warning("[%s] CAPTCHA on processagent.com — falling back to admin todo", order_number)
else:
LOG.warning("[%s] Playwright filing failed: %s — admin todo", order_number, result.error)
except Exception as exc:
LOG.warning("[%s] Playwright error: %s — admin todo", order_number, exc)
# Fall back to manual admin todo
return self.handle(order_data, order_number)
def handle(self, order_data: dict, order_number: str) -> list[str]:
@ -111,10 +157,7 @@ class BOC3FilingHandler:
"requested_at": datetime.utcnow().isoformat(),
}
# TODO: Automate via Playwright on processagent.com/order
# For now, create admin todo for manual filing
# Create admin todo
# Create admin todo for manual filing (Playwright attempt already made in process())
todo_data = {
"order_number": order_number,
"service": self.SERVICE_NAME,