Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Crie uma Sessão de Checkout no servidor e, em seguida, redirecione o cliente para a página de checkout hospedada do Stripe. O Stripe cuida de toda a interface do usuário de pagamento, conformidade PCI e exibição de métodos de pagamento.
Crie uma Server Action para gerar a sessão:
// app/actions/checkout.ts
"use server";
import { stripe } from "@/lib/stripe";
import { redirect } from "next/navigation";
export async function createCheckoutSession(priceId: string) {
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: [
{
price: priceId,
quantity: 1,
},
],
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
});
redirect(session.url!);
}Ou use um Route Handler:
// app/api/checkout/route.ts
import { stripe } from "@/lib/stripe";
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const { priceId } = await request.json();
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: [
{
price: priceId,
quantity: 1,
},
],
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
});
return NextResponse.json({ url: session.url });
}// app/buy/page.tsx
import { createCheckoutSession } from "@/app/actions/checkout";
export default function BuyPage() {
return (
<div className="max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold mb-4">Curso Premium</h1>
<p className="text-gray-600 mb-6">
Compra única para acesso vitalício.
</p>
<form action={createCheckoutSession.bind(null, "price_xxx123")}>
<button
type="submit"
className="w-full bg-blue-600 text-white py-3 rounded-lg hover:bg-blue-700"
>
Comprar por $49
</button>
</form>
</div>
);
}// app/success/page.tsx
import { stripe } from "@/lib/stripe";
interface SuccessPageProps {
searchParams: Promise<{ session_id?: string }>;
}
export default async function SuccessPage({ searchParams }: SuccessPageProps) {
const { session_id } = await searchParams;
if (!session_id) {
return <p>Sessão inválida.</p>;
}
const session = await stripe.checkout.sessions.retrieve(session_id, {
expand: ["line_items", "customer"],
});
return (
<div className="max-w-md mx-auto p-8 text-center">
<h1 className="text-2xl font-bold mb-4">Pagamento Bem-sucedido!</h1>
<p className="text-gray-600">
Obrigado pela sua compra. Seu pagamento de{" "}
{(session.amount_total! / 100).toFixed(2)}{" "}
{session.currency?.toUpperCase()} foi recebido.
</p>
<p className="mt-4 text-sm text-gray-500">
ID da Sessão: {session.id}
</p>
</div>
);
}stripe.checkout.sessions.create gera uma sessão nos servidores do Stripe e retorna uma URL. Redirecionar o cliente para essa URL abre a página de checkout hospedada do Stripe.{CHECKOUT_SESSION_ID} é uma variável de modelo que o Stripe substitui pelo ID real da sessão ao redirecionar de volta para sua success_url.mode determina o tipo de pagamento: "payment" para pagamento único, "subscription" para cobrança recorrente ou "setup" para salvar um cartão sem cobrar.line_items pode referenciar objetos de Preço existentes (criados no Dashboard) ou usar price_data inline para precificação dinâmica.Dados de preço inline (sem objeto de Preço pré-criado):
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: [
{
price_data: {
currency: "usd",
product_data: {
name: "Widget Personalizado",
description: "Um widget único",
},
unit_amount: 2999, // $29.99 em centavos
},
quantity: 1,
},
],
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/shop`,
});Anexar a um cliente existente:
const session = await stripe.checkout.sessions.create({
mode: "payment",
customer: "cus_xxx",
line_items: [{ price: "price_xxx", quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
});Múltiplos itens de linha:
line_items: [
{ price: "price_basic", quantity: 2 },
{ price: "price_addon", quantity: 1 },
],stripe.checkout.sessions.create retorna Promise<Stripe.Checkout.Session>.session.url é do tipo string | null. Ela é null apenas para o modo de checkout incorporado, portanto, a asserção ! é segura para fluxos baseados em redirecionamento.Stripe.Checkout.SessionCreateParams para tipar objetos de configuração se você os construir dinamicamente.import type Stripe from "stripe";
const params: Stripe.Checkout.SessionCreateParams = {
mode: "payment",
line_items: [{ price: priceId, quantity: 1 }],
success_url: "...",
cancel_url: "...",
};session.url expira após 24 horas. Não a armazene para uso posterior.amount_total está na menor unidade monetária (centavos para USD). Divida por 100 antes de exibir.redirect() do Next.js em uma Server Action, ele lançará um erro NEXT_REDIRECT internamente. Este é o comportamento esperado -- não o envolva em um try/catch que engula o erro.| Abordagem | Prós | Contras |
|---|---|---|
| Stripe Checkout (redirecionamento) | Código mínimo, compatível com PCI, suporta mais de 40 métodos de pagamento | Personalização limitada da interface do usuário |
| Checkout Incorporado | Permanece no seu site via iframe | Ainda opções de estilo limitadas |
| PaymentIntent + PaymentElement | Controle total da interface do usuário | Mais código, mais responsabilidade |
| Links de Pagamento | Zero código necessário | Sem controle programático |
redirect() para enviar o usuário ao Stripe.O Stripe substitui {CHECKOUT_SESSION_ID} pelo ID real da sessão ao redirecionar o cliente de volta para o seu site. Você pode então recuperar a sessão no lado do servidor para verificar os detalhes do pagamento.
"payment" -- pagamento único"subscription" -- cobrança recorrente"setup" -- salvar um cartão sem cobrá-loUm usuário pode navegar manualmente para sua URL de sucesso sem pagar. Sempre verifique o pagamento via webhooks (checkout.session.completed) ou recuperando a sessão no lado do servidor e verificando seu payment_status.
line_items: [{
price_data: {
currency: "usd",
product_data: { name: "Widget Personalizado" },
unit_amount: 2999, // $29.99 em centavos
},
quantity: 1,
}]O Stripe armazena os valores na menor unidade monetária (centavos para USD). Portanto, 2999 significa $29.99. Sempre divida por 100 para exibição em dólares.
redirect() lança um erro NEXT_REDIRECT internamente. Se seu bloco catch o engolir, o redirecionamento nunca acontece. Chame redirect() fora do try/catch, ou re-lance explicitamente os erros de redirecionamento.
const session = await stripe.checkout.sessions.create({
mode: "payment",
customer: "cus_xxx",
line_items: [{ price: "price_xxx", quantity: 1 }],
success_url: "...",
cancel_url: "...",
});A URL da Sessão de Checkout expira após 24 horas. Sempre gere uma nova sessão quando o usuário iniciar o checkout -- nunca armazene a URL para uso posterior.
import type Stripe from "stripe";
const params: Stripe.Checkout.SessionCreateParams = {
mode: "payment",
line_items: [{ price: priceId, quantity: 1 }],
success_url: "...",
cancel_url: "...",
};É null apenas ao usar o modo de checkout incorporado (onde nenhuma URL de redirecionamento é necessária). Para fluxos baseados em redirecionamento, é sempre uma string, tornando a asserção não nula ! segura.
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