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:
parent
830f5ae738
commit
2927b5cebb
7 changed files with 677 additions and 2 deletions
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue