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>
This commit is contained in:
justin 2026-04-29 08:48:36 -05:00
parent 830f5ae738
commit 2927b5cebb
7 changed files with 677 additions and 2 deletions

View file

@ -76,7 +76,7 @@ const GATEWAY_LABELS: Record<string, string> = {
const CreateSessionSchema = z.object({
order_id: z.string().min(1),
order_type: z.enum(["canada_crtc", "formation", "bundle", "compliance", "compliance_batch"]),
order_type: z.enum(["canada_crtc", "formation", "bundle", "compliance", "compliance_batch", "fcc_carrier_registration"]),
payment_method: z.enum(["card", "ach", "paypal", "klarna", "crypto"]),
});
@ -246,6 +246,48 @@ async function fetchOrderData(
} | null> {
// ── Canada CRTC ─────────────────────────────────────────────────────────
if (order_type === "fcc_carrier_registration") {
const { rows } = await pool.query(
`SELECT * FROM fcc_carrier_registrations WHERE order_number = $1`,
[order_id],
);
if (!rows.length) return null;
const order = rows[0] as Record<string, unknown>;
if (order.payment_status !== "pending_payment") return null;
const baseCents = (order.service_fee_cents as number) || 129900;
const formCents = ((order.formation_fee_cents as number) || 0) + ((order.state_fee_cents as number) || 0);
const addonCents = (order.addon_fee_cents as number) || 0;
const pucCents = (order.puc_fee_cents as number) || 0;
const totalCents = baseCents + formCents + addonCents + pucCents;
const lineItems: Array<{price_data: {currency: "usd"; product_data: {name: string}; unit_amount: number}; quantity: number}> = [
{ price_data: { currency: "usd", product_data: { name: "FCC Carrier / ISP Registration" }, unit_amount: baseCents }, quantity: 1 },
];
if (formCents > 0) {
lineItems.push({ price_data: { currency: "usd", product_data: { name: `Business Formation (${order.formation_state || "?"} ${((order.entity_type as string) || "LLC").toUpperCase()})` }, unit_amount: formCents }, quantity: 1 });
}
if ((order.include_stir_shaken as boolean)) {
lineItems.push({ price_data: { currency: "usd", product_data: { name: "STIR/SHAKEN Implementation" }, unit_amount: 49900 }, quantity: 1 });
}
if ((order.include_ocn as boolean)) {
lineItems.push({ price_data: { currency: "usd", product_data: { name: "NECA OCN Registration" }, unit_amount: 265000 }, quantity: 1 });
}
if (pucCents > 0) {
const stateCount = ((order.state_puc_states as string[]) || []).length;
lineItems.push({ price_data: { currency: "usd", product_data: { name: `State PUC Registration (${stateCount} state${stateCount !== 1 ? "s" : ""})` }, unit_amount: pucCents }, quantity: 1 });
}
return {
order,
stripeLineItems: lineItems as Stripe.Checkout.SessionCreateParams.LineItem[],
service_cents: totalCents,
base_cents: totalCents,
customer_email: (order.customer_email as string) || "",
customer_name: (order.customer_name as string) || "",
};
}
if (order_type === "canada_crtc") {
const { rows } = await pool.query(
`SELECT * FROM canada_crtc_orders WHERE order_number = $1`,
@ -1106,6 +1148,7 @@ export async function handlePaymentComplete(
bundle: "bundle_orders",
compliance: "compliance_orders",
compliance_batch: "compliance_orders",
fcc_carrier_registration: "fcc_carrier_registrations",
};
const table = tableMap[order_type];
if (!table) return;
@ -1200,6 +1243,48 @@ export async function handlePaymentComplete(
}
// ── Advance compliance batch orders (fan out worker dispatch per service) ──
// ── FCC Carrier Registration — dispatch pipeline worker ──────────────────
if (order_type === "fcc_carrier_registration") {
const workerUrl = process.env.WORKER_URL || "http://workers:8090";
setImmediate(async () => {
try {
const dispatchRes = await fetch(`${workerUrl}/jobs`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: "process_fcc_carrier_registration",
order_number: order_id,
}),
});
if (dispatchRes.ok) {
console.log(`[checkout] FCC carrier registration dispatched: ${order_id}`);
} else {
console.error(`[checkout] FCC carrier reg dispatch failed: HTTP ${dispatchRes.status}`);
}
} catch (err) {
console.error(`[checkout] FCC carrier reg dispatch error:`, err);
}
});
// Send confirmation email
try {
const { sendEmail } = await import("../email.js");
const custEmail = (order.customer_email as string) || "";
const custName = (order.customer_name as string) || "";
const firstName = custName.split(" ")[0] || custName;
await sendEmail({
to: custEmail,
subject: `FCC Carrier Registration — Order ${order_id} Confirmed`,
html: `<h2>Your FCC Carrier Registration is Underway</h2>
<p>Hi ${firstName},</p>
<p>Thank you for your order. We're now processing your carrier/ISP registration package.</p>
<p>You'll receive email updates as each step completes. If we need any additional information, we'll reach out.</p>
<p style="font-size:12px;color:#9ca3af;">Order: ${order_id}</p>
<p style="font-size:12px;color:#9ca3af;">Performance West Inc. | 525 Randall Ave Ste 100-1195, Cheyenne, WY 82001 | 1-888-411-0383</p>`,
});
} catch {}
}
if (order_type === "compliance_batch") {
// A batch order is a set of compliance_orders sharing a batch_id.
// Create ERPNext Sales Order + Invoice, then dispatch each sub-order.