500 (Internal Server Error) - Invalid `prisma.order.create()` invocation:

42 views Asked by At

I'm building my first ecommerce site and currently trying to integrate stripe payment however I'm coming across an issue I can't seem to get my head around. Payment intent is being added to my stripe payments however the order isn't being added to my mongodb database using prisma as intended, neither is it being added to the local storage. When routing to /checkout I am getting an Internal Server Error which shows as the following in my terminal:

PrismaClientValidationError: 
Invalid `prisma.order.create()` invocation:
{
  data: {
    user: {
      connect: {
        id: "65ce3a1f18f1723283abd0d1"
      }
    },
    amount: 6900,
    currency: "gbp",
    status: "pending",
    deliveryStatus: "pending",
    paymentIntentId: "pi_3Ok7hqD8Ejb1s3Do1685nR0Z",
    products: [
      {
        id: "*product id*",
        name: "*Product name*",
        description: "*My description*",
        category: "*category*",
        image: {
          image: "*imageUrl*",
          id: "*image id*"
        },
        quantity: 2,
        price: 34.5
      }
    ]
  }
}

Argument `Id` is missing.

In my browser console I am also getting the following error:

Error SyntaxError: Unexpected end of JSON input

prisma.schema:

model Order{
  id String  @id @default(auto()) @map("_id") @db.ObjectId
  userId String @db.ObjectId
  amount Float
  currency String
  status String
  deliveryStatus String?
  createDate DateTime @default(now())
  paymentIntentId String @unique
  products CartProductType[]
  address Address?

    user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}

type CartProductType{
  id String
  name String
  description String
  category String
  brand String
  selectedImg Image
  quantity Int
  price Float
}

type Image{
  color String
  colorCode String
  image String
}

type Address{
  city String
  country String
  line1 String
  line2 String?
  postal_code String
  state String
} 

/api/create-payment-intent/route.ts:

import Stripe from "stripe";
import prisma from "@/libs/prismadb";
import { NextResponse } from "next/server";
import { CartProductType } from "@/app/product/[productId]/productDetails";
import { getCurrentUser } from "@/actions/getCurrentUser";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, {
  apiVersion: "2022-11-15",
});

const calculateOrderAmount = (items: CartProductType[]) => {
  const totalPrice = items.reduce((acc, item) => {
    const itemTotal = item.price * item.quantity;
    return acc + itemTotal;
  }, 0);

  const price: any = Math.floor(totalPrice);

  return price;
};

export async function POST(request: Request) {
  const currentUser = await getCurrentUser();

  if (!currentUser) {
    return NextResponse.error();
  }

  const body = await request.json();
  const { items, payment_intent_id } = body;
  const total = calculateOrderAmount(items) * 100;
  const orderData = {
    user: { connect: { id: currentUser.id } },
    amount: total,
    currency: "gbp",
    status: "pending",
    deliveryStatus: "pending",
    paymentIntentId: payment_intent_id,
    products: items,
  };

  if (payment_intent_id) {
    const current_intent = await stripe.paymentIntents.retrieve(
      payment_intent_id
    );

    if (current_intent) {
      const updated_intent = await stripe.paymentIntents.update(
        payment_intent_id,
        { amount: total }
      );

      // update the order
      const [existing_order, update_order] = await Promise.all([
        prisma.order.findFirst({
          where: { paymentIntentId: payment_intent_id },
        }),
        prisma.order.update({
          where: { paymentIntentId: payment_intent_id },
          data: {
            amount: total,
            products: items,
          },
        }),
      ]);

      if (!existing_order) {
        return NextResponse.error();
      }

      return NextResponse.json({ paymentIntent: updated_intent });
    }
  } else {
    // create the intent
    const paymentIntent = await stripe.paymentIntents.create({
      amount: total,
      currency: "gbp",
      automatic_payment_methods: { enabled: true },
    });

    // create the order
    orderData.paymentIntentId = paymentIntent.id;

    await prisma.order.create({
      data: orderData,
    });

    return NextResponse.json({ paymentIntent });
  }

   // Return a default response (e.g., an error response) if none of the conditions are met
   return NextResponse.error();
}

POST request in my checkoutClient.tsx file:

useEffect(() => {
    //create a paymentintent as soon as the page loads
    if (cartProducts) {
      setLoading(true);
      setError(false);

      fetch("/api/create-payment-intent", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          items: cartProducts,
          payment_intent_id: paymentIntent,
        }),
      })
        .then((res) => {
          setLoading(false);
          if (res.status === 401) {
            return router.push("/login");
          }

          return res.json();
        })
        .then((data) => {
          setClientSecret(data.paymentIntent.client_secret);
          handleSetPaymentIntent(data.paymentIntent.id);
        })
        .catch((error) => {
          setError(true);
          console.log("Error", error);
          toast.error("Something went wrong");
        });
    }
  }, [cartProducts, paymentIntent]);

useCart.tsx hook:

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useState,
} from "react";
import { CartProductType } from "../product/[productId]/productDetails";
import { toast } from "react-hot-toast";

type CartContextType = {
  cartTotalQuantity: number;
  cartTotalAmount: number;
  cartProducts: CartProductType[] | null;
  handleAddToCart: (product: CartProductType) => void;
  handleRemoveFromCart: (product: CartProductType) => void;
  handleCartQuantityIncrease: (product: CartProductType) => void;
  handleCartQuantityDecrease: (product: CartProductType) => void;
  handleClearCart: () => void;
  paymentIntent: string | null;
  handleSetPaymentIntent: (val: string | null) => void;
};

