- Pipeline orchestrator: chains sequential fulfillment for new carrier bundles (formation → EIN → USDOT → MC → BOC-3 → MCS-150 → D&A → UCR) - Mailbox setup: Anytime Mailbox provisioning with USPS 1583 e-sign + online notarization - New services: ein-application ($79), virtual-mailbox ($149/yr) - Registered all new handlers in SERVICE_HANDLERS - Pipeline cron: every 5 minutes
182 lines
7.8 KiB
Python
182 lines
7.8 KiB
Python
"""Anytime Mailbox Setup — virtual mailbox provisioning for out-of-state formations.
|
|
|
|
When a carrier forms in Wyoming but operates elsewhere, they need a Wyoming
|
|
mailing address for official correspondence. We use Anytime Mailbox for this.
|
|
|
|
Setup requires USPS Form 1583 — notarized authorization for mail receipt.
|
|
The customer's photo ID (already collected during intake) is used for the 1583.
|
|
|
|
Flow:
|
|
1. Customer orders Wyoming formation package
|
|
2. LLC is formed in Wyoming
|
|
3. We open an Anytime Mailbox in Wyoming under the LLC name
|
|
4. Generate USPS Form 1583 pre-filled with customer + LLC info
|
|
5. Customer e-signs the 1583 via our portal
|
|
6. Schedule online notarization session (same service as CRTC BITS)
|
|
7. Submit notarized 1583 to Anytime Mailbox
|
|
8. Mailbox active — address provided to customer
|
|
|
|
Service slug: virtual-mailbox
|
|
Price: $149/yr
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
from datetime import datetime
|
|
|
|
LOG = logging.getLogger("workers.services.mailbox_setup")
|
|
|
|
|
|
class MailboxSetupHandler:
|
|
"""Handle virtual mailbox setup orders."""
|
|
|
|
SERVICE_SLUG = "virtual-mailbox"
|
|
SERVICE_NAME = "Virtual Mailbox (Anytime Mailbox)"
|
|
|
|
async def process(self, order_data: dict) -> list[str]:
|
|
"""Entry point called by job_server."""
|
|
order_number = order_data.get("order_number", order_data.get("name", ""))
|
|
return self.handle(order_data, order_number)
|
|
|
|
def handle(self, order_data: dict, order_number: str) -> list[str]:
|
|
"""Process a virtual mailbox setup order."""
|
|
LOG.info("[%s] Processing virtual mailbox setup", order_number)
|
|
|
|
intake = order_data.get("intake_data") or {}
|
|
if isinstance(intake, str):
|
|
intake = json.loads(intake)
|
|
|
|
entity_name = intake.get("entity_name", order_data.get("customer_name", ""))
|
|
customer_email = order_data.get("customer_email", "")
|
|
formation_state = intake.get("formation_state", "WY")
|
|
customer_name = intake.get("signer_name", order_data.get("customer_name", ""))
|
|
|
|
# Step 1: Create e-sign record for USPS Form 1583
|
|
try:
|
|
import psycopg2
|
|
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
|
|
cur = conn.cursor()
|
|
|
|
cur.execute("""
|
|
INSERT INTO esign_records (
|
|
order_number, document_type, document_title, entity_name,
|
|
document_metadata, requires_perjury, status,
|
|
expires_at
|
|
) VALUES (%s, %s, %s, %s, %s, FALSE, 'pending', NOW() + interval '14 days')
|
|
ON CONFLICT (order_number, document_type)
|
|
WHERE status IN ('pending', 'signed') DO NOTHING
|
|
""", (
|
|
order_number,
|
|
"usps-1583",
|
|
"USPS Form 1583 — Authorization to Receive Mail",
|
|
entity_name,
|
|
json.dumps({
|
|
"form_type": "usps-1583",
|
|
"mailbox_state": formation_state,
|
|
"entity_name": entity_name,
|
|
"customer_name": customer_name,
|
|
"requires_notarization": True,
|
|
"notarization_service": "online", # Same as CRTC BITS
|
|
"instructions": (
|
|
"This form authorizes Anytime Mailbox to receive mail on behalf of "
|
|
f"{entity_name}. After you sign, we will schedule a brief online "
|
|
"notarization session (5-minute video call with a notary). Your photo "
|
|
"ID on file will be used for identity verification."
|
|
),
|
|
}),
|
|
))
|
|
conn.commit()
|
|
conn.close()
|
|
LOG.info("[%s] E-sign record created for USPS 1583", order_number)
|
|
except Exception as exc:
|
|
LOG.error("[%s] E-sign 1583 setup failed: %s", order_number, exc)
|
|
|
|
# Step 2: Send e-sign link to customer
|
|
self._send_1583_email(order_number, entity_name, customer_email, customer_name)
|
|
|
|
# Step 3: Create admin todo
|
|
try:
|
|
import psycopg2
|
|
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
|
|
with conn.cursor() as cur:
|
|
cur.execute("""
|
|
INSERT INTO admin_todos (
|
|
title, category, priority, order_number, service_slug,
|
|
description, data, status
|
|
) VALUES (%s, %s, %s, %s, %s, %s, %s, 'pending')
|
|
""", (
|
|
f"Mailbox Setup — {entity_name} ({formation_state})",
|
|
"provisioning",
|
|
"normal",
|
|
order_number,
|
|
self.SERVICE_SLUG,
|
|
f"Set up Anytime Mailbox for {entity_name} in {formation_state}.\n\n"
|
|
f"Steps:\n"
|
|
f"1. Customer e-signs USPS Form 1583 (link sent)\n"
|
|
f"2. Schedule online notarization session\n"
|
|
f"3. Submit notarized 1583 to Anytime Mailbox\n"
|
|
f"4. Activate mailbox, provide address to customer\n\n"
|
|
f"Customer: {customer_email}\n"
|
|
f"Photo ID: on file in MinIO (from intake)\n"
|
|
f"Notarization: use same service as CRTC BITS",
|
|
json.dumps({
|
|
"order_number": order_number,
|
|
"entity_name": entity_name,
|
|
"formation_state": formation_state,
|
|
"customer_email": customer_email,
|
|
}),
|
|
))
|
|
conn.commit()
|
|
conn.close()
|
|
except Exception as exc:
|
|
LOG.error("[%s] Failed to create mailbox todo: %s", order_number, exc)
|
|
|
|
return []
|
|
|
|
def _send_1583_email(self, order_number, entity_name, customer_email, customer_name):
|
|
"""Send e-sign link for USPS Form 1583."""
|
|
if not customer_email:
|
|
return
|
|
try:
|
|
import smtplib
|
|
import jwt
|
|
from email.mime.text import MIMEText
|
|
|
|
secret = os.environ.get("JWT_SECRET", os.environ.get("ADMIN_JWT_SECRET", ""))
|
|
token = jwt.encode(
|
|
{"order_id": order_number, "order_type": "usps-1583", "email": customer_email},
|
|
secret, algorithm="HS256",
|
|
)
|
|
domain = os.environ.get("DOMAIN", "performancewest.net")
|
|
esign_url = f"https://{domain}/portal/esign?token={token}"
|
|
|
|
first = customer_name.split()[0] if customer_name else "there"
|
|
body = (
|
|
f"Hi {first},\n\n"
|
|
f"Your virtual mailbox for {entity_name} is being set up. "
|
|
f"To activate it, we need your authorization on USPS Form 1583.\n\n"
|
|
f"This is a quick process:\n"
|
|
f"1. Click the link below to review and sign the form\n"
|
|
f"2. We'll schedule a brief 5-minute video call with a notary\n"
|
|
f"3. Your photo ID on file will be used for verification\n"
|
|
f"4. Once notarized, your mailbox will be active within 24 hours\n\n"
|
|
f"Sign here: {esign_url}\n\n"
|
|
f"This link expires in 14 days.\n\n"
|
|
f"Questions? Call (888) 411-0383.\n\n"
|
|
f"Performance West Inc.\n"
|
|
)
|
|
|
|
msg = MIMEText(body)
|
|
msg["Subject"] = f"Action Required: Sign USPS Form 1583 — {entity_name} Mailbox"
|
|
msg["From"] = "noreply@performancewest.net"
|
|
msg["To"] = customer_email
|
|
|
|
with smtplib.SMTP("localhost", 25) as s:
|
|
s.sendmail(msg["From"], [customer_email], msg.as_string())
|
|
|
|
LOG.info("[%s] 1583 e-sign email sent to %s", order_number, customer_email)
|
|
except Exception as exc:
|
|
LOG.warning("[%s] Failed to send 1583 email: %s", order_number, exc)
|