feat: initial commit — asaas-checkout template white-label

Template genérico de checkout com ASAAS, parametrizado via env vars.
Inclui fluxo completo: checkout → pedido → polling → webhook → admin.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 06:40:41 +02:00
commit 038ce3f556
103 changed files with 20709 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
import { getProdutoById } from "@/lib/actions"
import { ProductBuyForm } from "@/components/product-buy-form"
import { notFound } from "next/navigation"
import type { Metadata } from "next"
import { appConfig } from "@/lib/config"
type Props = { params: Promise<{ id: string }> }
export async function generateMetadata({ params }: Props): Promise<Metadata> {
try {
const { id } = await params
const produto = await getProdutoById(id)
if (!produto) return { title: "Produto não encontrado" }
return {
title: `${produto.nome}${appConfig.name}`,
description: produto.descricao ?? produto.nome,
}
} catch {
return { title: "Erro ao carregar produto" }
}
}
export default async function ProductPage({ params }: Props) {
const { id } = await params
const produto = await getProdutoById(id)
if (!produto) notFound()
const precoFormatado = (produto.preco_centavos / 100).toLocaleString("pt-BR", {
style: "currency",
currency: "BRL",
})
return (
<div className="bg-gradient-to-b from-gray-50 to-white min-h-screen">
<div className="container mx-auto py-16 px-4 sm:px-6 lg:px-8 max-w-5xl">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
{/* Detalhes do produto */}
<div>
<h1 className="text-3xl font-bold text-primary mb-4">{produto.nome}</h1>
{produto.descricao && (
<p className="text-gray-600 text-lg mb-6">{produto.descricao}</p>
)}
<div className="flex flex-wrap gap-3 text-sm text-gray-500">
{produto.tipo && (
<span className="bg-gray-100 px-3 py-1 rounded-full">
Tipo: <strong>{produto.tipo}</strong>
</span>
)}
{produto.validade && (
<span className="bg-gray-100 px-3 py-1 rounded-full">
Validade: <strong>{produto.validade}</strong>
</span>
)}
{produto.midia && (
<span className="bg-gray-100 px-3 py-1 rounded-full">
Mídia: <strong>{produto.midia}</strong>
</span>
)}
</div>
</div>
{/* Formulário de compra */}
<div className="bg-white p-8 rounded-2xl shadow-lg">
<p className="text-4xl font-bold mb-6 text-primary">{precoFormatado}</p>
<ProductBuyForm produtoId={produto.id} />
</div>
</div>
</div>
</div>
)
}

5
app/produtos/page.tsx Normal file
View File

@@ -0,0 +1,5 @@
import { redirect } from "next/navigation"
export default function ProdutosPage() {
redirect("/comprar")
}