Running a Next.js application on Vercel. Vercel is not playing nicely with Stripe API. Since Stripe API is so popularly used with payments, not sure how I'm the only one encountering this issue? Chatgpt says issue is either gRPC or OpenSSL compatibility?
See logs:
admin.ts/Firebase Admin initialized successfully.
admin.ts/Firestore project ID: project_1
✅ Detected Node.js runtime: v20.18.3
✅ Using Stripe Key: sk_live_51
✅ STRIPE_DISABLE_GRPC: true
✅ create-checkout-session.ts handler triggered
🔹 Received request body: {
firebaseUID: '<redacted>',
email: '<redacted>',
priceId: 'price_1Pu4b<redacted>'
}
Error creating checkout session: Error: 2 UNKNOWN: Getting metadata from plugin failed with error: error:1E08010C:DECODER routines::unsupported
at a (.next/server/pages/api/create-checkout-session.js:1:1690) {
code: 2,
details: 'Getting metadata from plugin failed with error: error:1E08010C:DECODER routines::unsupported',
metadata: [Metadata]
}
Here is the code for my create-checkout-session.ts:
// pages/api/create-checkout-session.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { firestore } from "../../firebase/admin"; // Import Firestore from admin.ts
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
// This will throw in Edge runtime because 'process' is not available
const nodeVersion = process.version;
console.log("✅ Detected Node.js runtime:", nodeVersion);
} catch (err) {
console.log("⛔ Likely running in Edge runtime (process is undefined)");
}
// Initiate stripe inside handler not outside.
const Stripe = (await import('stripe')).default;
// Part 1: Decide which Stripe key to use
const isProductionDomain = req.headers.host?.includes('stierstocks.com');
// If stierstocks, use STRIPE_SECRET_KEY, if not use TEST_STRIPE_SECRET_KEY.
const stripeSecretKey = isProductionDomain
? process.env.STRIPE_SECRET_KEY
: process.env.TEST_STRIPE_SECRET_KEY;
// Ensure the Stripe secret key is not missing
if (!stripeSecretKey) {
console.error("❌ Stripe secret key is missing!");
return res.status(500).json({ error: "Stripe secret key is missing" });
}
console.log('✅ Using Stripe Key:', stripeSecretKey?.slice(0, 10));
// See if GRPC is disabled in env.
console.log("✅ STRIPE_DISABLE_GRPC:", process.env.STRIPE_DISABLE_GRPC);
const stripe = new Stripe(stripeSecretKey || '', {
apiVersion: '2025-04-30.basil',
httpClient: Stripe.createFetchHttpClient(), // ✅ Force REST client (no gRPC)
});
// Part 2: Retrieve the priceId based on Pricing.tsx
if (req.method === 'POST') {
// ✅ Extract incoming POST payload
const { firebaseUID, email, priceId } = req.body;
console.log("✅ create-checkout-session.ts handler triggered");
console.log("🔹 Received request body:", req.body);
// ✅ Validate priceId
if (!priceId) {
console.error('❌ priceId not provided in request body');
return res.status(400).json({ error: 'Missing priceId' });
}
// Determine the base URL dynamically for use in the final success and cancel url.
const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http';
const host = req.headers.host; // Get the host (e.g., :8080 or stierstocks.com)
const baseUrl = `${protocol}://${host}`;
try {
// Retrieve Firestore user data BEFORE checkout session is created
const userDocRef = firestore.collection("users").doc(firebaseUID);
const userDocSnap = await userDocRef.get();
if (!userDocSnap.exists) {
console.error(`❌ No Firestore document found for UID: ${firebaseUID}`);
} else {
console.log(`✅ Firestore data BEFORE checkout for UID ${firebaseUID}:`,
JSON.stringify(userDocSnap.data(), null, 2));
}
const lineItems = [{ price: priceId, quantity: 1 }];
console.log("Stripe Line Items:", lineItems);
// Create a Stripe checkout session
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: lineItems,
mode: 'subscription', // mode: 'payment' for 1 time payment
success_url: `${baseUrl}/success`, // Dynamically set success URL instead of hard coding it to stierstocks.com
cancel_url: `${baseUrl}`, // Dynamically set cancel URL instead of hard coding it to stierstocks.com
metadata: { firebaseUID }, // Attach the Firebase UID as metadata
customer_email: email, // ✅ Pre-fill Stripe with user’s email
});
// Log that the checkout session was created.
console.log(`Stripe Checkout Session Created:`, session);
// Log the incoming request to debug missing parameters
console.log("🔹 Received request body:", req.body);
// Log the Stripe session ID before sending it to the frontend
if (session && session.id) {
console.log(`✅ create-checkout-session.ts/session.id: ${session.id}`);
} else {
console.error("❌ Failed to create Stripe session. Response:", session);
}
// Sends the Stripe session ID back to the frontend, which is needed for redirecting the user to Stripe Checkout.
res.status(200).json({ id: session.id });
} catch (error) {
console.error('Error creating checkout session:', error);
return res.status(500).json({ error: (error as Error).message || 'An unknown error occurred' });
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
const userDocRef = firestore.collection("users").doc(firebaseUID); const userDocSnap = await userDocRef.get();which means that for some reason firebase is failing to load your doc. you need to check that first.