0

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');
  }
}
6
  • it would be easier to debug the issue if you share your code Commented May 5 at 8:14
  • @Tarzan posted my create-checkout-session.ts code, please take a look Commented May 6 at 4:15
  • based on your code console logs, the code is failing at this stage 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. Commented May 6 at 12:54
  • @Tarzan I see, because it doesnt even run the "if (!userDocSnap.exists) {" block? Commented May 6 at 22:47
  • that's correct! Commented May 7 at 9:12

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.