Next.js Discord

Discord Forum

Why is stripe not working properly?

Unanswered
Erythrina gall wasp posted this in #help-forum
Open in Discord
Avatar
Erythrina gall waspOP
When the user is redirect to success page, they are logged out.

10 Replies

Avatar
Erythrina gall waspOP
"use client";

import React, { useEffect, useState } from "react";
import { useStripe, useElements, PaymentElement } from "@stripe/react-stripe-js";
import convertToSubcurrency from "@/app/lib/convertToSubcurrency";
import { useSession } from "next-auth/react";

const CheckoutPage = ({ amount }: { amount: number }) => {
  const stripe = useStripe();
  const elements = useElements();
  const [errorMessage, setErrorMessage] = useState<string>();
  const [clientSecret, setClientSecret] = useState("");
  const [loading, setLoading] = useState(false);
  const { data: session } = useSession(); // Use session to get current user's info

  useEffect(() => {
    if (!session) return; // Only proceed if user is logged in
    fetch("/api/create-payment-intent", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ amount: convertToSubcurrency(amount) }),
    })
      .then((res) => res.json())
      .then((data) => setClientSecret(data.clientSecret));
  }, [amount, session]);

  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setLoading(true);

    if (!stripe || !elements) {
      return;
    }
    const { error: submitError } = await elements.submit();

    if (submitError) {
      setErrorMessage(submitError.message);
      setLoading(false);
      return;
    }

    const { error } = await stripe.confirmPayment({
      elements,
      clientSecret,
      confirmParams: {
        return_url: `http://www.localhost:3000/payment-success?amount=${amount}`,
      },
    });

    if (error) {
      // This point is only reached if there's an immediate error when
      // confirming the payment. Show the error to your customer (for example, payment details incomplete)
      setErrorMessage(error.message);
    }

    setLoading(false);
  };

  if (!clientSecret || !stripe || !elements) {
    return (
      <div className="flex items-center justify-center">
        <div
          className="inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-e-transparent align-[-0.125em] text-surface motion-reduce:animate-[spin_1.5s_linear_infinite] dark:text-white"
          role="status"
        >
          <span className="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]">
            Loading...
          </span>
        </div>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit} className="bg-white p-2 rounded-md">
      {clientSecret && <PaymentElement />}

      {errorMessage && <div>{errorMessage}</div>}

      <button
        disabled={!stripe || loading}
        className="text-white w-full p-5 bg-black mt-2 rounded-md font-bold disabled:opacity-50 disabled:animate-pulse"
      >
        {!loading ? `Pay $${amount}` : "Processing..."}
      </button>
    </form>
  );
};

export default CheckoutPage;
export default function PaymentSuccess({
  searchParams: { amount },
}: {
  searchParams: { amount: string };
}) {
  return (
    <main className="max-w-6xl mx-auto p-10 text-white text-center border m-10 rounded-md bg-gradient-to-tr from-blue-500 to-purple-500">
      <div className="mb-10">
        <h1 className="text-4xl font-extrabold mb-2">Thank you!</h1>
        <h2 className="text-2xl">You successfully sent</h2>

        <div className="bg-white p-2 rounded-md text-purple-500 mt-5 text-4xl font-bold">
          ${amount}
        </div>
      </div>
    </main>
  );
}
"use client";

import CheckoutPage from "@/app/stripeComponents/CheckoutPage";
import convertToSubcurrency from "@/app/lib/convertToSubcurrency";
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";

if (process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY === undefined) {
  throw new Error("NEXT_PUBLIC_STRIPE_PUBLIC_KEY is not defined");
}
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY);

export default function Home() {
  const amount = 4.99;

  return (
    <main className="max-w-6xl mx-auto p-10 text-white text-center border m-10 rounded-md bg-gradient-to-tr from-blue-500 to-purple-500">

      <Elements
        stripe={stripePromise}
        options={{
          mode: "payment",
          amount: convertToSubcurrency(amount),
          currency: "usd",
        }}
      >
        <CheckoutPage amount={amount} />
      </Elements>
    </main>
  );
}
 <Link href={"/checkout"} target="_blank">
          <button
            className="mt-6 px-6 py-2 rounded-full bg-gradientButton text-buttonText font-semibold hover:bg-proFillLight dark:hover:bg-proFill"
          >
            Upgrade Now
          </button>
          </Link>

