Commit graph

46 commits

Author SHA1 Message Date
justin
d4c4ae003e Fix 6 bugs from code review
Critical:
- Single-order discount used wrong column names (discount_pct/discount_flat_cents
  → discount_type/discount_value). Discounts were silently $0.
- Single-order discount skipped allowed_emails and expires_at checks
- Free orders now set paid_at = NOW()

High:
- Discount usage now tracked in discount_usage table + current_uses incremented
- Flat discount only replaces bundle when flat >= bundle (was always replacing)

Minor:
- Removed unused CDR profile fetch in EntityStep

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 02:16:38 -05:00
justin
de50971fb3 Add intake form links to compliance order confirmation email
The confirmation email now includes a prominent blue box with
direct links to the intake form for each ordered service. Subject
changed to "Action Required" to prompt the customer to complete it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:26:33 -05:00
justin
5cb360acba Fix: add allowed_emails to DiscountCode TypeScript interface
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:19:12 -05:00
justin
6ed5105ae5 Send intake email for $0 free orders
Free orders bypass Stripe, so the Stripe webhook never fires and the
intake/confirmation email never gets sent. Now trigger
sendComplianceIntakeEmail directly in the $0 bypass flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:18:19 -05:00
justin
b81e102d39 Validate allowed_emails on discount code lookup
The /api/v1/discount/:code endpoint now checks allowed_emails when
an email is provided. If the email isn't in the allowed list, returns
valid:false so the frontend doesn't show a fake discount. The promo
field is cleared and unlocked if validation fails.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:08:51 -05:00
justin
7bb08f3493 Handle already-paid orders gracefully in checkout
When create-session is called for an order that's already paid (e.g.
free order with page refresh, duplicate submit, or browser retry),
return a success redirect instead of 404. Prevents confusing
"Payment Not Confirmed" errors on the success page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:07:27 -05:00
justin
b91e76e44c Fix: FCC errors now fall through to local DB fallback
Was returning 502 early instead of throwing to trigger the catch block
where the local fcc_499_filers fallback lives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 00:42:43 -05:00
justin
bbfa6393fa Fall back to local DB when FCC name search is blocked
FCC/Akamai is blocking our server IP (403). Name search now falls back
to querying the local fcc_499_filers table (20K+ records) when the
live FCC search fails.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 00:41:47 -05:00
justin
33684a9152 Better error message when FCC 499 search is down
FCC ColdFusion app returns 503 periodically. Show helpful message
suggesting FRN search instead of generic "Search failed".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 00:39:38 -05:00
justin
7909f130c6 Fix $0 checkout bypass: remove nonexistent status column
compliance_orders has payment_status, not status.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 00:36:15 -05:00
justin
6d441a5cc0 Add email-restricted discount codes and $0 order bypass
- discount_codes.allowed_emails: when set, code only valid for listed emails
- Flat discounts now replace bundle discount (don't stack)
- $0 orders skip all payment gateways, mark paid immediately, redirect to success
- FREEDOM249: $249 flat off restricted to 4+ deficiency carriers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 00:16:47 -05:00
justin
d39e10485f Add URL promo code pre-fill and fix discount stacking logic
- Checkout page reads ?code= or ?promo= from URL, pre-fills and locks the
  promo field, shows the promo discount in the summary instead of the 15%
  bundle discount
- API: when a promo code % >= bundle %, replace the bundle discount entirely
  instead of stacking (e.g. MEMORIAL25 at 25% replaces the 15% bundle)
- Also checks discount code expiration in the query

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 15:06:19 -05:00
justin
71d466c922 Wire createCommission() into compliance batch checkout
Compliance batch orders now create commission ledger entries when
a discount code (agent referral) is used. Tracks total order amount,
discount applied, and links to the agent for payout processing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 11:00:38 -05:00
justin
b1750cfd20 Fix compliance check logging: use status (red/yellow/green) not severity
Check objects use status field (red/yellow/green), not severity
(critical/major/minor). Logging was always recording 0 issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 12:10:03 -05:00
justin
b5282f5e00 Fix compliance checker: include structured_checks + pdf_checks from audit
Was only reading pdf_checks, missing structured_checks. Also skip
minor-only issues — show major/critical that match email campaign data.
This fixes the "clean" result for carriers our audit flagged as deficient.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 10:59:59 -05:00
justin
c127cdd908 Improve compliance checker UX + add search logging
- Loading message: shows estimated time (30-90 seconds) + rotating status
  updates (RMD, CPNI, USAC, BDC, STIR/SHAKEN, compiling report)
- Timeout increased to 90s (was 60s)
- Error messages: "try again in a few minutes" (not "moments" or "check internet")
- New compliance_check_log table: logs every FCC lookup with FRN, entity,
  IP, user agent, referrer, issue count, severity, response time
- Enables conversion funnel analysis and follow-up

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 22:37:29 -05:00
justin
bd5193e45f Add Umami custom event tracking across all key pages
- Created /js/pw-analytics.js with conversion funnel events
- Added to Base.astro layout (all Astro pages) + 6 static HTML pages
- Events tracked: compliance-check-start, compliance-check-complete,
  order-cta-click, checkout-page-view, checkout-start, esign-opened,
  esign-submitted, campaign-click (UTM attribution), contact-form-submit
- Server-side payment-complete event from checkout webhook via Umami API
- Auto-tracks any element with data-track="event-name" attribute

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-05 05:10:13 -05:00
justin
40844b2aff Add generic eSign portal for all compliance document types
Reusable signing flow: service handler generates document → inserts
esign_records row → emails JWT link → client reviews PDF + signs →
API stores signature + resumes pipeline. Works for RMD, CPNI, CALEA,
499-A engagement, discontinuance, CRTC, and any future doc types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-04 10:45:37 -05:00
justin
c9881868dd Add Telegram notification on every new paid order
Sends to the monitoring bot immediately when payment is confirmed:
- Customer name and email
- Service/slug ordered
- Total amount (includes all fees: service + formation + state + addons)
- Payment method
- Order number and type

Fire-and-forget — never blocks the payment flow.
Requires TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID env vars on API container.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-04 07:32:42 -05:00
justin
7d783bdb39 Reprice services: à la carte ~$2,000 vs FCC Carrier Reg bundle $1,299
Individual services repriced so buying separately costs ~$2K, making
the $1,299 FCC Carrier Registration a clear 35% savings:

- CORES/FRN: $99 → $149
- Form 499 Initial: $299 → $349
- D.C. Registered Agent: $99 → $149/yr
- RMD: $219 → $249 (+ $100 FCC fee)
- CPNI: $149 → $199
- CALEA SSI: $299 → $799 (includes consulting work)
- BDC Broadband: $199 → $249
- BDC Voice: $149 → $199
- BDC Both: $299 → $349

Retired New Carrier Bundle ($1,799) — redirected to FCC Carrier
Registration ($1,299) which includes more services and the wizard.

Updated prices in: API catalog, batch order page, compliance checker CTA.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-04 06:23:19 -05:00
justin
47ca1bf10f Production readiness fixes: 3 critical + 2 high-priority
Critical #1 — CRTC: Fix undefined 'province' variable (canada_crtc.py:1322)
  Crashes every order at Step 6 document generation. Replaced with
  order_data.get("custom_incorporation_province", "BC").

Critical #2 — FCC Carrier Reg: Add State PUC state picker
  The order page collected "1/few/nationwide" but API expected an array
  of state codes. Added a multi-state checkbox grid that appears when
  State PUC add-on is checked. Sends puc_states: ["CA","NY",...] in
  service_wizard. Price updates per-state ($399 × count).

Critical #3 — Compliance: Add REQUIRED_FIELDS for fcc-499q and
  fcc-499a-discontinuance. Without these, intake validation was
  completely skipped — invalid data accepted silently.

High #4 — FCC Carrier Reg: Don't mark D.C. Agent complete
  prematurely. Was calling _update_step() right after creating the
  admin todo. Now waits for admin to confirm NW order is placed.

High #5 — Compliance: Add fcc-499q and fcc-499a-discontinuance to
  REQUIRES_ENTITY_FRN set. Both require FRN for USAC filing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-04 05:28:13 -05:00
justin
572f0cbf93 Implement 499-Q quarterly filing lifecycle
After 499-A+Q bundle is filed, the handler now creates actual
compliance_orders for each remaining quarterly 499-Q filing:

Schedule: Q1 due Feb 1, Q2 due May 1, Q3 due Aug 1, Q4 due Nov 1

Each quarterly order:
- Created as paid (covered by bundle price)
- Has due_date, quarter, period_end_date in intake_data
- Links to parent 499-A order
- Tracks reminder status (30d/14d/7d sent flags)

Notification worker (quarterly_499q_notify.py):
- Runs daily at 8am CT via systemd timer
- Sends HTML reminder emails at 30, 14, 7 days before due
- Email includes intake link for client to submit quarterly data
- Late warning at 7 days: "USAC may estimate higher contributions"
- Idempotent: won't re-send same reminder level

Added fcc-499q service slug ($0, not sold standalone).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 02:28:04 -05:00
justin
3e04a8fc16 Add zero-revenue 499-A filing at $179
New service slug fcc-499a-zero for carriers with no telecom revenue:
- $179 instead of $499 (no revenue analysis needed)
- Minimal intake: entity, officer, filer ID, filing type only
- Skips revenue schedules (blocks 3-4), USF calculations (block 5),
  traffic study upload, and revenue workbook generation
- Fills blocks 1-2 and 6 only, all revenue lines left as zero

Compliance checker: shows both options (mutually exclusive checkboxes)
Order page: maps form_499a_zero to fcc-499a-zero slug
Handler: detects slug and skips revenue pipeline
DC Agent shown when either 499-A variant is checked

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 02:04:18 -05:00
justin
d03135e9d0 Hide STIR/SHAKEN from compliance checker, remove toggle
STIR/SHAKEN card creates confusion with OCN bundling on the order
page — if carried over, it doesn't auto-select OCN. Self-reported
RMD status also doesn't reliably indicate actual compliance.
Reverted to hidden (computed internally but not shown to users).
Removed STIR toggle and pending-question watcher for it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-02 08:55:10 -05:00
justin
0f9b473030 Fix BDC check: ask voice first, rename card, support voice-only carriers
BDC Voice Subscription filing is required for all retail voice
providers (VoIP, CLEC, wireline) even without broadband service.
The compliance checker was asking about broadband first, which
caused voice-only carriers to overlook their BDC Voice obligation.

- Renamed card: "BDC Filing (Broadband + Voice Subscription Data)"
- Reversed question order: ask retail voice first, then broadband
- "No" for voice now says "No, or wholesale only" (wholesale exempt)
- Voice-only retail carriers correctly flagged for BDC Voice filing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-02 07:28:09 -05:00
justin
050b19a43a Enable STIR/SHAKEN card in compliance checker with originate/terminate toggle
- Uncomment STIR/SHAKEN check in fcc-lookup.ts — shows self-reported
  implementation status from RMD filing
- Add toggle: "Do you originate calls or only terminate?"
  - Terminate only → green, signing cert not required, file RMD as
    partial implementation
  - Originate or both → red, must have own STI certificate as of
    June 2025
- Toggle integrates with pending-question system (CTA waits for answer)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 10:55:00 -05:00
justin
2927b5cebb Add FCC Carrier/ISP Registration: API, checkout, handler, dispatch
Phase 3-5:
- API: POST /api/v1/fcc-carrier-registration (order creation with pricing)
- API: GET /api/v1/fcc-carrier-registration/:id (status)
- API: GET /api/v1/fcc-carrier-registration/state-fees (formation fees)
- Checkout: fcc_carrier_registration order type with Stripe line items
- Payment handler: dispatch worker + send confirmation email
- Pipeline handler: 8-step CRTC-style pipeline (formation → CORES → 499 →
  DC Agent → State PUC → RMD/CPNI/CALEA/BDC → add-ons → final review)
- Job server dispatch map entry
- Service page CTA updated to link to order page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 08:48:36 -05:00
justin
118d24cc1a Rename 'IPES & ISP Registrations' to 'FCC Carrier / ISP Registration'
Updated across 61 static HTML files (nav links), bundles catalog,
service page title/description/heading, and llms.txt.
URL stays /services/telecom/ipes-isp (no redirect needed).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 08:06:47 -05:00
justin
424a7f3b2d Add missing updateResource import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 01:49:07 -05:00
justin
28b407eea6 Fix portal user linking: use updateResource instead of set_value for child table
set_value doesn't work for child tables like portal_users. Use
updateResource (PUT /api/resource/Customer) which handles it correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 01:48:05 -05:00
justin
f5d307a1e8 Fix corp search LIMIT type: cast to int
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 23:05:43 -05:00
justin
159a576157 Add corporate record suggestions on Officer step, search all states
- OfficerStep searches entity_cache for matching corporations when loaded
- Shows clickable suggestions with RA name and address to auto-fill Officer 1
- Pre-fills contact_name/email/phone from entity data (helps data-only filers)
- Corp search endpoint: state param now optional (searches all states)
- Corp search returns registered_agent and principal_address fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 23:02:16 -05:00
justin
d6c513bca5 Link telecom_entity_id by FRN on intake save, fix batch siblings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 18:31:59 -05:00
justin
3dce721120 Add PUT to CORS allowed methods (needed for intake save)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 18:29:00 -05:00
justin
7b650179e4 Add Finish button handler + intake save API endpoint
- Wizard "Finish" button now submits intake data to the API
- New PUT /api/v1/compliance-orders/:id/intake endpoint saves intake data,
  updates entity, re-dispatches worker, and unpauses batch siblings
- Shows success screen with portal link after submission

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 18:26:20 -05:00
justin
e76f54b11f Fix batch Payment Entry (paidAmountCents was 0), fix CTA button JS syntax error
- Batch invoice creation now calculates actual total from line items
- Fixed JS syntax error: `className = "..." style="..."` was invalid JS
  (style= outside string literal caused Unexpected identifier 'style')
- Portal menu: set hide_standard_menu=1 to remove duplicate sidebar items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 04:20:05 -05:00
justin
1d564b2fdb RMD quality: recommend refiling when issues found
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 03:20:01 -05:00
justin
cbfb8d6091 Add engagement authorization, remove price headers from intake pages, fix duplicate emails
- Add clickwrap authorization checkbox to fcc-compliance, state-puc, neca-ocn order pages
- Store engagement_accepted_at/ip/version in compliance_orders (migration 074)
- Add 499-A past-due/multi-year eSign engagement letter generator
- Gate 499-A handler on engagement signature for past-due/multi-year orders
- Remove price/tax/fee headers from all 19 intake pages (post-payment only)
- Fix duplicate confirmation email for compliance_batch orders
- Add USAC past-due fee negotiation research doc

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 02:50:02 -05:00
justin
6865da2004 Add 499 detail address fallback when CORES returns empty
CORES scraper sometimes returns empty address depending on server IP.
Now falls back to Headquarters Address from the FCC 499 filer detail
page, which is more reliable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 22:07:55 -05:00
justin
438d3a6e2e Add customer portal orders page + my-orders API
- GET /api/v1/compliance-orders/my-orders?email= returns all orders
  grouped by batch, with USAC delegation status
- /account/orders page shows order history with:
  - Entity name, FRN, batch ID, date, payment status
  - Itemized services with pricing
  - USAC delegation callout with confirmation button
  - Auth-gated (requires login)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 21:27:10 -05:00
justin
86205c309c Replace USAC email instructions with confirmation button + add API endpoint
- Intake email now has "I've completed the delegation →" button
  instead of "reply to this email"
- Button links to order success page with action=usac_delegation
- New API: POST /api/v1/compliance-orders/:id/usac-delegation
  Records confirmation timestamp in intake_data and logs it
- Removed "reply to this email" from intake email

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 21:20:22 -05:00
justin
6ec28f6a79 Enable Plaid balance verification for ACH payments
Adds financial_connections with balances permission and prefetch
to Stripe Checkout sessions when payment_method is ACH. This uses
Plaid to verify the customer's bank account balance before accepting
payment, reducing ACH return risk.

- permissions: ["payment_method", "balances"]
- prefetch: ["balances"]
- verification_method: "instant"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 20:31:06 -05:00
justin
75ea2c5c6f Fix 3 bugs: BDC undo, JSON parse safety, price formatting
1. BDC Yes/No buttons now have "Change answer" undo — clicking
   Yes or No is reversible without re-running the check
2. intake_data JSON.parse wrapped in try/catch with error logging
   instead of silently returning empty object
3. All CTA price displays use .toFixed(2) for consistent formatting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 15:13:18 -05:00
justin
a7d7fee154 Fix 6 bugs found in compliance and checkout flows
1. CRITICAL: Add compliance_batch to stripe session tableMap —
   session IDs weren't being stored for batch orders
2. CRITICAL: Fix batch orders using order_number instead of batch_id
   when storing stripe_session_id
3. MAJOR: Tax deductibility note only shows for compliance orders,
   not CRTC/formation/bundles
4. MAJOR: Identity verification fallback changed from localhost:4321
   to performancewest.net with warning log
5. MEDIUM: Fix discount rounding — last service absorbs remainder
   to prevent cent loss across batch orders
6. LOW: Validate at least one paid service in batch orders
7. Standardize support email to info@performancewest.net everywhere

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 09:56:12 -05:00
justin
28d82912f7 Add compliance intake email after payment
Sends next-steps email with services list, USAC delegation
instructions for 499-A, and multi-year catch-up note.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 09:26:26 -05:00
justin
f8cd37ac8c Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:54:22 -05:00