I put a site online with Turbo repo on which I have a subscription system with Stripe
Locally, everything is working properly, I have no errors, my environment variables are well defined, my subscriptions are working well, etc.
As soon as I want to put my site into production, I get the error Error: Missing STRIPE_SECRET_KEY environment variable
.
I have this key in my environment variables and it has a value.
I currently have the following error: live:build: Error: Missing STRIPE_SECRET_KEY environment variable live:build: at 25070 (/vercel/path0/apps/live/.next/server/app/api/webhook/stripe/route.js:1:88381) live:build: at t (/vercel/path0/apps/live/.next/server/webpack-runtime.js:1:143) live:build: at /vercel/path0/apps/live/.next/server/app/api/webhook/stripe/route.js:1:131928 live:build: at t.X (/vercel/path0/apps/live/.next/server/webpack-runtime.js:1:1312) live:build: at /vercel/path0/apps/live/.next/server/app/api/webhook/stripe/route.js:1:131891 live:build: at Object.<anonymous> (/vercel/path0/apps/live/.next/server/app/api/webhook/stripe/route.js:1:131964) live:build: at Module._compile (node:internal/modules/cjs/loader:1562:14) live:build: at Object..js (node:internal/modules/cjs/loader:1699:10) live:build: at Module.load (node:internal/modules/cjs/loader:1313:32) live:build: at Function._load (node:internal/modules/cjs/loader:1123:12) live:build: live:build: > Build error occurred live:build: Error: Failed to collect page data for /api/webhook/stripe live:build: at /vercel/path0/node_modules/next/dist/build/utils.js:1268:15 live:build: at process.processTicksAndRejections (node:internal/process/task_queues:105:5) { live:build: type: 'Error' live:build: }
I would like this variable to be read and functional as it is already provided in all my environments.
To make my stripe work, I have the following configuration:
actions.ts : `“use server”;
import { UserRole } from “@prisma/client”;
import { prisma } from “@repo/db”;
import { redirect } from “next/navigation”;
import { addUserStripeCustomerId } from “…/actions/user-informations”;
import { stripe } from “./stripe”;
import stripeConfig from “./stripe.config”;
export const createStripeCheckoutSession = async (
customerId: string,
priceId: string,
userId: string
) => {
try {
const session = await stripe.checkout.sessions.create({
customer: customerId,
line_items: [
{
price: priceId,
quantity: 1,
},
],
mode: “subscription”,
success_url: ${stripeConfig.url.success}
,
cancel_url: ${stripeConfig.url.cancel}
,
metadata: {
userId,
},
});
return session;
} catch (error) {
console.error(error);
return null;
}
};
export const createStripeCustomer = async (
userId: string,
name: string,
email: string
) => {
try {
const customer = await stripe.customers.create({
email,
name,
});
const res = await addUserStripeCustomerId(userId, customer.id);
if (!res) {
throw new Error("Impossible de sauvegarder le customer id");
}
return customer;
} catch (error) {
console.error(error);
return null;
}
};
export const getUserFromPayment = async (paymentIntentId: string) => {
try {
// Récupérer le Payment Intent
const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId);
// Accéder aux métadonnées pour trouver l'utilisateur
const userId = paymentIntent.metadata?.userId;
console.log("Utilisateur identifié :", userId);
return userId;
} catch (error) {
console.error(“Erreur lors de la récupération du paiement :”, error);
return null;
}
};
export const getUserFromCustomerId = async (customerId: unknown) => {
if (typeof customerId !== “string”) {
console.error(“Le customerId n’est pas une string”);
return null;
}
try {
const customer = await prisma.user.findFirst({
where: {
stripeCustomerId: customerId,
},
});
return customer;
} catch (error) {
console.error(“Erreur lors de la récupération du customer :”, error);
return null;
}
};
export const createBillingPortalSession = async (
customerId: string,
role: UserRole
) => {
const stripeSession = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: ${stripeConfig.billing.return_url}
,
});
if (!stripeSession) throw new Error(“Impossible de créer la session”);
redirect(stripeSession.url);
};```
stripe.ts: `import Stripe from “stripe”;
if (!process.env.STRIPE_SECRET_KEY) {
throw new Error(“Missing STRIPE_SECRET_KEY environment variable”);
}
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: “2024-12-18.acacia”,
});`
role-manager.ts: `“use server”;
import { UserRole } from “@prisma/client”;
import Stripe from “stripe”;
import { downgradeUserRole, upgradeUserRole } from “…/actions/user-role”;
import { getUserFromCustomerId } from “./actions”;
export const checkoutSessionCompleted = async (event: Stripe.Event) => {
const session = event.data.object as Stripe.Checkout.Session;
const stripeCustomerId = session.customer;
const user = await getUserFromCustomerId(stripeCustomerId);
if (!user) {
console.error(“User not found for stripe customer id”, stripeCustomerId);
return;
}
await upgradeUserRole(user.id, UserRole.AGENT);
return;
};
export const invoicePaid = async (event: Stripe.Event) => {
const invoice = event.data.object as Stripe.Invoice;
const stripeCustomerId = invoice.customer;
const user = await getUserFromCustomerId(stripeCustomerId);
if (!user) {
console.error(“User not found for stripe customer id”, stripeCustomerId);
return;
}
await upgradeUserRole(user.id, UserRole.AGENT);
return;
};
export const invoicePaymentFailed = async (event: Stripe.Event) => {
const invoice = event.data.object as Stripe.Invoice;
const stripeCustomerId = invoice.customer;
const user = await getUserFromCustomerId(stripeCustomerId);
if (!user) {
console.error(“User not found for stripe customer id”, stripeCustomerId);
return;
}
await downgradeUserRole(user.id);
return;
};
export const subscriptionDeleted = async (event: Stripe.Event) => {
const subscription = event.data.object as Stripe.Subscription;
const stripeCustomerId = subscription.customer;
const user = await getUserFromCustomerId(stripeCustomerId);
if (!user) {
console.error(“User not found for stripe customer id”, stripeCustomerId);
return;
}
await downgradeUserRole(user.id);
return;
};`
And my API route at “api/webhook/stripe/route.ts” : `“use server”;
import {
checkoutSessionCompleted,
invoicePaid,
invoicePaymentFailed,
subscriptionDeleted,
} from “@repo/lib/stripe/role-management”;
import { NextRequest, NextResponse } from “next/server”;
import Stripe from “stripe”;
export const POST = async (req: NextRequest) => {
const body = (await req.json()) as Stripe.Event;
switch (body.type) {
case “checkout.session.completed”: {
await checkoutSessionCompleted(body);
break;
}
case “invoice.paid”: {
await invoicePaid(body);
break;
}
case “invoice.payment_failed”: {
await invoicePaymentFailed(body);
break;
}
case “customer.subscription.deleted”: {
await subscriptionDeleted(body);
break;
}
default: {
console.log(“Unhandled event type”, body.type);
break;
}
}
return NextResponse.json({ status: 200, ok: true });
};`
In local all it’s working well but I encounter error when I’m deploying this code
I’m using NextJS with the app router, node 22 and stripe 17.5