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>
342 lines
17 KiB
Markdown
342 lines
17 KiB
Markdown
# Performance West — 52-Jurisdiction Business Formation System
|
|
|
|
**Last updated:** 2026-04-05
|
|
|
|
50 US states + District of Columbia + British Columbia (Canada) = 52 jurisdictions.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Customer Website API Database
|
|
| | | |
|
|
+- Select state ----------->| | |
|
|
+- Search name ------------>+- GET /states/:code/ ----->+- (proxy to Python) -->|
|
|
| <-- available/taken ---- | name-search | |
|
|
+- Fill details ----------->| | |
|
|
+- Submit order ----------->+- POST /formations ------->+- INSERT ------------->|
|
|
| <-- order number ------ | | formation_orders |
|
|
| | | |
|
|
| ERPNext (BPM Orchestrator) | |
|
|
| | | |
|
|
| +------+------+ | |
|
|
| | Webhook |<-- order.created --| |
|
|
| | triggers | (not polling) | |
|
|
| | worker | | |
|
|
| +------+------+ | |
|
|
| | | |
|
|
| +------+------+ | |
|
|
| | Load state | | |
|
|
| | adapter | | |
|
|
| +------+------+ | |
|
|
| | | |
|
|
| +------+------+ | |
|
|
| | Playwright |--> State SOS | |
|
|
| | automation | Portal | |
|
|
| +------+------+ | |
|
|
| | | |
|
|
| +------+------+ | |
|
|
| | Update DB |---- UPDATE ------->| |
|
|
| | with result | status=filed | |
|
|
| +------+------+ | |
|
|
| | | |
|
|
| <-- email docs --------- | | |
|
|
```
|
|
|
|
**ERPNext as BPM orchestrator:** ERPNext webhooks trigger formation workers on order events (e.g., `order.created`, `payment.confirmed`). Workers are event-driven, not polling-based.
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
scripts/formation/
|
|
+-- __init__.py
|
|
+-- base.py # StatePortal base class + data models
|
|
+-- name_search.py # Multi-state name search coordinator
|
|
+-- formation_worker.py # Event-driven queue processor (ERPNext webhook triggers)
|
|
+-- operating_agreement.py # Template-based .docx/.pdf generator
|
|
+-- ein_worker.py # IRS EIN online application (Playwright)
|
|
+-- document_delivery.py # SMTP email with attached formation docs
|
|
+-- states/
|
|
+-- __init__.py # Registry: get_adapter(), get_config(), STATES dict
|
|
+-- al/ # Alabama
|
|
| +-- __init__.py
|
|
| +-- config.py # Portal URLs, NW RA address, fees, CSS selectors
|
|
| +-- adapter.py # ALPortal(StatePortal) -- search + filing automation
|
|
+-- ak/ # Alaska
|
|
+-- az/ # Arizona
|
|
... (51 US: 50 states + DC)
|
|
+-- dc/ # District of Columbia
|
|
+-- bc/ # British Columbia (Canada) -- CRTC Carrier Package
|
|
```
|
|
|
|
## BC (Canada) — CRTC Carrier Package
|
|
|
|
British Columbia uses a completely different workflow from US states:
|
|
|
|
- **Not a state SOS filing** — BC adapter handles the CRTC Carrier Package registration
|
|
- **Different portal:** BC Corporate Online (bcregistryservices.gov.bc.ca) — anonymous, no login required
|
|
- **CRTC requirements:** Carrier registration with the Canadian Radio-television and Telecommunications Commission
|
|
- **Separate API endpoint:** `/api/v1/canada-crtc` for BC-specific orders
|
|
- **Separate DB table:** `canada_crtc_orders` tracks BC orders independently
|
|
- **14-step pipeline** managed by `services/canada_crtc.py`
|
|
|
|
The BC adapter implements `BCPortal(StatePortal)` but overrides both name search and filing methods for Canadian requirements. BC government fees are passed through at cost.
|
|
|
|
### BC Config (`scripts/formation/states/bc/config.py`)
|
|
|
|
The BC config contains configuration blocks for all Canadian-specific services:
|
|
|
|
| Config Block | Purpose |
|
|
|-------------|---------|
|
|
| `bits` | BITS (international telecom) registration — filing URL, required documents |
|
|
| `ccts` | CCTS (complaint commission) membership — application URL, required info |
|
|
| `gckey` | GCKey signup wizard — 5-step URLs, CSS selectors, hCaptcha sitekeys, password rules |
|
|
| `ats` | Annual Telecommunications Survey — 6 survey forms (REP-T/T1, REP-U, 802a, 802j, Facilities, Pricing) with deadlines and revenue thresholds |
|
|
| `corporate_obligations` | BC corporate tax/filing obligations — T2, GST/HST, T4/T4A, BC PST, WorkSafeBC, CRTC update |
|
|
|
|
### GCKey Provisioning
|
|
|
|
Each carrier needs its own GCKey account (Government of Canada credential) for filing
|
|
annual telecommunications surveys via My CRTC Account.
|
|
|
|
- **Username format:** `pw-{bc_number}`
|
|
- **Recovery email:** `regulatory@domain.ca` (we control the mailbox)
|
|
- **Automation:** `gckey_provisioner.py` — Playwright-based 5-step signup wizard
|
|
- **hCaptcha:** Invisible challenge on Step 2 (sitekey `99871bd1-7b22-417a-b6cc-7ef645e5147a`)
|
|
- **Credentials stored** in ERPNext Sensitive ID (encrypted)
|
|
- **Steps 3-5** selectors inferred from Step 1-2 recon — need verification on first live run
|
|
|
|
### Compliance Calendar (17 Entries Per Carrier)
|
|
|
|
Created at pipeline Step 13. Entries span regulatory, corporate tax, and survey obligations:
|
|
|
|
**Regulatory (7):** BC Annual Report, CRTC Maintenance, Mailbox Renewal, Domain Renewal,
|
|
DID Renewal, CCTS Renewal, CRTC Registration Update
|
|
|
|
**Corporate Tax (6):** T2 Return (Jun 30), Tax Payment (Mar 31), GST/HST (Mar 31),
|
|
T4/T4A Slips (Feb 28), BC PST (volume-based), WorkSafeBC (Mar 1 — if employees)
|
|
|
|
**Annual Telecom Surveys (4+):** REP-T/T1 (mandatory for ALL carriers, Mar 1),
|
|
REP-U/802a/802j/Facilities/Pricing (only if >$10M CAD revenue)
|
|
|
|
## State Adapter Status
|
|
|
|
### Name Search Methods
|
|
|
|
| Method | States | How It Works |
|
|
|--------|--------|-------------|
|
|
| **Socrata REST API** | CO, IA, IL, MI, NY, OR, PA, VT, WA | Free JSON API via state open data portals. No browser needed. |
|
|
| **SFTP Bulk Download** | FL | Free SFTP (public credentials) with daily entity dumps. Pre-load into local DB. |
|
|
| **Playwright** | All remaining ~40 US states | Browser automation against state SOS web portals. |
|
|
| **BC Corporate Online** | BC (Canada) | Browser automation against BC Registry Services. |
|
|
|
|
### Filing Method
|
|
|
|
**All 51 US jurisdictions use Playwright** — no state offers a filing API.
|
|
**BC (Canada)** uses Playwright against BC Corporate Online + CRTC portal.
|
|
|
|
### Implementation Status
|
|
|
|
| State | Name Search | LLC Filing | Corp Filing | Selectors Verified |
|
|
|-------|-------------|-----------|-------------|-------------------|
|
|
| **WY** | Working | Stub (needs filing walkthrough) | Stub | Name search: YES |
|
|
| **CO** | Socrata API (working) | Stub | Stub | N/A (API) |
|
|
| **BC** | Stub (BC Corporate Online) | N/A (CRTC Carrier Package) | Stub | Need portal inspection |
|
|
| All others | Stub (structure ready) | Stub | Stub | Need portal inspection |
|
|
|
|
~12 states have real implementations, ~40 still stubbed.
|
|
|
|
### Per-State Work Required
|
|
|
|
For each state, the following work is needed to go live:
|
|
|
|
1. **Visit the state's name search URL** in a browser
|
|
2. **Inspect form elements** — record CSS selectors for input fields, buttons, result areas
|
|
3. **Update `config.py`** with verified selectors
|
|
4. **Test name search** with known entities
|
|
5. **Visit the filing URL** and walk through the LLC formation form
|
|
6. **Record all filing form selectors** — name, agent, address, member, payment fields
|
|
7. **Implement the `file_llc()` method** with the state-specific workflow
|
|
8. **Test with a real formation** (or test/sandbox environment if available)
|
|
9. **Repeat for `file_corporation()`**
|
|
|
|
Estimated time: **1-3 hours per state** depending on portal complexity.
|
|
|
|
## API Endpoints
|
|
|
|
| Method | Path | Description |
|
|
|--------|------|-------------|
|
|
| `GET` | `/api/v1/states` | All 52 jurisdictions with fees |
|
|
| `GET` | `/api/v1/states/:code/name-search?name=...` | Name availability check |
|
|
| `POST` | `/api/v1/formations` | Create formation order |
|
|
| `GET` | `/api/v1/formations/:orderNumber` | Check order status |
|
|
| `GET` | `/api/v1/bundles` | List available service bundles |
|
|
| `GET` | `/api/v1/payment-methods` | Available payment methods + surcharges |
|
|
| `POST` | `/api/v1/agents` | Sales agent referral tracking |
|
|
| `POST` | `/api/v1/canada-crtc` | BC (Canada) CRTC Carrier Package order |
|
|
|
|
## Database Tables
|
|
|
|
### `state_filing_fees` — 52 rows (populated via migration 002)
|
|
All state/jurisdiction filing fees, portal URLs, special requirements (publication, franchise tax, etc.)
|
|
|
|
### `formation_orders` — Customer orders
|
|
Full order lifecycle: received -> processing -> submitted -> filed -> delivered.
|
|
Includes `erpnext_invoice_name` and `erpnext_payment_request` columns for ERPNext integration.
|
|
|
|
### `nwra_wholesale_pricing` — NW RA costs per state
|
|
Our wholesale cost for registered agent service (TBD from portal exploration).
|
|
|
|
### `canada_crtc_orders` — BC Canada orders
|
|
Separate table for CRTC Carrier Package orders with Canadian-specific fields.
|
|
Includes `erpnext_invoice_name` and `erpnext_payment_request` columns.
|
|
|
|
### `service_bundles` — Pre-configured service bundles
|
|
Bundle definitions (e.g., "Complete Formation + RA + EIN") with 20% discount applied.
|
|
|
|
### `bundle_orders` — Bundle order tracking
|
|
Links bundle purchases to individual service orders.
|
|
|
|
### `sales_agents` — Referral agent accounts
|
|
Agent ID (REF-XXXXX), contact info, commission tier, payout details.
|
|
|
|
### `commission_ledger` — Agent commission tracking
|
|
Per-order commission records: agent_id, order_id, service_type, flat_commission_amount, status (pending/paid).
|
|
|
|
### `payment_surcharges` — Payment method surcharge rates
|
|
ACH 0%, Card 3%, Klarna 5%, CashApp 3%, AmazonPay 3%, Crypto 0%.
|
|
|
|
### `accounting_advisors` — Advisor profiles
|
|
Accounting advisor details for post-formation consulting referrals.
|
|
|
|
### `accounting_support_accounts` — Client accounting support
|
|
Links clients to assigned accounting advisors.
|
|
|
|
### `conversation_flags` — Support conversation metadata
|
|
Flags for support conversations (escalation, priority, category).
|
|
|
|
## Formation Worker
|
|
|
|
`scripts/formation/formation_worker.py` is triggered by ERPNext webhooks (event-driven):
|
|
|
|
1. ERPNext webhook fires on `order.created` or `payment.confirmed`
|
|
2. Worker loads the appropriate state adapter
|
|
3. Verifies name availability
|
|
4. Submits the filing via Playwright
|
|
5. Updates DB with filing number and status
|
|
6. Generates operating agreement (if ordered)
|
|
7. Obtains EIN (if ordered)
|
|
8. Emails all documents to customer
|
|
|
|
**Human-paced delays:** 30-120 minutes between orders (configurable via env vars) to avoid appearing automated.
|
|
|
|
**Single-instance locking:** `fcntl.flock` on `/tmp/formation-worker.lock` prevents concurrent runs.
|
|
|
|
## Document Generation — PDF Conversion
|
|
|
|
- **Primary:** Windows DocServer (108.181.102.34, port 22422) — Office 365 Word COM automation via MinIO transport
|
|
- `docserver_worker.py` polls `to-convert/` bucket every 12 seconds
|
|
- Converts via Word COM, drops PDF in `converted/` bucket
|
|
- Heartbeat file at `minio://performancewest/docserver-heartbeat.json` (60s interval)
|
|
- Atomic uploads via `.tmp_` prefix + `copy_object` rename
|
|
- Task Scheduler: `PW-DocserverWorker` — auto-restart on failure
|
|
- **Fallback:** LibreOffice headless (`soffice --headless --convert-to pdf`) auto-activates when DocServer heartbeat stale (>5 min)
|
|
- **E2E tested:** 36KB DOCX → 82KB PDF in 12 seconds total round-trip
|
|
|
|
Operating agreements, CRTC letters, and other generated documents go through DocServer first. If the Windows VM is down, the system falls back to LibreOffice automatically.
|
|
|
|
## Northwest Registered Agent Integration
|
|
|
|
NW RA is used **only for registered agent service**, not for filings.
|
|
- Wholesale portal at accounts.northwestregisteredagent.com
|
|
- We order RA service for the customer's state after filing completes
|
|
- NW RA address for each state is stored in the state's `config.py`
|
|
|
|
## Operating Agreement Generator
|
|
|
|
Template-based using `python-docx`:
|
|
- Standard 10-article LLC operating agreement
|
|
- Variables filled from order data (entity name, state, members, etc.)
|
|
- Outputs `.docx` + `.pdf` (via Windows DocServer, LibreOffice fallback)
|
|
- Disclaimer: "This is not legal advice"
|
|
|
|
## EIN Obtainment
|
|
|
|
Playwright automation against IRS EIN Assistant:
|
|
- Available Mon-Fri 7am-10pm ET only
|
|
- Fills SS-4 equivalent online form
|
|
- Extracts EIN from confirmation page
|
|
- Saves PDF confirmation
|
|
|
|
## Pricing
|
|
|
|
| Component | Customer Pays | Our Cost |
|
|
|-----------|--------------|----------|
|
|
| State filing fee | Pass-through (varies $35-$725) | Same |
|
|
| Service fee (basic) | $179 | $0 (our margin) |
|
|
| Service fee (complete) | $399 | $0 (our margin) |
|
|
| RA service | $99/year (WY: $49/year) | ~$80/year (NW RA wholesale, TBD) |
|
|
| EIN | $49 | $0 (IRS is free) |
|
|
| Operating agreement | $99 | $0 (template-generated) |
|
|
| Expedited | Pass-through (varies by state) | Same |
|
|
|
|
## Service Bundles (20% Off)
|
|
|
|
Customers can purchase pre-configured bundles at a 20% discount off individual pricing:
|
|
|
|
| Bundle | Includes | Individual Total | Bundle Price |
|
|
|--------|----------|-----------------|-------------|
|
|
| **Starter** | Basic Formation + EIN | $198 | $158 |
|
|
| **Professional** | Complete Formation + EIN + RA (1yr) | $573 | $458 |
|
|
| **Full Package** | Complete Formation + EIN + RA (1yr) + Operating Agreement | $672 | $538 |
|
|
|
|
Bundle orders are tracked in `service_bundles` and `bundle_orders` tables.
|
|
|
|
## Payment Methods & Surcharges
|
|
|
|
| Method | Surcharge | Notes |
|
|
|--------|-----------|-------|
|
|
| **ACH Direct Debit** | 0% | Recommended — lowest cost |
|
|
| **Credit/Debit Card** | 3% | Via Adyen (Visa/MC/Amex + Apple Pay + Google Pay) |
|
|
| **Klarna (Pay in 4)** | 5% | Via Adyen — installment payments |
|
|
| **Cash App Pay** | 3% | Via Adyen |
|
|
| **Amazon Pay** | 3% | Via Adyen |
|
|
| **Crypto (BTC/ETH/USDC/USDT/MATIC/TRX/BNB/LTC/DOGE)** | 0% | Via SHKeeper (pay.performancewest.net) |
|
|
|
|
Surcharges are displayed at checkout before payment confirmation. Stored in `payment_surcharges` table. Surcharge injection is handled by the `performancewest_erpnext` Frappe app hook.
|
|
|
|
**SHKeeper** runs as a self-hosted instance in k3s (Kubernetes) at `pay.performancewest.net` for crypto payments. Supports any ERC-20/TRC-20/BEP-20 token. Webhook callbacks confirm payment and trigger formation workflow via ERPNext.
|
|
|
|
## Sales Agent Commission System
|
|
|
|
Sales agents refer clients using a unique referral code (format: `REF-XXXXX`).
|
|
|
|
### How It Works
|
|
|
|
1. Agent shares their referral link: `performancewest.net/form?ref=REF-XXXXX`
|
|
2. Client receives a **5% discount** on service fees
|
|
3. Agent earns a **flat commission per service** (not percentage-based)
|
|
|
|
### Commission Schedule
|
|
|
|
| Service | Agent Commission |
|
|
|---------|-----------------|
|
|
| Canada CRTC Package | $300 |
|
|
| Basic Formation | $50 |
|
|
| Bundle Purchase | $100 |
|
|
|
|
Commissions are tracked in ERPNext Commission Ledger DocType (with PostgreSQL backup in `commission_ledger`). Paid 14 days after order delivery via Relay ACH.
|
|
|
|
## Discount Code System
|
|
|
|
Discount codes can be created in ERPNext and applied at checkout:
|
|
|
|
- **Percentage-based** (e.g., 10% off)
|
|
- **Fixed amount** (e.g., $25 off)
|
|
- **Usage limits** (single-use, multi-use, or unlimited)
|
|
- **Expiration dates**
|
|
- Discount codes stack with agent referral discounts (capped at 15% total)
|
|
- Discounts apply to **service fees only** — never to state filing fees, government fees, or expedited fees
|
|
|
|
## Employment Pages
|
|
|
|
Employment/careers pages exist in the codebase but are **hidden in production** (not linked from navigation, not indexed by search engines). Gated behind `import.meta.env.DEV` flag. Reserved for future use.
|