fix: guard against undefined order in blueprint checkout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-03-01 21:20:07 +00:00
parent b82358a934
commit 0c66bab042

View File

@@ -1,7 +1,11 @@
import { eq } from "drizzle-orm";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import Stripe from "stripe";
import { db } from "@turbostarter/db/server";
import { blueprintOrder } from "@turbostarter/db/schema";
const BLUEPRINT_PRICE_CENTS = 4700;
const CURRENCY = "eur";
@@ -16,10 +20,11 @@ function getStripe(): Stripe {
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { email, businessName, placeId, locale } = body as {
const { email, businessName, placeId, mapsUrl, locale } = body as {
email: string;
businessName: string;
placeId: string;
mapsUrl?: string;
locale?: string;
};
@@ -30,10 +35,87 @@ export async function POST(request: NextRequest) {
);
}
const stripe = getStripe();
const origin = request.headers.get("origin") || process.env.NEXT_PUBLIC_URL || "http://localhost:3000";
const origin =
request.headers.get("origin") ||
process.env.NEXT_PUBLIC_URL ||
"http://localhost:3000";
const lang = locale || "en";
// ── Bypass Stripe: create order + trigger engine directly ──
if (process.env.BYPASS_STRIPE === "true") {
const [order] = await db
.insert(blueprintOrder)
.values({
email,
businessName,
placeId: placeId || null,
mapsUrl: mapsUrl || null,
language: locale || "en",
stripeSessionId: `test_${Date.now()}`,
status: "pending",
})
.returning();
if (!order) {
return NextResponse.json({ error: "Failed to create order" }, { status: 500 });
}
const engineUrl = process.env.ENGINE_API_URL;
const callbackSecret = process.env.ENGINE_CALLBACK_SECRET;
if (!engineUrl || !callbackSecret) {
await db
.update(blueprintOrder)
.set({ status: "failed", errorMessage: "ENGINE_API_URL or ENGINE_CALLBACK_SECRET not set" })
.where(eq(blueprintOrder.id, order.id));
return NextResponse.json(
{ error: "Engine not configured" },
{ status: 503 },
);
}
const publicUrl = process.env.NEXT_PUBLIC_URL || origin;
const fulfillUrl =
mapsUrl ||
`https://www.google.com/maps/place/?q=place_id:${placeId}`;
const fulfillRes = await fetch(`${engineUrl}/api/blueprint/fulfill`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
url: fulfillUrl,
business_name: businessName,
place_id: placeId,
email,
language: locale || "en",
callback_url: `${publicUrl}/api/webhooks/engine`,
callback_secret: callbackSecret,
order_id: order.id,
}),
});
if (fulfillRes.ok) {
const data = (await fulfillRes.json()) as { job_id: string };
await db
.update(blueprintOrder)
.set({ status: "processing", engineJobId: data.job_id })
.where(eq(blueprintOrder.id, order.id));
} else {
const errText = await fulfillRes.text();
await db
.update(blueprintOrder)
.set({ status: "failed", errorMessage: errText })
.where(eq(blueprintOrder.id, order.id));
}
// Redirect to success page with the order ID as session_id
const successUrl = `${origin}/${lang}/get-started?step=success&session_id=${order.id}`;
return NextResponse.json({ url: successUrl });
}
// ── Normal Stripe checkout ──
const stripe = getStripe();
const session = await stripe.checkout.sessions.create({
mode: "payment",
payment_method_types: ["card"],
@@ -56,6 +138,8 @@ export async function POST(request: NextRequest) {
business_name: businessName,
place_id: placeId || "",
email,
maps_url: mapsUrl || "",
language: locale || "en",
},
success_url: `${origin}/${lang}/get-started?step=success&session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${origin}/${lang}/get-started?step=payment`,