rebours/server.ts
ordinarthur a9ebfb7b77 clean
2026-02-24 15:36:30 +01:00

121 lines
4.5 KiB
TypeScript

import { Elysia, t } from 'elysia'
import { cors } from '@elysiajs/cors'
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY ?? '', {
apiVersion: '2025-01-27.acacia',
})
const DOMAIN = process.env.DOMAIN ?? 'http://localhost:3000'
const PRODUCTS = {
lumiere_orbitale: {
name: 'LUMIÈRE_ORBITALE — REBOUR',
description: 'Lampe de table unique. Béton texturé coulé à la main + dôme céramique laqué. Collection 001.',
amount: 180000,
currency: 'eur',
},
}
const app = new Elysia()
.use(cors({ origin: '*', methods: ['GET', 'POST'] }))
// ── SEO : robots + sitemap (nginx ne les génère pas dynamiquement) ──────────
.get('/robots.txt', () =>
new Response(`User-agent: *\nAllow: /\nSitemap: ${DOMAIN}/sitemap.xml\n`, {
headers: { 'Content-Type': 'text/plain', 'Cache-Control': 'public, max-age=86400' },
})
)
.get('/sitemap.xml', () => {
const today = new Date().toISOString().split('T')[0]
return new Response(
`<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n <url><loc>${DOMAIN}/</loc><lastmod>${today}</lastmod><changefreq>weekly</changefreq><priority>1.0</priority></url>\n</urlset>`,
{ headers: { 'Content-Type': 'application/xml', 'Cache-Control': 'public, max-age=86400' } }
)
})
// ── API Stripe : créer session checkout ───────────────────────────────────
.post(
'/api/checkout',
async ({ body }) => {
const product = PRODUCTS[body.product as keyof typeof PRODUCTS]
if (!product) return new Response('Produit inconnu', { status: 404 })
const session = await stripe.checkout.sessions.create({
mode: 'payment',
payment_method_types: ['card'],
line_items: [{
price_data: {
currency: product.currency,
unit_amount: product.amount,
product_data: { name: product.name, description: product.description },
},
quantity: 1,
}],
success_url: `${DOMAIN}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${DOMAIN}/#collection`,
locale: 'fr',
customer_email: body.email ?? undefined,
custom_text: {
submit: { message: 'Pièce unique — fabriquée à Paris. Délai : 6 à 8 semaines.' },
},
})
return { url: session.url }
},
{
body: t.Object({
product: t.String(),
email: t.Optional(t.String()),
}),
}
)
// ── API : vérifier session après paiement ─────────────────────────────────
.get(
'/api/session/:id',
async ({ params }) => {
const session = await stripe.checkout.sessions.retrieve(params.id)
return {
status: session.payment_status,
amount: session.amount_total,
currency: session.currency,
customer_email: session.customer_details?.email ?? null,
}
},
{ params: t.Object({ id: t.String() }) }
)
// ── Webhook Stripe ─────────────────────────────────────────────────────────
.post('/api/webhook', async ({ request, headers }) => {
const sig = headers['stripe-signature']
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET
if (!sig || !webhookSecret) return new Response('Missing signature', { status: 400 })
let event: Stripe.Event
try {
event = stripe.webhooks.constructEvent(
Buffer.from(await request.arrayBuffer()), sig, webhookSecret
)
} catch {
return new Response('Webhook Error', { status: 400 })
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object as Stripe.Checkout.Session
if (session.payment_status === 'paid') {
console.log(`✓ Paiement — ${session.id}${session.customer_details?.email}`)
}
}
return { received: true }
})
.listen(3000)
console.log(`
┌──────────────────────────────────────┐
│ REBOUR API — http://localhost:3000 │
│ NODE_ENV: ${process.env.NODE_ENV ?? 'development'}
└──────────────────────────────────────┘
`)