Non-interconnected VoIP, satellite, paging, private radio, and other specialized services have different registration requirements. Show a yellow info box under Q1 directing these users to contact us for a custom assessment instead of using the wizard. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
875 lines
52 KiB
HTML
875 lines
52 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="description" content="Register your telecom carrier or ISP with the FCC — CORES/FRN, Form 499, state PUC, RMD, CPNI, CALEA. Optional business formation included.">
|
||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||
<title>FCC Carrier / ISP Registration — Performance West Inc.</title>
|
||
<script>
|
||
window.__PW_API = (function() {
|
||
var h = window.location.hostname;
|
||
if (h === "localhost" || h === "127.0.0.1") return "http://" + h + ":3001";
|
||
if (h === "dev.performancewest.net") return "https://api.dev.performancewest.net";
|
||
return "https://api.performancewest.net";
|
||
})();
|
||
</script>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
body{font-family:'Inter',system-ui,sans-serif;color:#1f2937;background:#f9fafb;line-height:1.6}
|
||
a{color:#1e3a5f;text-decoration:none}
|
||
.wrap{max-width:740px;margin:0 auto;padding:2rem 1.25rem 4rem}
|
||
h1{font-size:1.65rem;font-weight:700;color:#111827;margin-bottom:.25rem}
|
||
.subtitle{font-size:.9rem;color:#6b7280;margin-bottom:1.5rem}
|
||
.card{background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:1.25rem;margin-bottom:1rem}
|
||
.card h2{font-size:1.05rem;font-weight:700;color:#1e3a5f;margin-bottom:.75rem}
|
||
.label{display:block;font-size:.82rem;font-weight:600;color:#374151;margin-bottom:.3rem;margin-top:.6rem}
|
||
select,input[type=text],input[type=email],input[type=tel],input[type=number]{width:100%;padding:.5rem .7rem;border:1px solid #d1d5db;border-radius:8px;font-size:.88rem;background:#fff}
|
||
select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px rgba(30,58,95,.15)}
|
||
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.4rem;padding:.65rem 1.5rem;border-radius:8px;font-weight:600;font-size:.92rem;cursor:pointer;border:none;transition:all .15s;font-family:inherit}
|
||
.btn-primary{background:#1e3a5f;color:#fff}.btn-primary:hover{background:#162e4d}
|
||
.btn-primary:disabled{opacity:.5;cursor:not-allowed}
|
||
.btn-back{background:#e5e7eb;color:#374151}
|
||
.hidden{display:none}
|
||
.step-dots{display:flex;gap:.75rem;margin-bottom:1.5rem}
|
||
.step-dot{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.72rem;font-weight:700;background:#e5e7eb;color:#6b7280}
|
||
.step-dot.active{background:#1e3a5f;color:#fff}
|
||
.step-dot.done{background:#22c55e;color:#fff}
|
||
.q-card{padding:.65rem .85rem;border:1.5px solid #d1d5db;border-radius:8px;background:#fff;cursor:pointer;transition:all .15s;text-align:left;font-family:inherit;font-size:.88rem;color:#374151;width:100%;display:block;margin-bottom:.4rem}
|
||
.q-card:hover{border-color:#1e3a5f;background:#f0f4f8}
|
||
.q-card.selected{border-color:#1e3a5f;background:#eff6ff;color:#1e3a5f;font-weight:600}
|
||
.q-card.selected::before{content:"\2713 "}
|
||
.q-label{font-weight:600;color:#1f2937;font-size:.95rem;margin-bottom:.3rem}
|
||
.q-hint{font-size:.82rem;color:#6b7280;margin-bottom:.6rem}
|
||
.info-box{padding:.6rem .85rem;background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;font-size:.82rem;color:#1e40af;margin:.75rem 0}
|
||
.warn-box{padding:.6rem .85rem;background:#fef3c7;border:1px solid #fbbf24;border-radius:8px;font-size:.82rem;color:#92400e;margin:.75rem 0}
|
||
.svc-row{display:flex;align-items:center;gap:.6rem;padding:.5rem .7rem;border:1px solid #e5e7eb;border-radius:8px;margin-bottom:.35rem;font-size:.85rem}
|
||
.svc-row input{accent-color:#1e3a5f;width:16px;height:16px;flex-shrink:0}
|
||
.svc-row .svc-name{flex:1;font-weight:600}
|
||
.svc-row .svc-price{color:#059669;font-weight:600;white-space:nowrap}
|
||
.svc-row .svc-inc{color:#9ca3af;font-size:.78rem}
|
||
.svc-row.disabled{opacity:.5}
|
||
.price-box{background:#f0f4f8;border:2px solid #1e3a5f;border-radius:10px;padding:1rem;text-align:center;margin:1rem 0}
|
||
.price-box .price{font-size:1.6rem;font-weight:800;color:#1e3a5f}
|
||
.price-box .price-detail{font-size:.78rem;color:#6b7280}
|
||
.entity-toggle{display:flex;gap:.5rem;margin-bottom:.75rem}
|
||
.entity-toggle button{flex:1;padding:.6rem;border:2px solid #d1d5db;border-radius:8px;background:#fff;cursor:pointer;font-weight:600;font-size:.88rem;font-family:inherit;color:#374151;transition:all .15s}
|
||
.entity-toggle button.active{border-color:#1e3a5f;background:#eff6ff;color:#1e3a5f}
|
||
.recommend-box{padding:.75rem;background:#f0fdf4;border:2px solid #22c55e;border-radius:8px;margin:.75rem 0;font-size:.85rem}
|
||
.recommend-box strong{color:#166534}
|
||
.err{color:#dc2626;font-size:.82rem;margin-top:.5rem;display:none}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
<h1>FCC Carrier / ISP Registration</h1>
|
||
<p class="subtitle">Start your telecom carrier or ISP from scratch. We handle all federal and state registrations based on the services you plan to offer.</p>
|
||
|
||
<div class="step-dots">
|
||
<div class="step-dot active" id="dot-1">1</div>
|
||
<div class="step-dot" id="dot-2">2</div>
|
||
<div class="step-dot" id="dot-3">3</div>
|
||
<div class="step-dot" id="dot-4">4</div>
|
||
<div class="step-dot" id="dot-5">5</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<!-- STEP 1: Service Wizard -->
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<div id="step-1" class="card">
|
||
<h2>What services will you offer?</h2>
|
||
<p class="q-hint">Tell us what you plan to do and we'll determine exactly which registrations you need.</p>
|
||
|
||
<!-- Q1: Service types -->
|
||
<div id="q1">
|
||
<p class="q-label">What type of service will you offer?</p>
|
||
<p class="q-hint">Select all that apply.</p>
|
||
<label class="svc-row"><input type="checkbox" data-svc="voice"> <span class="svc-name">Voice / phone service</span> <span class="svc-inc">Interconnected VoIP, CLEC, IXC, or traditional telephony</span></label>
|
||
<label class="svc-row"><input type="checkbox" data-svc="broadband"> <span class="svc-name">Broadband internet service</span> <span class="svc-inc">ISP, fiber, fixed wireless, cable</span></label>
|
||
<div style="margin-top:.5rem;padding:.5rem .75rem;background:#fefce8;border:1px solid #fde68a;border-radius:8px;font-size:.82rem;color:#92400e;line-height:1.5">
|
||
<strong>Other service types?</strong> If you plan to offer non-interconnected VoIP, satellite, paging, private radio, or other specialized services, <a href="/contact?subject=FCC+Registration+Inquiry" style="color:#1e3a5f;font-weight:600;text-decoration:underline">contact us for a custom assessment</a> — registration requirements vary significantly for these services.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Q1b: Customer type — separate section, only shown after checking voice/broadband -->
|
||
<div id="q1b" class="hidden" style="margin-top:1rem">
|
||
<p class="q-label">Who are your customers?</p>
|
||
<p class="q-hint">This affects which FCC filings are required. If your voice and broadband customers are different, select the option that covers both.</p>
|
||
<button type="button" class="q-card" data-custtype="retail">Retail — I sell directly to businesses or consumers</button>
|
||
<button type="button" class="q-card" data-custtype="wholesale">Wholesale — I sell to other carriers, resellers, or CLECs</button>
|
||
<button type="button" class="q-card" data-custtype="both">Both — I have retail end users and wholesale/carrier customers</button>
|
||
</div>
|
||
|
||
<!-- Q2: Voice delivery (shown if voice selected) -->
|
||
<div id="q2" class="hidden" style="margin-top:1rem">
|
||
<p class="q-label">How will you deliver voice service?</p>
|
||
<button type="button" class="q-card" data-voice="reseller">I'll resell service from a provider like Bandwidth, Telnyx, Twilio, or a UCaaS platform</button>
|
||
<button type="button" class="q-card" data-voice="own_switch">I'll run my own switching equipment (softswitch, SBC, PBX) and buy wholesale trunks</button>
|
||
<button type="button" class="q-card" data-voice="ucaas">I'll offer a hosted/UCaaS platform and interconnect directly with carriers</button>
|
||
<button type="button" class="q-card" data-voice="unsure">Not sure yet — I'm still planning</button>
|
||
</div>
|
||
|
||
<!-- Q3: Infrastructure needs (shown if own_switch or ucaas) -->
|
||
<div id="q3" class="hidden" style="margin-top:1rem">
|
||
<p class="q-label">Will you need any of these?</p>
|
||
<p class="q-hint">Check all that apply. These determine whether you need your own OCN and STIR/SHAKEN certificate.</p>
|
||
<label class="svc-row"><input type="checkbox" data-infra="lcr"> <span class="svc-name">Least-cost routing (LCR)</span> <span class="svc-inc">Route across multiple wholesale providers</span></label>
|
||
<label class="svc-row"><input type="checkbox" data-infra="own_dids"> <span class="svc-name">Your own phone numbers (DID blocks)</span> <span class="svc-inc">From number providers like Bandwidth, Inteliquent</span></label>
|
||
<label class="svc-row"><input type="checkbox" data-infra="interconnect"> <span class="svc-name">Direct carrier interconnection</span> <span class="svc-inc">SIP trunking you sell to other carriers</span></label>
|
||
<label class="svc-row"><input type="checkbox" data-infra="stir_sign"> <span class="svc-name">Sign outbound calls with your own identity</span> <span class="svc-inc">STIR/SHAKEN certificate for call authentication</span></label>
|
||
<div id="ocn-explain" class="info-box hidden">
|
||
<strong>Why do wholesale voice vendors require an OCN?</strong><br>
|
||
An Operating Company Number identifies your company in the telecom numbering system. Major voice providers like Bandwidth, Telnyx, Lumen, and Peerless require it to open a wholesale account, port numbers, and assign DID blocks. Without an OCN, you're limited to reseller-tier access.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Q4: Broadband type (shown if broadband selected) -->
|
||
<div id="q4" class="hidden" style="margin-top:1rem">
|
||
<p class="q-label">How will you provide internet service?</p>
|
||
<button type="button" class="q-card" data-bb="reseller">I resell another provider's internet (white-label fiber, cable resale)</button>
|
||
<button type="button" class="q-card" data-bb="facilities">I own or lease network infrastructure (fiber, fixed wireless, towers)</button>
|
||
<button type="button" class="q-card" data-bb="cloud">I provide cloud services only (SaaS, hosting) — no last-mile internet</button>
|
||
<div id="cloud-warn" class="warn-box hidden">
|
||
Based on your description, you may not need FCC carrier registration. Cloud and SaaS services are generally not regulated as telecommunications. <a href="/contact" style="font-weight:600;text-decoration:underline">Contact us for a free assessment.</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Q5: Customer geography (retail) / Operating scope (wholesale) -->
|
||
<div id="q5" class="hidden" style="margin-top:1rem">
|
||
<div id="q5-retail">
|
||
<p class="q-label">Where will your end-user customers be located?</p>
|
||
<p class="q-hint">Retail telecom providers must register with the state PUC in each state where they serve customers. State PUC registration is triggered by customer location — your service terminates in their state using local infrastructure, creating regulatory nexus regardless of where you're incorporated.</p>
|
||
<button type="button" class="q-card" data-states="1">One state only — all my customers will be in a single state</button>
|
||
<button type="button" class="q-card" data-states="few">A few states (2–5) — I know which states I'll serve initially</button>
|
||
<button type="button" class="q-card" data-states="nationwide">Nationwide — I'll accept customers from any US state</button>
|
||
<div id="state-puc-explain" class="hidden" style="margin-top:.5rem">
|
||
<div class="info-box">
|
||
<strong>State PUC Registration</strong><br>
|
||
About 27 states require full certification and tariff filing (CA, NY, PA, FL, IL, etc.), ~9 require registration only, and a handful (TX, UT, VA, WY) have minimal requirements. You don't need to register in all 50 states on day one — start with the states where you'll have customers, then expand as you grow. We handle state PUC filings at $399/state.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="q5-wholesale" class="hidden">
|
||
<p class="q-label">Where will your carrier customers be located?</p>
|
||
<p class="q-hint">Wholesale carriers generally don't need state PUC registration — the retail carrier serving end users holds the state obligation, not you. Your FCC registrations (CORES, Form 499, RMD) cover you at the federal level.</p>
|
||
<button type="button" class="q-card" data-states="1">Primarily one region</button>
|
||
<button type="button" class="q-card" data-states="few">A few regions</button>
|
||
<button type="button" class="q-card" data-states="nationwide">Nationwide</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Q6: International services -->
|
||
<div id="q6" class="hidden" style="margin-top:1rem">
|
||
<p class="q-label">Will you offer international services?</p>
|
||
<p class="q-hint">Calls to/from other countries, international data transit, or serving customers outside the US.</p>
|
||
<button type="button" class="q-card" data-intl="yes">Yes — international calling, transit, or foreign customers</button>
|
||
<button type="button" class="q-card" data-intl="no">No — domestic US only</button>
|
||
<div id="intl-explain" class="hidden">
|
||
<div class="info-box">
|
||
<strong>International Section 214 Authorization</strong><br>
|
||
The FCC requires a separate International Section 214 license to provide telecommunications services between the US and foreign points. This applies to international voice termination, international toll-free, global SIP trunking, and international data transit. Domestic-only carriers do not need this.
|
||
</div>
|
||
<div style="margin-top:.6rem;padding:.75rem;background:linear-gradient(135deg,#fef2f2,#fff1f2);border:2px solid #fca5a5;border-radius:10px">
|
||
<div style="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem">
|
||
<img src="/images/flags/canada.png" alt="Canada" style="width:24px;height:auto;border-radius:3px;box-shadow:0 1px 2px rgba(0,0,0,.15)">
|
||
<strong style="font-size:.9rem;color:#991b1b">Expand into Canada?</strong>
|
||
</div>
|
||
<p style="font-size:.82rem;color:#7f1d1d;margin-bottom:.5rem;line-height:1.5">
|
||
If you plan to terminate voice traffic to Canada, sell services to Canadian customers, or operate infrastructure in Canada, you need a <strong>CRTC registration</strong> (Canada's FCC equivalent). Canadian carriers get direct access to Canadian DIDs, local number portability, and CRTC interconnection — often at lower termination rates than routing through a US-based international gateway.
|
||
</p>
|
||
<p style="font-size:.8rem;color:#9a3412;margin-bottom:.5rem">
|
||
Our Canada CRTC package includes BC incorporation, CRTC telecom registration, and a .ca domain — everything you need to operate as a licensed Canadian carrier.
|
||
</p>
|
||
<a href="/order/canada-crtc" target="_blank" style="display:inline-block;padding:.4rem 1rem;background:#dc2626;color:#fff;font-weight:600;font-size:.82rem;border-radius:6px;text-decoration:none">Learn More — Canada CRTC ($3,899) →</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top:1rem;text-align:right">
|
||
<button class="btn btn-primary" id="btn-next-1">See What You Need →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<!-- STEP 2: Registration Checklist -->
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<div id="step-2" class="card hidden">
|
||
<h2>Your Registration Package</h2>
|
||
<p class="q-hint">Based on your answers, here's what you need. You can add or remove services.</p>
|
||
|
||
<p style="font-size:.78rem;font-weight:600;color:#059669;text-transform:uppercase;margin-bottom:.5rem">Included in your $1,299 package</p>
|
||
<div id="base-services"></div>
|
||
|
||
<p style="font-size:.78rem;font-weight:600;color:#d97706;text-transform:uppercase;margin:.75rem 0 .5rem">Optional add-ons (additional cost)</p>
|
||
<div id="addon-services"></div>
|
||
|
||
<div class="price-box">
|
||
<div class="price" id="total-price">$1,299</div>
|
||
<div class="price-detail" id="price-detail">Base registration package</div>
|
||
</div>
|
||
|
||
<div style="display:flex;gap:.5rem;justify-content:flex-end">
|
||
<button class="btn btn-back" id="btn-back-2">← Back</button>
|
||
<button class="btn btn-primary" id="btn-next-2">Choose Entity →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<!-- STEP 3: Entity Choice -->
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<div id="step-3" class="card hidden">
|
||
<h2>Your Business Entity</h2>
|
||
<p class="q-hint">We'll file registrations under this entity. You can use an existing one or we'll form a new one.</p>
|
||
|
||
<div class="entity-toggle">
|
||
<button type="button" id="entity-existing" class="active">Use Existing Entity</button>
|
||
<button type="button" id="entity-new">Form a New Entity</button>
|
||
</div>
|
||
|
||
<!-- Existing entity search -->
|
||
<div id="entity-existing-form">
|
||
<label class="label">Search by FRN or company name</label>
|
||
<input type="text" id="entity-search" placeholder="e.g. 0012345678 or Acme Telecom LLC">
|
||
<div id="entity-search-results" style="margin-top:.5rem"></div>
|
||
|
||
<div id="entity-details" class="hidden" style="margin-top:.75rem;padding:.75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px">
|
||
<label class="label">Legal Name</label>
|
||
<input type="text" id="ex-legal-name">
|
||
<label class="label">EIN / TIN</label>
|
||
<input type="text" id="ex-ein" placeholder="12-3456789 or 123-45-6789">
|
||
<label class="label">State of Formation</label>
|
||
<input type="text" id="ex-formation-state" maxlength="2" placeholder="WY">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- New formation -->
|
||
<div id="entity-new-form" class="hidden">
|
||
<!-- Nexus guidance questions -->
|
||
<div id="formation-guide">
|
||
<p class="q-label">Where do you live / primary office?</p>
|
||
<select id="home-state" style="margin-bottom:.5rem">
|
||
<option value="">Select state...</option>
|
||
</select>
|
||
|
||
<p class="q-label">Do you have employees or a physical office there?</p>
|
||
<div style="display:flex;gap:.5rem;margin-bottom:.5rem">
|
||
<button type="button" class="q-card" data-nexus="yes" style="flex:1">Yes</button>
|
||
<button type="button" class="q-card" data-nexus="no" style="flex:1">No — remote/virtual</button>
|
||
</div>
|
||
|
||
<div id="size-q" class="hidden">
|
||
<p class="q-label">Expected first-year size?</p>
|
||
<button type="button" class="q-card" data-size="small">Small (< $50K, 1-2 people)</button>
|
||
<button type="button" class="q-card" data-size="medium">Medium ($50K-$500K, small team)</button>
|
||
<button type="button" class="q-card" data-size="large">Large (> $500K, multiple states)</button>
|
||
</div>
|
||
|
||
<div id="state-recommend" class="recommend-box hidden"></div>
|
||
</div>
|
||
|
||
<label class="label">Formation State</label>
|
||
<select id="new-formation-state"></select>
|
||
|
||
<label class="label">Entity Type</label>
|
||
<select id="new-entity-type">
|
||
<option value="llc">LLC (recommended for most carriers)</option>
|
||
<option value="corporation">Corporation</option>
|
||
</select>
|
||
|
||
<label class="label">Entity Name (leave blank for numbered)</label>
|
||
<input type="text" id="new-entity-name" placeholder="e.g. Acme Telecom LLC">
|
||
|
||
<div id="formation-fee-display" style="margin-top:.5rem;font-size:.85rem;color:#6b7280"></div>
|
||
</div>
|
||
|
||
<div style="margin-top:1rem;display:flex;gap:.5rem;justify-content:flex-end">
|
||
<button class="btn btn-back" id="btn-back-3">← Back</button>
|
||
<button class="btn btn-primary" id="btn-next-3">Contact Info →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<!-- STEP 4: Contact & Officer -->
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<div id="step-4" class="card hidden">
|
||
<h2>Contact & Officer Information</h2>
|
||
<p class="q-hint">This information is used on all FCC filings and state registrations.</p>
|
||
|
||
<label class="label">Contact Name *</label>
|
||
<input type="text" id="contact-name">
|
||
<label class="label">Contact Email *</label>
|
||
<input type="email" id="contact-email">
|
||
<label class="label">Contact Phone *</label>
|
||
<input type="tel" id="contact-phone">
|
||
<label class="label">Title</label>
|
||
<input type="text" id="contact-title" value="Chief Executive Officer">
|
||
|
||
<label class="label" style="margin-top:1rem">Business Address *</label>
|
||
<input type="text" id="addr-street" placeholder="Street address">
|
||
<div style="display:flex;gap:.5rem;margin-top:.3rem">
|
||
<input type="text" id="addr-city" placeholder="City" style="flex:2">
|
||
<input type="text" id="addr-state" placeholder="ST" maxlength="2" style="flex:.5">
|
||
<input type="text" id="addr-zip" placeholder="ZIP" style="flex:1">
|
||
</div>
|
||
|
||
<div style="margin-top:1rem;display:flex;gap:.5rem;justify-content:flex-end">
|
||
<button class="btn btn-back" id="btn-back-4">← Back</button>
|
||
<button class="btn btn-primary" id="btn-next-4">Review →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<!-- STEP 5: Review & Payment -->
|
||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||
<div id="step-5" class="card hidden">
|
||
<h2>Review & Checkout</h2>
|
||
<div id="review-content"></div>
|
||
|
||
<label style="display:flex;align-items:flex-start;gap:.5rem;padding:.65rem;margin-top:.75rem;border:1px solid #e5e7eb;border-radius:8px;cursor:pointer;font-size:.75rem;color:#6b7280;line-height:1.5">
|
||
<input type="checkbox" id="engage-check" required style="margin-top:2px;accent-color:#1e3a5f">
|
||
<span>I authorize Performance West Inc. to prepare and submit regulatory filings on my behalf as described above. I understand Performance West provides compliance consulting services, not legal advice or legal representation. I confirm the information I provide is accurate to the best of my knowledge. <a href="/terms" target="_blank" style="color:#1e3a5f;text-decoration:underline">Terms of Service</a></span>
|
||
</label>
|
||
|
||
<div style="margin-top:.75rem">
|
||
<label class="label">Payment Method</label>
|
||
<div style="display:flex;flex-direction:column;gap:.3rem">
|
||
<label class="svc-row"><input type="radio" name="pay" value="ach" checked> <span class="svc-name">ACH Bank Transfer</span> <span style="color:#059669;font-size:.78rem">No fee</span></label>
|
||
<label class="svc-row"><input type="radio" name="pay" value="card"> <span class="svc-name">Credit / Debit Card</span> <span style="color:#9ca3af;font-size:.78rem">+3%</span></label>
|
||
<label class="svc-row"><input type="radio" name="pay" value="paypal"> <span class="svc-name">PayPal</span> <span style="color:#9ca3af;font-size:.78rem">+3%</span></label>
|
||
<label class="svc-row"><input type="radio" name="pay" value="crypto"> <span class="svc-name">Cryptocurrency</span> <span style="color:#059669;font-size:.78rem">No fee</span></label>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top:1rem;display:flex;gap:.5rem;justify-content:flex-end">
|
||
<button class="btn btn-back" id="btn-back-5">← Back</button>
|
||
<button class="btn btn-primary" id="btn-pay">Continue to Payment</button>
|
||
</div>
|
||
<p class="err" id="checkout-err"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function() {
|
||
var API = window.__PW_API;
|
||
|
||
// ── State data ──
|
||
var STATES = [
|
||
['AL','Alabama'],['AK','Alaska'],['AZ','Arizona'],['AR','Arkansas'],['CA','California'],
|
||
['CO','Colorado'],['CT','Connecticut'],['DE','Delaware'],['DC','District of Columbia'],['FL','Florida'],
|
||
['GA','Georgia'],['HI','Hawaii'],['ID','Idaho'],['IL','Illinois'],['IN','Indiana'],
|
||
['IA','Iowa'],['KS','Kansas'],['KY','Kentucky'],['LA','Louisiana'],['ME','Maine'],
|
||
['MD','Maryland'],['MA','Massachusetts'],['MI','Michigan'],['MN','Minnesota'],['MS','Mississippi'],
|
||
['MO','Missouri'],['MT','Montana'],['NE','Nebraska'],['NV','Nevada'],['NH','New Hampshire'],
|
||
['NJ','New Jersey'],['NM','New Mexico'],['NY','New York'],['NC','North Carolina'],['ND','North Dakota'],
|
||
['OH','Ohio'],['OK','Oklahoma'],['OR','Oregon'],['PA','Pennsylvania'],['RI','Rhode Island'],
|
||
['SC','South Carolina'],['SD','South Dakota'],['TN','Tennessee'],['TX','Texas'],['UT','Utah'],
|
||
['VT','Vermont'],['VA','Virginia'],['WA','Washington'],['WV','West Virginia'],['WI','Wisconsin'],['WY','Wyoming']
|
||
];
|
||
|
||
// Populate state dropdowns
|
||
['home-state','new-formation-state'].forEach(function(id) {
|
||
var sel = document.getElementById(id);
|
||
STATES.forEach(function(s) {
|
||
var opt = document.createElement('option');
|
||
opt.value = s[0]; opt.textContent = s[0] + ' — ' + s[1];
|
||
sel.appendChild(opt);
|
||
});
|
||
});
|
||
|
||
// ── Wizard state ──
|
||
var wizard = {
|
||
serviceTypes: [], customerType: 'retail', international: false,
|
||
voiceDelivery: '', infraNeeds: [],
|
||
broadbandType: '', operatingStates: '',
|
||
// Derived
|
||
needsRmd: false, needsCpni: false, needsCalea: false, needsBdc: false,
|
||
needsOcn: false, needsStirShaken: false, needs214: false,
|
||
// Entity
|
||
entitySource: 'existing', formationState: '', entityType: 'llc',
|
||
entityName: '', ein: '', frn: '', legalName: '',
|
||
// Contact
|
||
contactName: '', contactEmail: '', contactPhone: '', contactTitle: 'Chief Executive Officer',
|
||
addrStreet: '', addrCity: '', addrState: '', addrZip: '',
|
||
// Pricing
|
||
baseFee: 129900, formationFee: 0, stateFee: 0, pucFee: 0, addonFee: 0,
|
||
};
|
||
|
||
// ── Step navigation ──
|
||
function showStep(n) {
|
||
[1,2,3,4,5].forEach(function(i) {
|
||
document.getElementById('step-' + i).classList.toggle('hidden', i !== n);
|
||
var dot = document.getElementById('dot-' + i);
|
||
dot.className = 'step-dot' + (i < n ? ' done' : i === n ? ' active' : '');
|
||
});
|
||
document.querySelector('.wrap').scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||
}
|
||
|
||
// ── Q1: Service types ──
|
||
function updateVoiceQuestions() {
|
||
var hasVoice = wizard.serviceTypes.indexOf('voice') >= 0;
|
||
var isWholesaleOnly = wizard.customerType === 'wholesale';
|
||
// Wholesale voice carriers run their own switching by definition — skip Q2, show Q3 directly
|
||
if (hasVoice && isWholesaleOnly) {
|
||
document.getElementById('q2').classList.add('hidden');
|
||
document.getElementById('q3').classList.remove('hidden');
|
||
wizard.voiceDelivery = 'own_switch'; // implied for wholesale
|
||
} else if (hasVoice) {
|
||
document.getElementById('q2').classList.remove('hidden');
|
||
// Q3 visibility still controlled by Q2 answer
|
||
} else {
|
||
document.getElementById('q2').classList.add('hidden');
|
||
document.getElementById('q3').classList.add('hidden');
|
||
}
|
||
}
|
||
document.querySelectorAll('[data-svc]').forEach(function(cb) {
|
||
cb.addEventListener('change', function() {
|
||
wizard.serviceTypes = Array.from(document.querySelectorAll('[data-svc]:checked')).map(function(c) { return c.dataset.svc; });
|
||
var hasVoice = wizard.serviceTypes.indexOf('voice') >= 0;
|
||
var hasBroadband = wizard.serviceTypes.indexOf('broadband') >= 0;
|
||
document.getElementById('q1b').classList.toggle('hidden', !hasVoice && !hasBroadband);
|
||
updateVoiceQuestions();
|
||
document.getElementById('q4').classList.toggle('hidden', !hasBroadband);
|
||
if (hasVoice || hasBroadband) document.getElementById('q5').classList.remove('hidden');
|
||
});
|
||
});
|
||
// ── Q1b: Customer type ──
|
||
document.querySelectorAll('[data-custtype]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
document.querySelectorAll('[data-custtype]').forEach(function(b) { b.classList.remove('selected'); });
|
||
btn.classList.add('selected');
|
||
wizard.customerType = btn.dataset.custtype;
|
||
// Re-evaluate whether to show Q2 or skip to Q3
|
||
updateVoiceQuestions();
|
||
// Toggle Q5 variant: retail vs wholesale
|
||
var isWholesaleOnly = wizard.customerType === 'wholesale';
|
||
document.getElementById('q5-retail').classList.toggle('hidden', isWholesaleOnly);
|
||
document.getElementById('q5-wholesale').classList.toggle('hidden', !isWholesaleOnly);
|
||
});
|
||
});
|
||
|
||
// ── Q2: Voice delivery ──
|
||
document.querySelectorAll('[data-voice]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
document.querySelectorAll('[data-voice]').forEach(function(b) { b.classList.remove('selected'); });
|
||
btn.classList.add('selected');
|
||
wizard.voiceDelivery = btn.dataset.voice;
|
||
var showQ3 = wizard.voiceDelivery === 'own_switch' || wizard.voiceDelivery === 'ucaas';
|
||
document.getElementById('q3').classList.toggle('hidden', !showQ3);
|
||
});
|
||
});
|
||
|
||
// ── Q3: Infrastructure needs ──
|
||
document.querySelectorAll('[data-infra]').forEach(function(cb) {
|
||
cb.addEventListener('change', function() {
|
||
wizard.infraNeeds = Array.from(document.querySelectorAll('[data-infra]:checked')).map(function(c) { return c.dataset.infra; });
|
||
var needsOcn = wizard.infraNeeds.indexOf('lcr') >= 0 || wizard.infraNeeds.indexOf('own_dids') >= 0 ||
|
||
wizard.infraNeeds.indexOf('interconnect') >= 0 || wizard.infraNeeds.indexOf('stir_sign') >= 0;
|
||
document.getElementById('ocn-explain').classList.toggle('hidden', !needsOcn);
|
||
});
|
||
});
|
||
|
||
// ── Q4: Broadband type ──
|
||
document.querySelectorAll('[data-bb]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
document.querySelectorAll('[data-bb]').forEach(function(b) { b.classList.remove('selected'); });
|
||
btn.classList.add('selected');
|
||
wizard.broadbandType = btn.dataset.bb;
|
||
document.getElementById('cloud-warn').classList.toggle('hidden', wizard.broadbandType !== 'cloud');
|
||
});
|
||
});
|
||
|
||
// ── Q5: Customer geography ──
|
||
document.querySelectorAll('[data-states]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
document.querySelectorAll('[data-states]').forEach(function(b) { b.classList.remove('selected'); });
|
||
btn.classList.add('selected');
|
||
wizard.operatingStates = btn.dataset.states;
|
||
// Show PUC info for multi-state or nationwide
|
||
document.getElementById('state-puc-explain').classList.toggle('hidden', btn.dataset.states === '1');
|
||
document.getElementById('q6').classList.remove('hidden');
|
||
});
|
||
});
|
||
|
||
// ── Q6: International services ──
|
||
document.querySelectorAll('[data-intl]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
document.querySelectorAll('[data-intl]').forEach(function(b) { b.classList.remove('selected'); });
|
||
btn.classList.add('selected');
|
||
wizard.international = btn.dataset.intl === 'yes';
|
||
document.getElementById('intl-explain').classList.toggle('hidden', !wizard.international);
|
||
});
|
||
});
|
||
|
||
// ── Step 1 → 2: Derive registrations ──
|
||
document.getElementById('btn-next-1').addEventListener('click', function() {
|
||
if (wizard.serviceTypes.length === 0) { alert('Please select at least one service type.'); return; }
|
||
|
||
var hasVoice = wizard.serviceTypes.indexOf('voice') >= 0;
|
||
var hasBroadband = wizard.serviceTypes.indexOf('broadband') >= 0;
|
||
var hasRetail = wizard.customerType === 'retail' || wizard.customerType === 'both';
|
||
var wholesaleOnly = wizard.customerType === 'wholesale';
|
||
|
||
// Voice carriers always need RMD + CPNI regardless of retail/wholesale
|
||
wizard.needsRmd = hasVoice;
|
||
wizard.needsCpni = hasVoice;
|
||
// CALEA: carriers serving end users (retail/both). Wholesale-only "carrier's carrier"
|
||
// providers are exempt as "interconnecting carriers" under CALEA.
|
||
// Also required for facilities-based broadband serving end users.
|
||
wizard.needsCalea = !wholesaleOnly && (hasVoice || (hasBroadband && wizard.broadbandType === 'facilities'));
|
||
// BDC only for broadband providers with retail end users
|
||
wizard.needsBdc = hasBroadband && hasRetail;
|
||
// International Section 214 authorization
|
||
wizard.needs214 = wizard.international;
|
||
wizard.needsOcn = wizard.infraNeeds.indexOf('lcr') >= 0 || wizard.infraNeeds.indexOf('own_dids') >= 0 ||
|
||
wizard.infraNeeds.indexOf('interconnect') >= 0 || wizard.infraNeeds.indexOf('stir_sign') >= 0;
|
||
wizard.needsStirShaken = wizard.infraNeeds.indexOf('stir_sign') >= 0 || wizard.needsOcn;
|
||
|
||
buildChecklist();
|
||
showStep(2);
|
||
});
|
||
|
||
// ── Build registration checklist (Step 2) ──
|
||
function buildChecklist() {
|
||
var base = document.getElementById('base-services');
|
||
var addons = document.getElementById('addon-services');
|
||
base.innerHTML = ''; addons.innerHTML = '';
|
||
|
||
function baseSvc(name, desc, relevant) {
|
||
var row = document.createElement('div');
|
||
row.className = 'svc-row' + (relevant ? '' : ' disabled');
|
||
row.innerHTML = '<span style="color:#059669;font-size:1rem;flex-shrink:0">\u2713</span> ' +
|
||
'<span class="svc-name">' + name + '</span> ' +
|
||
'<span class="svc-inc">' + (relevant ? 'Included' : 'Included (not needed for your service type)') + '</span>';
|
||
base.appendChild(row);
|
||
}
|
||
|
||
function addonRow(name, price, key, preChecked) {
|
||
var row = document.createElement('label');
|
||
row.className = 'svc-row';
|
||
row.innerHTML = '<input type="checkbox" data-reg="' + key + '" ' + (preChecked ? 'checked' : '') + '> ' +
|
||
'<span class="svc-name">' + name + '</span> ' +
|
||
'<span class="svc-price">+$' + (price / 100).toLocaleString() + '</span>';
|
||
row.querySelector('input').addEventListener('change', updatePrice);
|
||
addons.appendChild(row);
|
||
}
|
||
|
||
// All included in base $1,299
|
||
baseSvc('CORES / FRN Registration', 'FCC Registration Number', true);
|
||
baseSvc('Form 499 Initial Registration', 'USAC Universal Service Fund enrollment', true);
|
||
baseSvc('D.C. Registered Agent (Annual)', 'FCC process-of-service address', true);
|
||
baseSvc('RMD Registration', 'Robocall Mitigation Database', wizard.needsRmd);
|
||
baseSvc('CPNI Annual Certification', 'Customer data protection compliance', wizard.needsCpni);
|
||
baseSvc('CALEA SSI Plan', 'Lawful intercept compliance plan', wizard.needsCalea);
|
||
baseSvc('BDC Filing', 'Broadband Data Collection', wizard.needsBdc);
|
||
|
||
// Add-ons (extra cost)
|
||
addonRow('STIR/SHAKEN Implementation', 49900, 'stir_shaken', wizard.needsStirShaken);
|
||
addonRow('NECA OCN Registration', 265000, 'ocn', wizard.needsOcn);
|
||
addonRow('International Section 214 Authorization', 149900, 'intl_214', wizard.needs214);
|
||
addonRow('State PUC Registration (per state)', 39900, 'state_puc', false);
|
||
|
||
updatePrice();
|
||
}
|
||
|
||
function updatePrice() {
|
||
var total = wizard.baseFee;
|
||
var details = ['Base: $1,299'];
|
||
|
||
// Add-on fees from checked checkboxes
|
||
document.querySelectorAll('[data-reg]').forEach(function(cb) {
|
||
if (!cb.checked) return;
|
||
var key = cb.dataset.reg;
|
||
if (key === 'stir_shaken') { total += 49900; details.push('STIR/SHAKEN: +$499'); }
|
||
if (key === 'ocn') { total += 265000; details.push('OCN: +$2,650'); }
|
||
if (key === 'intl_214') { total += 149900; details.push('Intl 214: +$1,499'); }
|
||
if (key === 'state_puc') { total += 39900; details.push('State PUC: +$399/state'); }
|
||
});
|
||
|
||
// Formation fees (if new entity)
|
||
if (wizard.formationFee || wizard.stateFee) {
|
||
total += wizard.formationFee + wizard.stateFee;
|
||
details.push('Formation: +$' + ((wizard.formationFee + wizard.stateFee) / 100).toLocaleString());
|
||
}
|
||
|
||
document.getElementById('total-price').textContent = '$' + (total / 100).toLocaleString();
|
||
document.getElementById('price-detail').textContent = details.join(' · ');
|
||
}
|
||
|
||
// ── Step 2 → 3 ──
|
||
document.getElementById('btn-next-2').addEventListener('click', function() { showStep(3); });
|
||
document.getElementById('btn-back-2').addEventListener('click', function() { showStep(1); });
|
||
|
||
// ── Step 3: Entity toggle ──
|
||
document.getElementById('entity-existing').addEventListener('click', function() {
|
||
wizard.entitySource = 'existing';
|
||
this.classList.add('active');
|
||
document.getElementById('entity-new').classList.remove('active');
|
||
document.getElementById('entity-existing-form').classList.remove('hidden');
|
||
document.getElementById('entity-new-form').classList.add('hidden');
|
||
wizard.formationFee = 0; wizard.stateFee = 0;
|
||
updatePrice();
|
||
});
|
||
document.getElementById('entity-new').addEventListener('click', function() {
|
||
wizard.entitySource = 'new_formation';
|
||
this.classList.add('active');
|
||
document.getElementById('entity-existing').classList.remove('active');
|
||
document.getElementById('entity-new-form').classList.remove('hidden');
|
||
document.getElementById('entity-existing-form').classList.add('hidden');
|
||
});
|
||
|
||
// ── Formation state guidance ──
|
||
document.querySelectorAll('[data-nexus]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
document.querySelectorAll('[data-nexus]').forEach(function(b) { b.classList.remove('selected'); });
|
||
btn.classList.add('selected');
|
||
if (btn.dataset.nexus === 'yes') {
|
||
var st = document.getElementById('home-state').value;
|
||
showRecommendation(st, 'Your home state (' + st + ') is recommended since you already have business presence there. This avoids foreign qualification fees.');
|
||
document.getElementById('size-q').classList.add('hidden');
|
||
} else {
|
||
document.getElementById('size-q').classList.remove('hidden');
|
||
}
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('[data-size]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
document.querySelectorAll('[data-size]').forEach(function(b) { b.classList.remove('selected'); });
|
||
btn.classList.add('selected');
|
||
var home = document.getElementById('home-state').value;
|
||
if (btn.dataset.size === 'small') {
|
||
showRecommendation('WY', 'Wyoming is recommended for small operations — $100 formation fee, no state income tax, $60/yr annual report, strong privacy. You\'ll need foreign qualification if you expand to other states.');
|
||
} else if (btn.dataset.size === 'medium') {
|
||
showRecommendation(home || 'DE', (home ? home : 'Delaware') + ' is recommended. ' + (home ? 'Forming in your home state avoids foreign qualification fees.' : 'Delaware is investor-friendly with a specialized business court.'));
|
||
} else {
|
||
showRecommendation(home || 'WY', 'Forming in your home state is recommended for larger operations — you\'ll need foreign qualifications in other states regardless.');
|
||
}
|
||
});
|
||
});
|
||
|
||
function showRecommendation(state, reason) {
|
||
var box = document.getElementById('state-recommend');
|
||
box.innerHTML = '<strong>Recommended: ' + state + '</strong><br>' + reason +
|
||
'<br><button type="button" onclick="document.getElementById(\'new-formation-state\').value=\'' + state + '\';updateFormationFee()" style="margin-top:.5rem;font-size:.82rem;font-weight:600;color:#1e3a5f;background:none;border:none;cursor:pointer;text-decoration:underline">Use ' + state + '</button>';
|
||
box.classList.remove('hidden');
|
||
document.getElementById('new-formation-state').value = state;
|
||
updateFormationFee();
|
||
}
|
||
|
||
// ── Formation fee lookup ──
|
||
var stateFeesCache = null;
|
||
window.updateFormationFee = function() {
|
||
var state = document.getElementById('new-formation-state').value;
|
||
var type = document.getElementById('new-entity-type').value;
|
||
if (!state) return;
|
||
if (stateFeesCache) {
|
||
applyFormationFee(state, type);
|
||
} else {
|
||
fetch(API + '/api/v1/states').then(function(r) { return r.json(); }).then(function(d) {
|
||
stateFeesCache = d;
|
||
applyFormationFee(state, type);
|
||
}).catch(function() {});
|
||
}
|
||
};
|
||
function applyFormationFee(state, type) {
|
||
if (!stateFeesCache) return;
|
||
var fees = (stateFeesCache.states || stateFeesCache).find(function(s) { return s.state_code === state; });
|
||
if (!fees) return;
|
||
var fee = type === 'corporation' ? (fees.corp_formation_fee || fees.llc_formation_fee) : fees.llc_formation_fee;
|
||
wizard.stateFee = fee || 0;
|
||
wizard.formationFee = 2500; // $25 filing markup
|
||
document.getElementById('formation-fee-display').textContent =
|
||
'State filing fee: $' + ((wizard.stateFee) / 100).toLocaleString() + ' + $25 filing service = $' + ((wizard.stateFee + 2500) / 100).toLocaleString();
|
||
updatePrice();
|
||
}
|
||
document.getElementById('new-formation-state').addEventListener('change', function() { updateFormationFee(); });
|
||
document.getElementById('new-entity-type').addEventListener('change', function() { updateFormationFee(); });
|
||
|
||
// ── Step 3 → 4 ──
|
||
document.getElementById('btn-next-3').addEventListener('click', function() { showStep(4); });
|
||
document.getElementById('btn-back-3').addEventListener('click', function() { showStep(2); });
|
||
|
||
// ── Step 4 → 5 ──
|
||
document.getElementById('btn-next-4').addEventListener('click', function() {
|
||
var name = document.getElementById('contact-name').value.trim();
|
||
var email = document.getElementById('contact-email').value.trim();
|
||
var phone = document.getElementById('contact-phone').value.trim();
|
||
if (!name || !email || !phone) { alert('Please fill in contact name, email, and phone.'); return; }
|
||
wizard.contactName = name; wizard.contactEmail = email; wizard.contactPhone = phone;
|
||
wizard.contactTitle = document.getElementById('contact-title').value.trim();
|
||
wizard.addrStreet = document.getElementById('addr-street').value.trim();
|
||
wizard.addrCity = document.getElementById('addr-city').value.trim();
|
||
wizard.addrState = document.getElementById('addr-state').value.trim().toUpperCase();
|
||
wizard.addrZip = document.getElementById('addr-zip').value.trim();
|
||
buildReview();
|
||
showStep(5);
|
||
});
|
||
document.getElementById('btn-back-4').addEventListener('click', function() { showStep(3); });
|
||
document.getElementById('btn-back-5').addEventListener('click', function() { showStep(4); });
|
||
|
||
// ── Build review ──
|
||
function buildReview() {
|
||
var services = [];
|
||
document.querySelectorAll('[data-reg]:checked').forEach(function(cb) { services.push(cb.dataset.reg); });
|
||
var html = '<table style="width:100%;border-collapse:collapse;font-size:.85rem;margin-bottom:1rem">';
|
||
html += '<tr style="border-bottom:2px solid #e5e7eb"><th style="text-align:left;padding:.4rem">Item</th><th style="text-align:right;padding:.4rem">Price</th></tr>';
|
||
|
||
var included = ['CORES/FRN', 'Form 499 Initial', 'D.C. Registered Agent'];
|
||
if (wizard.needsRmd) included.push('RMD');
|
||
if (wizard.needsCpni) included.push('CPNI');
|
||
if (wizard.needsCalea) included.push('CALEA');
|
||
if (wizard.needsBdc) included.push('BDC');
|
||
|
||
var items = [
|
||
{ name: 'FCC Carrier Registration Package (' + included.join(', ') + ')', price: wizard.baseFee },
|
||
];
|
||
if (services.indexOf('stir_shaken') >= 0) items.push({ name: 'STIR/SHAKEN Implementation', price: 49900 });
|
||
if (services.indexOf('ocn') >= 0) items.push({ name: 'NECA OCN Registration', price: 265000 });
|
||
if (services.indexOf('intl_214') >= 0) items.push({ name: 'International Section 214 Authorization', price: 149900 });
|
||
if (services.indexOf('state_puc') >= 0) items.push({ name: 'State PUC Registration', price: 39900 });
|
||
if (wizard.entitySource === 'new_formation') {
|
||
items.push({ name: 'Business Formation (' + (document.getElementById('new-formation-state').value || '?') + ' ' + document.getElementById('new-entity-type').value.toUpperCase() + ')', price: wizard.formationFee + wizard.stateFee });
|
||
}
|
||
|
||
var total = 0;
|
||
items.forEach(function(item) {
|
||
total += item.price;
|
||
html += '<tr style="border-bottom:1px solid #f3f4f6"><td style="padding:.35rem">' + item.name + '</td>';
|
||
html += '<td style="text-align:right;padding:.35rem">' + (item.price ? '$' + (item.price / 100).toLocaleString() : 'Included') + '</td></tr>';
|
||
});
|
||
html += '<tr style="border-top:2px solid #e5e7eb;font-weight:700"><td style="padding:.5rem">Total</td><td style="text-align:right;padding:.5rem">$' + (total / 100).toLocaleString() + '</td></tr>';
|
||
html += '</table>';
|
||
|
||
html += '<div style="font-size:.85rem;color:#6b7280;margin-bottom:.5rem">';
|
||
html += '<strong>Entity:</strong> ' + (wizard.entitySource === 'existing' ? (document.getElementById('ex-legal-name').value || 'Existing entity') : 'New ' + document.getElementById('new-entity-type').value.toUpperCase() + ' in ' + (document.getElementById('new-formation-state').value || '?'));
|
||
html += '<br><strong>Contact:</strong> ' + wizard.contactName + ' (' + wizard.contactEmail + ')';
|
||
html += '</div>';
|
||
|
||
document.getElementById('review-content').innerHTML = html;
|
||
}
|
||
|
||
// ── Checkout ──
|
||
document.getElementById('btn-pay').addEventListener('click', async function() {
|
||
var btn = this;
|
||
var errEl = document.getElementById('checkout-err');
|
||
errEl.style.display = 'none';
|
||
if (!document.getElementById('engage-check').checked) { errEl.textContent = 'Please accept the authorization terms.'; errEl.style.display = 'block'; return; }
|
||
btn.disabled = true; btn.textContent = 'Creating order...';
|
||
|
||
var services = [];
|
||
document.querySelectorAll('[data-reg]:checked').forEach(function(cb) { services.push(cb.dataset.reg); });
|
||
var payMethod = document.querySelector('input[name=pay]:checked').value;
|
||
|
||
try {
|
||
var orderResp = await fetch(API + '/api/v1/fcc-carrier-registration', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
customer_name: wizard.contactName,
|
||
customer_email: wizard.contactEmail,
|
||
customer_phone: wizard.contactPhone,
|
||
entity_source: wizard.entitySource,
|
||
entity_legal_name: wizard.entitySource === 'existing' ? document.getElementById('ex-legal-name').value.trim() : document.getElementById('new-entity-name').value.trim(),
|
||
ein: wizard.entitySource === 'existing' ? document.getElementById('ex-ein').value.trim() : '',
|
||
formation_state: wizard.entitySource === 'existing' ? document.getElementById('ex-formation-state').value.trim() : document.getElementById('new-formation-state').value,
|
||
entity_type: wizard.entitySource === 'new_formation' ? document.getElementById('new-entity-type').value : '',
|
||
frn: wizard.frn || '',
|
||
contact_name: wizard.contactName,
|
||
contact_email: wizard.contactEmail,
|
||
contact_phone: wizard.contactPhone,
|
||
contact_title: wizard.contactTitle,
|
||
address_street: wizard.addrStreet,
|
||
address_city: wizard.addrCity,
|
||
address_state: wizard.addrState,
|
||
address_zip: wizard.addrZip,
|
||
service_wizard: {
|
||
service_types: wizard.serviceTypes,
|
||
customer_type: wizard.customerType,
|
||
international: wizard.international,
|
||
voice_delivery: wizard.voiceDelivery,
|
||
infra_needs: wizard.infraNeeds,
|
||
broadband_type: wizard.broadbandType,
|
||
operating_states: wizard.operatingStates,
|
||
},
|
||
services: services,
|
||
engagement_accepted: true,
|
||
}),
|
||
});
|
||
var orderData = await orderResp.json();
|
||
if (!orderResp.ok) throw new Error(orderData.error || 'Order creation failed');
|
||
|
||
btn.textContent = 'Redirecting to payment...';
|
||
var sessionResp = await fetch(API + '/api/v1/checkout/create-session', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ order_id: orderData.order_number, order_type: 'fcc_carrier_registration', payment_method: payMethod }),
|
||
});
|
||
var sessionData = await sessionResp.json();
|
||
if (!sessionResp.ok) throw new Error(sessionData.error || 'Checkout failed');
|
||
if (sessionData.checkout_url) window.location.href = sessionData.checkout_url;
|
||
else throw new Error('No checkout URL');
|
||
} catch (err) {
|
||
errEl.textContent = err.message || 'Something went wrong.';
|
||
errEl.style.display = 'block';
|
||
btn.disabled = false; btn.textContent = 'Continue to Payment';
|
||
}
|
||
});
|
||
|
||
// ── Entity search ──
|
||
var searchTimeout;
|
||
document.getElementById('entity-search').addEventListener('input', function() {
|
||
clearTimeout(searchTimeout);
|
||
var q = this.value.trim();
|
||
if (q.length < 3) return;
|
||
searchTimeout = setTimeout(function() {
|
||
var isDigits = /^\d+$/.test(q);
|
||
var params = isDigits && q.length === 10 ? 'frn=' + q : 'q=' + encodeURIComponent(q);
|
||
fetch(API + '/api/v1/fcc/search?' + params).then(function(r) { return r.json(); }).then(function(d) {
|
||
var results = d.results || [];
|
||
var container = document.getElementById('entity-search-results');
|
||
container.innerHTML = '';
|
||
if (results.length === 0) { container.innerHTML = '<p style="font-size:.82rem;color:#9ca3af">No results found.</p>'; return; }
|
||
results.forEach(function(r) {
|
||
var btn = document.createElement('button');
|
||
btn.type = 'button';
|
||
btn.className = 'q-card';
|
||
btn.innerHTML = '<strong>' + (r.business_name || 'Unknown') + '</strong>' +
|
||
(r.frn ? ' <span style="font-size:.78rem;color:#6b7280">FRN: ' + r.frn + '</span>' : '') +
|
||
(r.city ? ' <span style="font-size:.78rem;color:#9ca3af">' + r.city + ', ' + (r.state || '') + '</span>' : '');
|
||
btn.addEventListener('click', function() {
|
||
wizard.frn = r.frn || '';
|
||
document.getElementById('ex-legal-name').value = r.business_name || '';
|
||
document.getElementById('entity-details').classList.remove('hidden');
|
||
// Auto-fill from FCC lookup
|
||
if (r.frn) {
|
||
fetch(API + '/api/v1/fcc/lookup?frn=' + r.frn + '&quick=1').then(function(resp) { return resp.json(); }).then(function(fcc) {
|
||
if (fcc.cores) {
|
||
if (fcc.cores.address && !/LLC|Inc|Corp/i.test(fcc.cores.address)) wizard.addrStreet = fcc.cores.address;
|
||
if (fcc.cores.city) wizard.addrCity = fcc.cores.city;
|
||
if (fcc.cores.state) { wizard.addrState = fcc.cores.state; document.getElementById('ex-formation-state').value = fcc.cores.state; }
|
||
if (fcc.cores.zip) wizard.addrZip = fcc.cores.zip;
|
||
document.getElementById('addr-street').value = wizard.addrStreet;
|
||
document.getElementById('addr-city').value = wizard.addrCity;
|
||
document.getElementById('addr-state').value = wizard.addrState;
|
||
document.getElementById('addr-zip').value = wizard.addrZip;
|
||
}
|
||
if (fcc.rmd && fcc.rmd.contact_name) {
|
||
wizard.contactName = fcc.rmd.contact_name;
|
||
document.getElementById('contact-name').value = fcc.rmd.contact_name;
|
||
}
|
||
if (fcc.entity_name) document.getElementById('ex-legal-name').value = fcc.entity_name;
|
||
}).catch(function() {});
|
||
}
|
||
});
|
||
container.appendChild(btn);
|
||
});
|
||
}).catch(function() {});
|
||
}, 500);
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|