export const CartContext = createContext<CartContextType | null>(null);

interface Props {
  [propName: string]: any;
}

export const CartContextProvider = (props: Props) => {
  const [cartTotalQuantity, setCartTotalQuantity] = useState(0);
  const [cartTotalAmount, setCartTotalAmount] = useState(0);
  const [cartProducts, setCartProducts] = useState<CartProductType[] | null>(
    null
  );
  const [paymentIntent, setPaymentIntent] = useState<string | null>(null);

  //Retrieves local storage cart data and updates the cart
  useEffect(() => {
    const cartItems: any = localStorage.getItem("HillsideCart");
    const cartStorageProducts: CartProductType[] | null = JSON.parse(cartItems);
    const paymentIntentStorage: any = localStorage.getItem("paymentIntentStorage");
    const paymentIntent: string | null = JSON.parse(paymentIntentStorage);

    setCartProducts(cartStorageProducts);
    setPaymentIntent(paymentIntent);

  }, []);

  //Updates the total amount figure when items are added and removed
  useEffect(() => {
    const updateTotal = () => {
      if (cartProducts) {
        const { total, quantity } = cartProducts?.reduce(
          (acc, item) => {
            const itemTotal = item.price * item.quantity;
            acc.total += itemTotal;
            acc.quantity += item.quantity;

            return acc;
          },
          {
            total: 0,
            quantity: 0,
          }
        );
        setCartTotalAmount(total);
        setCartTotalQuantity(quantity);
      }
    };
    updateTotal();
  }, [cartProducts]);

  //Hanldes adding new products to the cart
  const handleAddToCart = useCallback((product: CartProductType) => {
    setCartProducts((prev) => {
      let updatedCart;

      if (prev) {
        updatedCart = [...prev, product];
      } else {
        updatedCart = [product];
      }
      localStorage.setItem("HillsideCart", JSON.stringify(updatedCart));
      return updatedCart;
    });
    toast.success("Item Added to Cart");
  }, []);

  //Handles removing products from the cart
  const handleRemoveFromCart = useCallback(
    (product: CartProductType) => {
      if (cartProducts) {
        const filteredProducts = cartProducts.filter((item) => {
          return item.id !== product.id;
        });
        setCartProducts(filteredProducts);
        localStorage.setItem("HillsideCart", JSON.stringify(filteredProducts));
      }
      toast.success("Item removed from Cart");
    },
    [cartProducts]
  );

  //
  //handles increasing quantity of product in cart page
  const handleCartQuantityIncrease = useCallback(
    (product: CartProductType) => {
      let updatedCart;

      //checks for max quantity and returns error message
      if (product.quantity === 99) {
        return toast.error("Maximum quantity reached");
      }
      //checks to see if products exist in cart - finds the relevant item index and checks it exists
      if (cartProducts) {
        updatedCart = [...cartProducts];
        const existingIndex = cartProducts.findIndex(
          (item) => item.id === product.id
        );
        //accesses product and updates
        if (existingIndex > -1) {
          updatedCart[existingIndex].quantity = ++updatedCart[existingIndex]
            .quantity;
        }
        setCartProducts(updatedCart);
        localStorage.setItem("HillsideCart", JSON.stringify(updatedCart));
      }
    },
    [cartProducts]
  );

  //
  //handles decreasing quantity of product in cart page
  const handleCartQuantityDecrease = useCallback(
    (product: CartProductType) => {
      let updatedCart;

      //checks for max quantity and returns error message
      if (product.quantity === 1) {
        return toast.error("Minumum quantity reached");
      }
      //checks to see if products exist in cart - finds the relevant item index and checks it exists
      if (cartProducts) {
        updatedCart = [...cartProducts];
        const existingIndex = cartProducts.findIndex(
          (item) => item.id === product.id
        );
        //accesses product and updates
        if (existingIndex > -1) {
          updatedCart[existingIndex].quantity = --updatedCart[existingIndex]
            .quantity;
        }
        setCartProducts(updatedCart);
        localStorage.setItem("HillsideCart", JSON.stringify(updatedCart));
      }
    },
    [cartProducts]
  );

  //handles clearing the cart
  const handleClearCart = useCallback(() => {
    setCartProducts(null);
    setCartTotalQuantity(0);
    localStorage.setItem("HillsideCart", JSON.stringify(null));
    toast.success("Cart cleared");
  }, []);

  //sets payment intent
  const handleSetPaymentIntent = useCallback(
    (val: string | null) => {
      setPaymentIntent(val);
      localStorage.setItem("paymentIntentStorage", JSON.stringify(val));
    },
    [paymentIntent]
  );

  const value = {
    cartTotalQuantity,
    cartTotalAmount,
    cartProducts,
    handleAddToCart,
    handleRemoveFromCart,
    handleCartQuantityIncrease,
    handleCartQuantityDecrease,
    handleClearCart,
    paymentIntent,
    handleSetPaymentIntent,
  };

  return <CartContext.Provider value={value} {...props} />;
};

export const useCart = () => {
  const context = useContext(CartContext);

  if (context === null) {
    throw new Error("useCart must be used within a CartContext Provider");
  }

  return context;
};

Searched through similar looking errors with no luck to direct me.

0

There are 0 answers