Next.js Stripe webhook
Answered
Rhinelander posted this in #help-forum
Original message was deleted.
Answered by B33fb0n3
You can listen to the „payment_intent.succeeded“ event to listen to successful payments. This event is executed once after the payment is completed. Inside it you will get all the informations, that are inside your paymentintent object. These information can be used to add it inside your database.
If you are using hosted sessions from stripe, you can listen to the „checkout.sessions.complete“ event to access the items or also use the paymentintent
If you are using hosted sessions from stripe, you can listen to the „checkout.sessions.complete“ event to access the items or also use the paymentintent
12 Replies
Original message was deleted
You can listen to the „payment_intent.succeeded“ event to listen to successful payments. This event is executed once after the payment is completed. Inside it you will get all the informations, that are inside your paymentintent object. These information can be used to add it inside your database.
If you are using hosted sessions from stripe, you can listen to the „checkout.sessions.complete“ event to access the items or also use the paymentintent
If you are using hosted sessions from stripe, you can listen to the „checkout.sessions.complete“ event to access the items or also use the paymentintent
Answer
Rhinelander
Did you mean like that
console.log("recived", event.type);
switch (event.type) {
case "payment_intent.succeeded":
const paymentIntentSucceeded = event.data.object;
console.log("paymentIntentSucceeded", paymentIntentSucceeded);
break;
default:
console.log(`Unhandled event type ${event.type}`);
}
return NextResponse.json({ received: true });@Rhinelander ts
console.log("recived", event.type);
switch (event.type) {
case "payment_intent.succeeded":
const paymentIntentSucceeded = event.data.object;
console.log("paymentIntentSucceeded", paymentIntentSucceeded);
break;
default:
console.log(`Unhandled event type ${event.type}`);
}
return NextResponse.json({ received: true });
looks good for me. Of course you need a little more code than that, but if it's just a snipet it looks good for me
Rhinelander
import { auth } from "@/auth";
import stripe from "@/lib/stripe";
import { absoluteUrl } from "@/lib/utils";
import { NextResponse } from "next/server";
export async function GET() {
try {
const session = await auth();
const user = session?.user;
if (!user || !user.id) {
return new NextResponse("Unauthorized", { status: 401 });
}
const userId = user.id;
const stripeSession = await stripe.checkout.sessions.create({
success_url: absoluteUrl("/coin-shop"),
cancel_url: absoluteUrl("/coin-shop"),
payment_method_types: ["card"],
mode: "payment",
billing_address_collection: "auto",
customer_email: user.email!,
line_items: [
{
price_data: {
currency: "EUR",
product_data: {
name: "100 coins",
},
unit_amount: 1000,
},
quantity: 1,
},
],
metadata: {
userId,
},
});
return new NextResponse(JSON.stringify({ url: stripeSession.url }));
} catch (error) {
console.log(error);
return new NextResponse("Internal error", { status: 500 });
}
}Changed it a bit. Now i just need to set line items to be dynamic
I think it will work need to test it first
@Rhinelander typescript
import { auth } from "@/auth";
import stripe from "@/lib/stripe";
import { absoluteUrl } from "@/lib/utils";
import { NextResponse } from "next/server";
export async function GET() {
try {
const session = await auth();
const user = session?.user;
if (!user || !user.id) {
return new NextResponse("Unauthorized", { status: 401 });
}
const userId = user.id;
const stripeSession = await stripe.checkout.sessions.create({
success_url: absoluteUrl("/coin-shop"),
cancel_url: absoluteUrl("/coin-shop"),
payment_method_types: ["card"],
mode: "payment",
billing_address_collection: "auto",
customer_email: user.email!,
line_items: [
{
price_data: {
currency: "EUR",
product_data: {
name: "100 coins",
},
unit_amount: 1000,
},
quantity: 1,
},
],
metadata: {
userId,
},
});
return new NextResponse(JSON.stringify({ url: stripeSession.url }));
} catch (error) {
console.log(error);
return new NextResponse("Internal error", { status: 500 });
}
}
the creation of the session looks also good for me. Keep in mind, that you stripe webhook needs to be POST and you should verify the stripe signature to execute your code only when stripe sends the webhook event
Rhinelander
Yep I do that!
import stripe from "@/lib/stripe";
import prisma from "@/prisma/db";
import { NextResponse } from "next/server";
import Stripe from "stripe";
export async function POST(request: Request) {
const body = await request.text();
console.log("Body", body);
const sig = request.headers.get("Stripe-Signature");
if (!sig) {
console.log("No signature");
return NextResponse.json({ error: "No signature" });
}
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!,
);
} catch (error: any) {
console.log(`Webhook signature verification failed.`, error.message);
return new NextResponse(error, { status: 400 });
}
const session = event.data.object as Stripe.Checkout.Session;
if (event.type === "checkout.session.completed") {
if (!session?.metadata?.userId) {
return new NextResponse("User id is required!"), { status: 400 };
}
console.log(session);
await prisma.purchase.create({
data: {
stripePurchaseId: session.id,
price: Number(session.amount_total),
coinsAmount: Number(session?.metadata?.coinsAmount),
userId: session?.metadata?.userId,
},
});
}
return NextResponse.json({ received: true });
}