fix: worker emails (localhost:25 -> SMTP relay) + create ERPNext SO on webhook payment

Two bugs found tracing Mitchell Allen's batch CB-95BA6C90 (5 DOT services, card):

1) Worker authorization/signing-link/status emails were sent via
   smtplib.SMTP('localhost', 25), which has no MTA in the workers container ->
   every send failed '[Errno 111] Connection refused', so customers never got
   their e-sign links and orders sat 'awaiting client signature' forever. Routed
   all 9 hardcoded localhost:25 sites (state_trucking, mcs150_update, boc3_filing,
   hazmat_phmsa, mailbox_setup, dot_esign, completion_emails) through the
   authenticated SMTP relay (SMTP_HOST/PORT/STARTTLS/login) + added a shared
   worker_email.send_worker_email helper.

2) The ERPNext Sales Order for compliance/compliance_batch was only created in
   the /checkout/create-session endpoint, but CARD orders confirm via the Stripe
   WEBHOOK -> handlePaymentComplete, which never created the SO. Result: every
   webhook-confirmed order had erpnext_sales_order=NULL and workers logged
   'Sales Order not found 404' then built from PG. Added idempotent
   ensureComplianceSalesOrder() to handlePaymentComplete so ALL payment methods
   (card-webhook, PayPal, crypto) create + link the SO.
This commit is contained in:
justin 2026-06-09 14:40:46 -05:00
parent 220f301453
commit 68e6b60951
9 changed files with 229 additions and 9 deletions

View file

@ -39,7 +39,12 @@ def send_email(to: str, subject: str, html: str):
msg["Subject"] = subject
msg.attach(MIMEText(html, "html"))
with smtplib.SMTP("localhost", 25) as s:
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(SMTP_FROM, [to], msg.as_string())

View file

@ -369,7 +369,12 @@ class BOC3FilingHandler:
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25) as s:
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(msg["From"], [customer_email], msg.as_string())
LOG.info("[%s] Status email sent to %s", order_number, customer_email)
@ -404,7 +409,12 @@ class BOC3FilingHandler:
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25) as s:
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(msg["From"], [customer_email], msg.as_string())
LOG.info("[%s] Confirmation email sent to %s", order_number, customer_email)

View file

@ -298,5 +298,10 @@ def _send_signing_email(
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25, timeout=30) as s:
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(msg["From"], [customer_email], msg.as_string())

View file

@ -184,7 +184,12 @@ class HazmatPHMSAHandler:
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25) as s:
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(msg["From"], [customer_email], msg.as_string())
LOG.info("[%s] Status email sent to %s", order_number, customer_email)

View file

@ -187,7 +187,12 @@ class MailboxSetupHandler:
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25) as s:
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(msg["From"], [customer_email], msg.as_string())
LOG.info("[%s] 1583 e-sign email sent to %s", order_number, customer_email)

View file

@ -391,7 +391,12 @@ class MCS150UpdateHandler:
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25) as s:
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(msg["From"], [customer_email], msg.as_string())
LOG.info("[%s] Status email sent to %s", order_number, customer_email)

View file

@ -756,7 +756,12 @@ class StateTruckingHandler:
msg["Subject"] = f"Action Required: Sign Your Authorization — {service_name}"
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25, timeout=30) as s:
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(msg["From"], [customer_email], msg.as_string())
def _send_status_email(self, order_number, service_name, entity_name, dot_number, customer_email):
@ -784,7 +789,12 @@ class StateTruckingHandler:
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25) as s:
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(msg["From"], [customer_email], msg.as_string())
LOG.info("[%s] Status email sent to %s", order_number, customer_email)

View file

@ -0,0 +1,76 @@
"""Shared SMTP send helper for worker services.
Worker services historically called ``smtplib.SMTP("localhost", 25)`` directly,
which fails inside the workers container (no MTA on localhost:25) -- so every
authorization / signing-link / delivery email silently failed with
``[Errno 111] Connection refused`` and customers never received them. This routes
all worker email through the same authenticated SMTP relay the rest of the system
uses (Carbonio at co.carrierone.com:587 by default), configured via the standard
SMTP_* env vars.
Usage:
from scripts.workers.worker_email import send_worker_email
ok = send_worker_email(to, subject, html, attachments=[(filename, bytes, mime)])
"""
from __future__ import annotations
import logging
import os
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
LOG = logging.getLogger("workers.email")
SMTP_HOST = os.getenv("SMTP_HOST", "co.carrierone.com")
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
SMTP_USER = os.getenv("SMTP_USER", "noreply@performancewest.net")
SMTP_PASS = os.getenv("SMTP_PASS", "")
SMTP_FROM = os.getenv("SMTP_FROM", "Performance West <noreply@performancewest.net>")
def send_worker_email(
to: str,
subject: str,
html: str,
*,
text: str | None = None,
attachments: list[tuple[str, bytes, str]] | None = None,
cc: str | None = None,
) -> bool:
"""Send an email via the configured SMTP relay. Returns True on success.
attachments: list of (filename, content_bytes, subtype) e.g. ("x.pdf", b"...", "pdf").
Never raises -- logs and returns False so callers stay non-fatal.
"""
try:
msg = MIMEMultipart("mixed")
msg["From"] = SMTP_FROM
msg["To"] = to
if cc:
msg["Cc"] = cc
msg["Subject"] = subject
alt = MIMEMultipart("alternative")
if text:
alt.attach(MIMEText(text, "plain"))
alt.attach(MIMEText(html, "html"))
msg.attach(alt)
for fname, content, subtype in (attachments or []):
part = MIMEApplication(content, _subtype=subtype)
part.add_header("Content-Disposition", "attachment", filename=fname)
msg.attach(part)
recipients = [to] + ([cc] if cc else [])
with smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=30) as s:
s.starttls()
if SMTP_USER and SMTP_PASS:
s.login(SMTP_USER, SMTP_PASS)
s.sendmail(SMTP_FROM, recipients, msg.as_string())
LOG.info("Worker email sent to %s (subject=%r)", to, subject[:60])
return True
except Exception as exc: # noqa: BLE001
LOG.warning("Worker email send failed to %s: %s", to, exc)
return False