Reputation: 11
so my product is a subscription service, which grants users access to a service on my website.I've created the product on stripe, and i added the payment link to my website:
<a
className="bg-red-600 text-white py-2 px-4 rounded inline-block"
target="_blank"
href={`https://buy.stripe.com/test_bIY15h1Jp5eg6409AA?prefilled_email=${session.user.email}`}
> Pay Now
i've written my webhook.ts code in my api, that upon successful payment grant the user access, and upon subscription not being paid, revoke access. It works to an extent; during testing after i cancel a subscription and i try subscribing again with that same email(i add the customerId to the user database), stripe creates another customer instead of using that same customer and its causing issues
my webhookk.ts:
import { NextApiResponse, NextApiRequest } from "next";
import Stripe from "stripe";
import mongooseConnect from "@/lib/mongooseConnect";
import User from "@/models/User";
import Order from "@/models/Order";
import { buffer } from "micro";
const stripe = new Stripe(process.env.STRIPE_SK as string);
const webhookSecret = process.env.STRIPE_ENDPOINT_SECRET as string;
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
await mongooseConnect();
const sig = req.headers['stripe-signature'];
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(await buffer(req), sig!, webhookSecret);
} catch (err: any) {
console.error(`Webhook signature verification failed. ${err.message}`);
return res.status(400).json({ error: err.message });
}
const eventType = event.type;
try {
switch (eventType) {
case 'checkout.session.completed': {
const session = event.data.object as Stripe.Checkout.Session;
const email = session.customer_details?.email;
if (email) {
let user = await User.findOne({ email });
let subscription;
if (typeof session.subscription === 'string') {
subscription = await stripe.subscriptions.retrieve(session.subscription);
} else {
subscription = session.subscription as Stripe.Subscription;
}
const priceId = subscription?.items.data[0]?.price?.id || null;
if (!user) {
// Create a new user if none exists
user = await User.create({
email,
name: session.customer_details?.name || 'Unknown',
stripeCustomerId: session.customer as string,
priceId,
hasAccess: true,
});
} else {
// Update existing user with Stripe Customer ID if it doesn't exist
if (!user.stripeCustomerId) {
user.stripeCustomerId = session.customer as string;
}
// Ensure user has access
user.hasAccess = true;
user.priceId = priceId;
await user.save();
}
// Create an order record
await Order.create({
user: user._id,
name: user.name,
email: user.email,
stripeCustomerId: user.stripeCustomerId,
paid: true,
});
}
break;
}
case 'customer.subscription.deleted': {
const subscription = event.data.object as Stripe.Subscription;
const user = await User.findOne({
stripeCustomerId: subscription.customer as string,
});
if (user) {
// Revoke access
user.hasAccess = false;
await user.save();
}
break;
}
default:
console.log(`Unhandled event type ${event.type}`);
}
} catch (e: any) {
console.error(`Stripe error: ${e.message} | EVENT TYPE: ${eventType}`);
}
return res.status(200).json({ received: true });
}
export const config = {
api: {
bodyParser: false,
},
};
my model/user.ts:
import mongoose, { Schema, model, models } from "mongoose";
const userSchema = new Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: false, default: null },
image: { type: String, required: false },
stripeCustomerId: { type: String, required: false, default: null },
hasAccess: {type: Boolean, required: true, default: false},
priceId: { type: String, required: false, default: null },
emailVerified: { type: Boolean, default: false },
}, {timestamps: true});
const User = models.User || model("User", userSchema);
export default User;
it works,but after a subscription is canceled and you try paying again, it creates a new customer in stripe dashboard, and it mess up the code, any solution to customer duplication?
Upvotes: -1
Views: 30
Reputation: 2031
You should create (or retrieve if there's an existing one) a customer first, and pass the customer
to the checkout session creation API, so that Stripe will associate the newly created subscription with this customer instead of creating a new customer. Refer to the API reference for more details
Upvotes: 0