In pricing, we use this.
import { NextRequest, NextResponse } from "next/server";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: NextRequest) {
  try {
    const { amount } = await request.json();

    const paymentIntent = await stripe.paymentIntents.create({
      amount: amount,
      currency: "usd",
      automatic_payment_methods: { enabled: true },
    });

    return NextResponse.json({ clientSecret: paymentIntent.client_secret });
  } catch (error) {
    console.error("Internal Error:", error);
    return NextResponse.json(
      { error: `Internal Server Error: ${error}` },
      { status: 500 }
    );
  }
}
this is route.ts, api/create-payment-intent/route.ts
Avatar
Erythrina gall waspOP
I tried debugging,
"use client";
import { useSearchParams } from "next/navigation";
import React, { useEffect, useState } from "react";
import { getFirestore, doc, updateDoc } from "firebase/firestore";
import { useSession } from "next-auth/react";

const PaymentSuccess = () => {
  const searchParams = useSearchParams();
  const paymentIntent = searchParams.get("payment_intent"); // Get the payment intent ID from the URL
  const redirectStatus = searchParams.get("redirect_status"); // Check if payment succeeded
  const { data: session } = useSession(); // Get current user session
  const [amount, setAmount] = useState<string | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const [subscriptionEndDateDisplay, setSubscriptionEndDateDisplay] = useState<string | null>(null);
  const db = getFirestore();

  useEffect(() => {
    console.log("useEffect triggered with:", { paymentIntent, redirectStatus });
    if (paymentIntent && redirectStatus === "succeeded") {
      fetchPaymentDetails(paymentIntent); // Fetch payment details if payment succeeded
    }
  }, [paymentIntent, redirectStatus]);
  // Fetch payment details from Stripe
  const fetchPaymentDetails = async (paymentIntentId: string) => {
    setLoading(true);
    try {
      console.log("Fetching payment details for:", paymentIntentId);
      const response = await fetch(`/api/payment-success?payment_intent=${paymentIntentId}`);
      
      console.log("Response status:", response.status);
      const data = await response.json();
      console.log("Received data:", data);

      if (data.success) {
        setAmount(data.amount);
        if (session?.user?.email) {
          console.log("Calling updateUserStatusToPro for:", session.user.email);
          updateUserStatusToPro(session.user.email); // Upgrade user to Pro
        } else {
          console.error("Session user email is null. User not logged in?");
        }
      } else {
        console.error("Error fetching payment details:", data.error);
      }
    } catch (error) {
      console.error("Error fetching payment details:", error);
    } finally {
      setLoading(false);
    }
  };

  // Function to update the user's status to 'pro'
  const updateUserStatusToPro = async (userEmail: string): Promise<void> => {
    console.log("updateUserStatusToPro function triggered for:", userEmail);
    
   
 try {
      const userRef = doc(db, "users", userEmail);
      console.log("Firestore document reference:", userRef.path);

      const subscriptionEndDate = new Date();
      subscriptionEndDate.setDate(subscriptionEndDate.getDate() + 30); // 30-day subscription

      await updateDoc(userRef, {
        subscription_status: "pro",
        subscription_end: subscriptionEndDate,
      });

      setSubscriptionEndDateDisplay(subscriptionEndDate.toDateString());
      console.log("User status successfully updated to Pro.");
    } catch (error) {
      console.error("Error updating user status:", error);
    }
  };

  console.log("Session user email:", session?.user?.email);

  return (
    <main className="max-w-6xl mx-auto p-10 text-white text-center border m-10 rounded-md bg-gradient-to-tr from-blue-500 to-purple-500">
      <div className="mb-10">
        <h1 className="text-4xl font-extrabold mb-2">Thank you!</h1>
        <h2 className="text-2xl">Your payment was successful!</h2>
        {loading ? (
          <p>Loading payment details...</p>
        ) : (
          amount && (
            <div className="bg-white p-2 rounded-md text-purple-500 mt-5 text-4xl font-bold">
              ${amount}
            </div>
          )
        )}
        {subscriptionEndDateDisplay && (
          <p className="text-lg mt-4">Your subscription is valid until: {subscriptionEndDateDisplay}</p>
        )}
      </div>
    </main>
  );
};

export default PaymentSuccess;
use effecr isnt even getting triggered