This commit is contained in:
74
server/api/checkout.session.post.ts
Normal file
74
server/api/checkout.session.post.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import Stripe from "stripe";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const config = useRuntimeConfig();
|
||||
const stripeSecretKey = config.stripeSecretKey;
|
||||
|
||||
if (!stripeSecretKey) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: "Stripe secret key not configured",
|
||||
});
|
||||
}
|
||||
|
||||
const stripe = new Stripe(stripeSecretKey, {
|
||||
apiVersion: "2025-10-29.clover",
|
||||
});
|
||||
|
||||
const body = await readBody<{
|
||||
designId: string;
|
||||
templateId?: string;
|
||||
designName?: string;
|
||||
amount: number;
|
||||
currency?: string;
|
||||
successUrl: string;
|
||||
cancelUrl: string;
|
||||
customerEmail?: string;
|
||||
}>(event);
|
||||
|
||||
if (
|
||||
!body?.designId ||
|
||||
!body?.amount ||
|
||||
!body?.successUrl ||
|
||||
!body?.cancelUrl
|
||||
) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Missing required fields",
|
||||
});
|
||||
}
|
||||
|
||||
const { currency = "usd" } = body;
|
||||
|
||||
const session = await stripe.checkout.sessions.create({
|
||||
mode: "payment",
|
||||
payment_method_types: ["card"],
|
||||
billing_address_collection: "auto",
|
||||
customer_email: body.customerEmail,
|
||||
shipping_address_collection: { allowed_countries: ["US", "CA"] },
|
||||
line_items: [
|
||||
{
|
||||
quantity: 1,
|
||||
price_data: {
|
||||
currency,
|
||||
unit_amount: Math.round(body.amount * 100),
|
||||
product_data: {
|
||||
name: body.designName ?? `Slipmat design ${body.designId}`,
|
||||
metadata: {
|
||||
designId: body.designId,
|
||||
...(body.templateId ? { templateId: body.templateId } : {}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
metadata: {
|
||||
designId: body.designId,
|
||||
...(body.templateId ? { templateId: body.templateId } : {}),
|
||||
},
|
||||
success_url: body.successUrl,
|
||||
cancel_url: body.cancelUrl,
|
||||
});
|
||||
|
||||
return { id: session.id, url: session.url };
|
||||
});
|
||||
62
server/api/checkout/[sessionId].get.ts
Normal file
62
server/api/checkout/[sessionId].get.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import Stripe from 'stripe'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const config = useRuntimeConfig()
|
||||
const stripeSecretKey = config.stripeSecretKey
|
||||
|
||||
if (!stripeSecretKey) {
|
||||
throw createError({ statusCode: 500, statusMessage: 'Stripe secret key not configured' })
|
||||
}
|
||||
|
||||
const params = event.context.params as { sessionId?: string }
|
||||
const sessionId = params?.sessionId
|
||||
|
||||
if (!sessionId) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Missing session id' })
|
||||
}
|
||||
|
||||
const stripe = new Stripe(stripeSecretKey, {
|
||||
apiVersion: '2025-10-29.clover',
|
||||
})
|
||||
|
||||
try {
|
||||
const session = await stripe.checkout.sessions.retrieve(sessionId, {
|
||||
expand: ['payment_intent', 'customer'],
|
||||
})
|
||||
|
||||
const customerDetails = session.customer_details ?? null
|
||||
|
||||
return {
|
||||
id: session.id,
|
||||
paymentStatus: session.payment_status,
|
||||
amountTotal: session.amount_total,
|
||||
currency: session.currency,
|
||||
customerEmail: customerDetails?.email ?? session.customer_email ?? null,
|
||||
customerName: customerDetails?.name ?? null,
|
||||
createdAt: session.created ? new Date(session.created * 1000).toISOString() : null,
|
||||
metadata: session.metadata ?? {},
|
||||
customerDetails: customerDetails
|
||||
? {
|
||||
name: customerDetails.name ?? null,
|
||||
email: customerDetails.email ?? null,
|
||||
phone: customerDetails.phone ?? null,
|
||||
address: customerDetails.address
|
||||
? {
|
||||
line1: customerDetails.address.line1 ?? null,
|
||||
line2: customerDetails.address.line2 ?? null,
|
||||
city: customerDetails.address.city ?? null,
|
||||
state: customerDetails.address.state ?? null,
|
||||
postalCode: customerDetails.address.postal_code ?? null,
|
||||
country: customerDetails.address.country ?? null,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw createError({
|
||||
statusCode: error?.statusCode ?? 500,
|
||||
statusMessage: error?.message ?? 'Unable to retrieve checkout session',
|
||||
})
|
||||
}
|
||||
})
|
||||
63
server/api/designs.post.ts
Normal file
63
server/api/designs.post.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody<{
|
||||
designId: string;
|
||||
templateId: string;
|
||||
ownerEmail?: string | null;
|
||||
ownerId?: string | null;
|
||||
previewUrl?: string | null;
|
||||
productionUrl?: string | null;
|
||||
canvasJson: unknown;
|
||||
metadata?: Record<string, unknown>;
|
||||
}>(event);
|
||||
|
||||
if (!body?.designId || !body?.templateId || body.canvasJson === undefined) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Missing required design fields",
|
||||
});
|
||||
}
|
||||
|
||||
const config = useRuntimeConfig();
|
||||
const backendUrl = config.public?.backendUrl;
|
||||
|
||||
if (!backendUrl) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: "Backend URL not configured",
|
||||
});
|
||||
}
|
||||
|
||||
// Extract the authorization token from the incoming request
|
||||
const authHeader = getHeader(event, "authorization");
|
||||
|
||||
const payload = {
|
||||
designId: body.designId,
|
||||
templateId: body.templateId,
|
||||
ownerEmail: body.ownerEmail ?? null,
|
||||
ownerId: body.ownerId ?? null,
|
||||
previewUrl: body.previewUrl ?? null,
|
||||
productionUrl: body.productionUrl ?? null,
|
||||
canvasJson: body.canvasJson,
|
||||
metadata: body.metadata ?? {},
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await $fetch("/designs", {
|
||||
baseURL: backendUrl,
|
||||
method: "POST",
|
||||
headers: authHeader ? { Authorization: authHeader } : {},
|
||||
body: payload,
|
||||
});
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
result,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error("[designs] Failed to forward design payload", err);
|
||||
throw createError({
|
||||
statusCode: 502,
|
||||
statusMessage: (err as Error)?.message ?? "Failed to persist design",
|
||||
});
|
||||
}
|
||||
});
|
||||
30
server/api/designs/[designId].get.ts
Normal file
30
server/api/designs/[designId].get.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const params = event.context.params as { designId?: string }
|
||||
const designId = params?.designId
|
||||
|
||||
if (!designId) {
|
||||
throw createError({ statusCode: 400, statusMessage: "Missing design id" })
|
||||
}
|
||||
|
||||
const config = useRuntimeConfig()
|
||||
const backendUrl = config.public?.backendUrl
|
||||
|
||||
if (!backendUrl) {
|
||||
throw createError({ statusCode: 500, statusMessage: "Backend URL not configured" })
|
||||
}
|
||||
|
||||
try {
|
||||
const design = await $fetch(`/designs/${encodeURIComponent(designId)}`, {
|
||||
baseURL: backendUrl,
|
||||
method: "GET",
|
||||
})
|
||||
|
||||
return design
|
||||
} catch (err) {
|
||||
console.error(`[designs] Failed to fetch design ${designId}`, err)
|
||||
throw createError({
|
||||
statusCode: 502,
|
||||
statusMessage: (err as Error)?.message ?? "Failed to load design",
|
||||
})
|
||||
}
|
||||
})
|
||||
43
server/api/orders.get.ts
Normal file
43
server/api/orders.get.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const config = useRuntimeConfig();
|
||||
const backendUrl = config.public?.backendUrl;
|
||||
|
||||
if (!backendUrl) {
|
||||
throw createError({ statusCode: 500, statusMessage: "Backend URL not configured" });
|
||||
}
|
||||
|
||||
const query = getQuery(event);
|
||||
const customerEmail = typeof query.customerEmail === "string" ? query.customerEmail : null;
|
||||
|
||||
if (!customerEmail) {
|
||||
throw createError({ statusCode: 400, statusMessage: "Missing customerEmail" });
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await $fetch("/transactions", {
|
||||
baseURL: backendUrl,
|
||||
method: "GET",
|
||||
query: { customerEmail },
|
||||
});
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result && Array.isArray((result as any).data)) {
|
||||
return (result as any).data;
|
||||
}
|
||||
|
||||
if (result && Array.isArray((result as any).orders)) {
|
||||
return (result as any).orders;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
console.error("[orders] Failed to fetch order history", err);
|
||||
throw createError({
|
||||
statusCode: 502,
|
||||
statusMessage: (err as Error)?.message ?? "Failed to load order history",
|
||||
});
|
||||
}
|
||||
});
|
||||
73
server/api/transactions.post.ts
Normal file
73
server/api/transactions.post.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody<{
|
||||
stripeSessionId: string;
|
||||
designId: string;
|
||||
templateId?: string;
|
||||
amount: number | string;
|
||||
currency: string;
|
||||
customerEmail?: string;
|
||||
customerDetails?: {
|
||||
name?: string | null;
|
||||
email?: string | null;
|
||||
phone?: string | null;
|
||||
address?: {
|
||||
line1?: string | null;
|
||||
line2?: string | null;
|
||||
city?: string | null;
|
||||
state?: string | null;
|
||||
postalCode?: string | null;
|
||||
country?: string | null;
|
||||
} | null;
|
||||
};
|
||||
}>(event);
|
||||
|
||||
if (!body?.stripeSessionId || !body?.designId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Missing required fields",
|
||||
});
|
||||
}
|
||||
|
||||
const config = useRuntimeConfig();
|
||||
const backendUrl = config.public?.backendUrl;
|
||||
|
||||
if (!backendUrl) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: "Backend URL not configured",
|
||||
});
|
||||
}
|
||||
|
||||
const record = {
|
||||
stripeSessionId: body.stripeSessionId,
|
||||
designId: body.designId,
|
||||
templateId: body.templateId ?? null,
|
||||
amount: body.amount.toString(),
|
||||
currency: body.currency,
|
||||
customerEmail: body.customerEmail ?? null,
|
||||
customerDetails: body.customerDetails ?? null,
|
||||
};
|
||||
|
||||
console.log("[transactions] Forwarding record to backend:", record);
|
||||
|
||||
try {
|
||||
const backendResult = await $fetch("/transactions", {
|
||||
baseURL: backendUrl,
|
||||
method: "POST",
|
||||
body: record,
|
||||
});
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
receivedAt: new Date().toISOString(),
|
||||
backendResult,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error("[transactions] Failed to forward to backend", err);
|
||||
throw createError({
|
||||
statusCode: 502,
|
||||
statusMessage:
|
||||
(err as Error)?.message ?? "Failed to save transaction to backend",
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user