skip to Main Content

I am creating a stripe checkout session in my development mode, that is successfully sending information to my webhook, and when I print out my session details, it successfully prints out all the information from the session. The weird part is though, that when I try to access an individual piece of information so I can save it in my backend with prisma, it says the information is undefined when i console.log it out. It’s so weird I can see the whole thing, but not the individual information. What am I doing wrong and how can I access the individual lines in my Next.js program?

Here is my webhook code

import Stripe from "stripe";
import { stripe } from "@/lib/stripe";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import prisma from "@/lib/db/prisma";
import { OrderItem } from "@prisma/client";
import { createCart, getCart } from "@/lib/db/cart";
import { revalidatePath } from "next/cache";

export async function POST(req: Request) {
  const cart = (await getCart()) ?? (await createCart());
  const body = await req.text();
  const signature = headers().get("Stripe-Signature") as string;

  let event: Stripe.Event;

  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!,
    );
  } catch (error) {
    return new NextResponse("Invalid signature", { status: 400 });
  }

  const session = event.data.object as Stripe.Checkout.Session;

  if (event.type === "charge.succeeded") {
    console.log("*********************************************************");
    console.log(session.shipping_details?.address?.state);
    console.log("*********************************************************");
    const paymentIntent = event.data.object;

    const userId = paymentIntent.metadata.userId;
    const {
      name,
      address,
      aptNumber,
      city,
      state,
      zipCode,
      country,
      paymentMethod,
      totalInCents,
      taxInCents,
      cartItems,
    } = paymentIntent.metadata;

    try {
      await prisma.$transaction(async (prisma) => {
        // Create the order
        const order = await prisma.order.create({
          data: {
            userId,
            name,
            address,
            aptNumber,
            city,
            state,
            zipCode,
            country,
            paymentMethod,
            totalInCents: parseInt(totalInCents),
            taxInCents: parseInt(taxInCents),
          },
        });

        // Create order items
        const orderItems = JSON.parse(cartItems).map((item: OrderItem) => ({
          productId: item.productId,
          productName: item.productName,
          price: item.price,
          quantity: item.quantity,
          orderId: order.id,
        }));

        await prisma.orderItem.createMany({
          data: orderItems,
        });

        // Empty the user's cart
        await prisma.cartItem.deleteMany({
          where: {
            cart: {
              userId: userId,
            },
          },
        });

        await prisma.cart.update({
          where: { id: cart.id },
          data: {},
        });
        revalidatePath("/", "layout");
      });
    } catch (error) {
      console.error("Error handling checkout session:", error);
    }
  }

  return new NextResponse("ok", { status: 200 });
}

and here is what is printed out when I console.log the whole session.

{
  id: *the id is here*,
  object: 'charge',
  amount: 1499,
  amount_captured: 1499,
  amount_refunded: 0,
  application: null,
  application_fee: null,
  application_fee_amount: null,
  balance_transaction: null,
  billing_details: {
    address: {
      city: 'Austin',
      country: 'US',
      line1: '3186 Brentwood Drive',
      line2: null,
      postal_code: '78746',
      state: 'TX'
    },
    email: '[email protected]',
    name: 'John Doe',
    phone: null
  },
  calculated_statement_descriptor: 'JOHN DOE',
  captured: true,
  created: 1722718453,
  currency: 'usd',
  customer: null,
  description: null,
  destination: null,
  dispute: null,
  disputed: false,
  failure_balance_transaction: null,
  failure_code: null,
  failure_message: null,
  fraud_details: {},
  invoice: null,
  livemode: false,
  metadata: {},
  on_behalf_of: null,
  order: null,
  outcome: {
    network_status: 'approved_by_network',
    reason: null,
    risk_level: 'normal',
    risk_score: 35,
    seller_message: 'Payment complete.',
    type: 'authorized'
  },
  paid: true,
  payment_intent: 'pi_3PjoxtRu5CVeMm6U0PoyXjnC',
  payment_method: 'pm_1PjoxsRu5CVeMm6UvlsVXLbb',
  payment_method_details: {
    card: {
      amount_authorized: 1499,
      authorization_code: null,
      brand: 'visa',
      checks: [Object],
      country: 'US',
      exp_month: 11,
      exp_year: 2042,
      extended_authorization: [Object],
      fingerprint: '08eKoYQiOE8pTlLD',
      funding: 'credit',
      incremental_authorization: [Object],
      installments: null,
      last4: '4242',
      mandate: null,
      multicapture: [Object],
      network: 'visa',
      network_token: [Object],
      overcapture: [Object],
      three_d_secure: null,
      wallet: null
    },
    type: 'card'
  },
  radar_options: {},
  receipt_email: null,
  receipt_number: null,
  receipt_url: 'https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xUGZDT1pSdTVDVmVNbTZVKPaxurUGMgYiOLShtJk6LBZH4IQkJ4DUUBK9TzBeAOdnf8adJI3SNgJhlihMuQHs9e8IDacRGL5vePD12-',
  refunded: false,
  review: null,
  shipping: {
    address: {
      city: 'AUSTIN',
      country: 'US',
      line1: '3186 Brentwood Drive',
      line2: null,
      postal_code: '78746',
      state: 'TX'
    },
    carrier: null,
    name: 'John Doe',
    phone: null,
    tracking_number: null
  },
  source: null,
  source_transfer: null,
  statement_descriptor: null,
  statement_descriptor_suffix: null,
  status: 'succeeded',
  transfer_data: null,
  transfer_group: null
}

and when I check an individual item such as

console.log(session.shipping_details?.address?.state);

it just prints out undefined, which is weird.

ALSO, you may have noticed, and I find it really weird too, but when I am trying to access the information in session, and try to do a session."autocomplete" to get the available keys to choose from, it does not give me just basic shipping. only shipping_details, which is really weird as well. I can seem to access top level keys, such as session.created, but deeper keys value pairs don’t seem to work. I have never dealt with this before so I am very confused.

I have console.logged about every possible way I can think about. I have looked through the documentation, but it mostly just shows me how the session object is structured, but nothing that might make me understand why it is not working.

2

Answers


  1. Have you tried by any chance?

    console.log(session.shipping?.address?.state);
    

    Else you can try logging these first. I hope you will find more valuable logs

    console.log(session?.shipping)
    console.log(session?.shipping_details)
    

    Maybe shipping_details has some other properties instead of the address

    Login or Signup to reply.
  2. charge.succeeded event is a Charge object, but your code maps it to a Checkout Session object.

    session.shipping_details.address.state is only available on Checkout Session, but not Charge object. It’s expected that field is not available. To get the state in the shipping address under Charge object, your code should:

    1. Map to the event.data.object to Charge object:
      const charge = event.data.object as Stripe.Charge;
      
    2. Get the shipping field by using the spec of Charge object:
      console.log(charge.shipping?.address?.state);
      

    The issue here is that the object is incorrectly mapped to TypeScript object type and the fields are not correctly retrieved. Your system should ensure that event.data.object is mapped to the correct object type.

    If you’re looking for Checkout Session events, it should be checkout.session.* events such as checkout.session.completed event instead of charge.succeeded event.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search