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
Have you tried by any chance?
Else you can try logging these first. I hope you will find more valuable logs
Maybe shipping_details has some other properties instead of the address
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:event.data.object
to Charge object: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 ascheckout.session.completed
event instead ofcharge.succeeded
event.