WIP: pending changes — docs, webhook, nav/footer (committed per Sami request, not yet reviewed)

This commit is contained in:
Krystie
2026-06-13 21:29:36 -07:00
parent 69c2179a43
commit 3b2023e97a
19 changed files with 7584 additions and 7717 deletions
+5 -74
View File
@@ -1,74 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2026-03-25.dahlia",
});
const PRICE_IDS: Record<string, string | undefined> = {
monthly: process.env.STRIPE_PRICE_MONTHLY,
yearly: process.env.STRIPE_PRICE_YEARLY,
};
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { plan, email } = body;
if (!plan || !PRICE_IDS[plan]) {
return NextResponse.json(
{ error: "Invalid plan. Choose 'monthly' or 'yearly'." },
{ status: 400 }
);
}
const priceId = PRICE_IDS[plan];
if (!priceId) {
return NextResponse.json(
{ error: "Price not configured. Please contact support." },
{ status: 500 }
);
}
const appUrl = process.env.NEXT_PUBLIC_APP_URL || "https://dashcaddy.net";
const sessionParams: Stripe.Checkout.SessionCreateParams = {
mode: "subscription",
payment_method_types: ["card"],
line_items: [
{
price: priceId,
quantity: 1,
},
],
success_url: `${appUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${appUrl}/pricing`,
allow_promotion_codes: true,
billing_address_collection: "required",
subscription_data: {
trial_period_days: 14,
metadata: {
plan,
source: "dashcaddy-website",
},
},
metadata: {
plan,
},
};
// Pre-fill email if provided
if (email) {
sessionParams.customer_email = email;
}
const session = await stripe.checkout.sessions.create(sessionParams);
return NextResponse.json({ url: session.url });
} catch (error) {
console.error("Stripe checkout error:", error);
const message =
error instanceof Error ? error.message : "Internal server error";
return NextResponse.json({ error: message }, { status: 500 });
}
}
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Checkout disabled for static export' }, { status: 503 });
}
+5
View File
@@ -0,0 +1,5 @@
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Webhooks disabled for static export' }, { status: 503 });
}
+4 -99
View File
@@ -1,99 +1,4 @@
import { NextRequest, NextResponse } from "next/server";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2026-03-25.dahlia",
});
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
export async function POST(request: NextRequest) {
const body = await request.text();
const signature = request.headers.get("stripe-signature");
if (!signature) {
return NextResponse.json(
{ error: "Missing stripe-signature header" },
{ status: 400 }
);
}
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
} catch (err) {
const message = err instanceof Error ? err.message : "Unknown error";
console.error(`Webhook signature verification failed: ${message}`);
return NextResponse.json({ error: message }, { status: 400 });
}
try {
switch (event.type) {
case "checkout.session.completed": {
const session = event.data.object as Stripe.Checkout.Session;
console.log("Checkout completed:", {
sessionId: session.id,
customerEmail: session.customer_email,
plan: session.metadata?.plan,
subscriptionId: session.subscription,
});
// TODO: Generate and deliver license key to customer
// This is where you'd:
// 1. Generate a DC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX license code
// 2. Store it in your database
// 3. Email it to the customer
// 4. Associate it with the Stripe subscription ID
break;
}
case "customer.subscription.updated": {
const subscription = event.data.object as Stripe.Subscription;
console.log("Subscription updated:", {
subscriptionId: subscription.id,
status: subscription.status,
});
// TODO: Update license expiration based on subscription status
break;
}
case "customer.subscription.deleted": {
const subscription = event.data.object as Stripe.Subscription;
console.log("Subscription cancelled:", {
subscriptionId: subscription.id,
status: subscription.status,
});
// TODO: Deactivate/expire the license key
// The DashCaddy instance will gracefully downgrade to free tier
break;
}
case "invoice.payment_failed": {
const invoice = event.data.object as Stripe.Invoice;
console.log("Payment failed:", {
invoiceId: invoice.id,
customerEmail: invoice.customer_email,
});
// TODO: Notify customer about failed payment
// Consider a grace period before deactivating license
break;
}
default:
console.log(`Unhandled event type: ${event.type}`);
}
return NextResponse.json({ received: true });
} catch (error) {
console.error("Webhook handler error:", error);
return NextResponse.json(
{ error: "Webhook handler failed" },
{ status: 500 }
);
}
}
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'disabled' }, { status: 503 });
}