141 lines
4.5 KiB
TypeScript
141 lines
4.5 KiB
TypeScript
import { Elysia, t } from 'elysia'
|
|
import { cors } from '@elysiajs/cors'
|
|
import { staticPlugin } from '@elysiajs/static'
|
|
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'
|
|
|
|
// Produit LUMIÈRE_ORBITALE — prix en centimes (EUR)
|
|
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, // 1800 EUR
|
|
currency: 'eur',
|
|
},
|
|
}
|
|
|
|
const app = new Elysia()
|
|
.use(cors({
|
|
origin: DOMAIN,
|
|
methods: ['GET', 'POST'],
|
|
}))
|
|
// Sert les fichiers statiques (html, css, js, assets)
|
|
.use(staticPlugin({
|
|
assets: '.',
|
|
prefix: '/',
|
|
indexHTML: true,
|
|
}))
|
|
|
|
// ── Créer une session de checkout Stripe ──────────────────────────────────
|
|
.post(
|
|
'/api/checkout',
|
|
async ({ body }) => {
|
|
const product = PRODUCTS[body.product as keyof typeof PRODUCTS]
|
|
if (!product) {
|
|
throw new Error('Produit inconnu')
|
|
}
|
|
|
|
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.html?session_id={CHECKOUT_SESSION_ID}`,
|
|
cancel_url: `${DOMAIN}/#collection`,
|
|
locale: 'fr',
|
|
custom_text: {
|
|
submit: { message: 'Pièce unique — fabriquée à Paris. Délai de fabrication : 6 à 8 semaines.' },
|
|
},
|
|
})
|
|
|
|
return { url: session.url }
|
|
},
|
|
{
|
|
body: t.Object({
|
|
product: t.String(),
|
|
email: t.Optional(t.String({ format: 'email' })),
|
|
}),
|
|
}
|
|
)
|
|
|
|
// ── Vérification de session (page success) ────────────────────────────────
|
|
.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
|
|
const rawBody = await request.arrayBuffer()
|
|
try {
|
|
event = stripe.webhooks.constructEvent(
|
|
Buffer.from(rawBody),
|
|
sig,
|
|
webhookSecret
|
|
)
|
|
} catch (err) {
|
|
console.error('⚠ Webhook signature invalide:', err)
|
|
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 reçu — session: ${session.id}`)
|
|
console.log(` Client: ${session.customer_details?.email}`)
|
|
console.log(` Montant: ${(session.amount_total ?? 0) / 100} EUR`)
|
|
// → ici : envoyer email confirmation, mettre à jour BDD, etc.
|
|
}
|
|
}
|
|
|
|
return { received: true }
|
|
})
|
|
|
|
.listen(3000)
|
|
|
|
console.log(`
|
|
┌──────────────────────────────────────┐
|
|
│ REBOUR — SERVEUR DÉMARRÉ │
|
|
│ http://localhost:3000 │
|
|
│ │
|
|
│ POST /api/checkout │
|
|
│ GET /api/session/:id │
|
|
│ POST /api/webhook │
|
|
└──────────────────────────────────────┘
|
|
`)
|