diff --git a/src/package.json b/src/package.json index 9b8628e..090a254 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "web-app", "type": "module", - "version": "1.0.0", + "version": "1.1.0", "private": true, "scripts": { "dev": "vite --host :: --port 3000", diff --git a/supabase/functions/create-checkout-session/index.js b/supabase/functions/create-checkout-session/index.js deleted file mode 100644 index 6c02b1b..0000000 --- a/supabase/functions/create-checkout-session/index.js +++ /dev/null @@ -1,163 +0,0 @@ -/* eslint-disable no-undef */ -// Edge Function: create-checkout-session -// Environnement: Deno (Supabase Edge Functions self-host) -// Secrets/vars à définir (ex: volumes/functions/.env ou env docker): -// STRIPE_SECRET_KEY=sk_live_... -// SUPABASE_URL=https://supabase.abcdcode.fr (ou ce que tu utilises) -// SUPABASE_SERVICE_ROLE_KEY=xxxxxxxxxxxxxxxx - -import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; -import Stripe from "https://esm.sh/stripe@12.0.0"; -import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; - -const STRIPE_SECRET_KEY = Deno.env.get("STRIPE_SECRET_KEY") ?? ""; -const SUPABASE_URL = Deno.env.get("SUPABASE_URL") ?? ""; -const SUPABASE_SERVICE_ROLE_KEY = - Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? ""; - -if (!STRIPE_SECRET_KEY) { - console.warn("⚠️ STRIPE_SECRET_KEY is not set – Stripe calls will fail."); -} -if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) { - console.warn( - "⚠️ SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY is not set – database writes may fail.", - ); -} - -const stripe = new Stripe(STRIPE_SECRET_KEY, { - apiVersion: "2022-11-15", - httpClient: Stripe.createFetchHttpClient(), -}); - -const corsHeaders = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "authorization, x-client-info, apikey, content-type", -}; - -serve(async (req) => { - // Préflight CORS - if (req.method === "OPTIONS") { - return new Response("ok", { headers: corsHeaders }); - } - - try { - const body = await req.json().catch(() => null); - - if (!body) { - return new Response( - JSON.stringify({ error: "Invalid JSON body" }), - { - status: 400, - headers: { ...corsHeaders, "Content-Type": "application/json" }, - }, - ); - } - - const { priceId, orderData, successUrl, cancelUrl } = body; - - if (!priceId) { - return new Response( - JSON.stringify({ error: "priceId is required" }), - { - status: 400, - headers: { ...corsHeaders, "Content-Type": "application/json" }, - }, - ); - } - - if (!successUrl || !cancelUrl) { - return new Response( - JSON.stringify({ - error: "successUrl and cancelUrl are required", - }), - { - status: 400, - headers: { ...corsHeaders, "Content-Type": "application/json" }, - }, - ); - } - - if (!STRIPE_SECRET_KEY) { - return new Response( - JSON.stringify({ - error: "Stripe secret key is not configured on the server", - }), - { - status: 500, - headers: { ...corsHeaders, "Content-Type": "application/json" }, - }, - ); - } - - // 1. Client Supabase pour sauver la commande - const supabaseClient = createClient( - SUPABASE_URL, - SUPABASE_SERVICE_ROLE_KEY, - { - global: { - // On forwarde l'Authorization si tu en as besoin côté RLS - headers: { - Authorization: req.headers.get("Authorization") ?? "", - }, - }, - }, - ); - - // 2. Insertion dans la table "orders" - const { data: order, error: orderError } = await supabaseClient - .from("orders") - .insert({ - ...orderData, - status: "pending_payment", - created_at: new Date().toISOString(), - }) - .select() - .single(); - - if (orderError) { - console.error("Error saving order:", orderError); - throw new Error(`Error saving order: ${orderError.message}`); - } - - // 3. Création de la session de paiement Stripe - const session = await stripe.checkout.sessions.create({ - payment_method_types: ["card"], - line_items: [ - { - price: priceId, - quantity: 1, - }, - ], - mode: "payment", - - // ⚠️ Champs Stripe doivent être en snake_case - success_url: `${successUrl}&session_id={CHECKOUT_SESSION_ID}&order_id=${order?.id}`, - cancel_url: cancelUrl, - - metadata: { - order_id: order?.id?.toString(), - product_name: orderData?.productName ?? "", - }, - customer_email: orderData?.email ?? undefined, - }); - - return new Response( - JSON.stringify({ sessionId: session.id, url: session.url }), - { - headers: { ...corsHeaders, "Content-Type": "application/json" }, - status: 200, - }, - ); - } catch (error) { - console.error("create-checkout-session error:", error); - - return new Response( - JSON.stringify({ error: error.message ?? "Unknown error" }), - { - headers: { ...corsHeaders, "Content-Type": "application/json" }, - status: 400, - }, - ); - } -}); diff --git a/supabase/functions/send-order-confirmation-email/index.js b/supabase/functions/send-order-confirmation-email/index.js deleted file mode 100644 index d2dae43..0000000 --- a/supabase/functions/send-order-confirmation-email/index.js +++ /dev/null @@ -1,290 +0,0 @@ -/* eslint-disable no-undef */ -// Edge Function: send-order-confirmation-email -// Self-host Supabase (Docker) - -import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; -import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; -import Stripe from "https://esm.sh/stripe@12.0.0"; - -const RESEND_API_KEY = Deno.env.get("RESEND_API_KEY"); -const STRIPE_SECRET_KEY = Deno.env.get("STRIPE_SECRET_KEY") || ""; -const SUPABASE_URL = Deno.env.get("SUPABASE_URL") || ""; -const SUPABASE_SERVICE_ROLE_KEY = - Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") || ""; -const ADMIN_EMAIL = Deno.env.get("ADMIN_EMAIL") || ""; - -const stripe = new Stripe(STRIPE_SECRET_KEY, { - apiVersion: "2022-11-15", - httpClient: Stripe.createFetchHttpClient(), -}); - -const corsHeaders = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "authorization, x-client-info, apikey, content-type", -}; - -serve(async (req) => { - if (req.method === "OPTIONS") { - return new Response("ok", { headers: corsHeaders }); - } - - try { - const { orderData, sessionId } = await req.json(); - - // 1️⃣ Récupérer l'email vérifié depuis la session Stripe - let customerEmail = null; - - if (sessionId && STRIPE_SECRET_KEY) { - try { - const session = await stripe.checkout.sessions.retrieve(sessionId); - - // Priorité : customer_details.email puis customer_email - customerEmail = - session?.customer_details?.email || session?.customer_email; - } catch (stripeError) { - console.error("Error retrieving Stripe session:", stripeError); - // Fallback : email envoyé par le front - customerEmail = orderData?.email; - } - } else { - // Sans sessionId (tests par ex.) - customerEmail = orderData?.email; - } - - if (!customerEmail) { - console.warn("No email found in Stripe session or order data."); - // On continue quand même : la commande sera enregistrée mais sans email - } - - // 2️⃣ Client Supabase (SERVICE ROLE pour pouvoir écrire dans la table) - const supabaseClient = createClient( - SUPABASE_URL, - SUPABASE_SERVICE_ROLE_KEY, - { - global: { - headers: { - Authorization: req.headers.get("Authorization") ?? "", - }, - }, - }, - ); - - // 3️⃣ Enregistrer la commande dans Supabase - const finalOrderData = { - ...orderData, - customer_email: customerEmail, - session_id: sessionId, - status: "En attente de traitement", - created_at: new Date().toISOString(), - }; - - const { data: order, error: orderError } = await supabaseClient - .from("orders") - .insert(finalOrderData) - .select() - .single(); - - if (orderError) { - console.error("Error saving order to Supabase:", orderError); - // on continue quand même pour tenter les mails - } - - // 4️⃣ Email client (joli template violet) - let customerEmailError = null; - let customerEmailResult = null; - - if (RESEND_API_KEY && customerEmail) { - const res = await fetch("https://api.resend.com/emails", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${RESEND_API_KEY}`, - }, - body: JSON.stringify({ - from: "Dites le en chanson ", - to: [customerEmail], - subject: `Votre commande Dites le en chanson est confirmée ! (ID: ${order?.id?.slice(0, 8) ?? ""})`, - html: ` - - - - - -Confirmation de votre commande - Dites le en chanson - - - - - -
-
- Dites-le en chanson Logo -

Merci pour votre commande !

-
- -
-

Bonjour ${orderData?.recipient_name ?? "cher client"},

- -

- Nous sommes ravis de vous confirmer que votre commande a bien été enregistrée. - Notre équipe de créateurs est déjà prête à composer votre chanson personnalisée ! -

- -
-

Récapitulatif de votre commande (ID: ${order?.id?.slice(0,8) ?? ""}) :

- -

Produit: ${orderData?.product_name ?? "Chanson personnalisée"}

-

Prix Payé: ${orderData?.price ? orderData.price + " €" : "—"}

-

Pour: ${orderData?.recipient_name ?? "—"}

-

Langue: ${orderData?.language ?? "—"}

-

Voix: ${orderData?.voice_gender ?? "—"}

-

Style: ${orderData?.musical_style ?? "—"}

-

Ambiance: ${orderData?.mood ?? "—"}

- - ${orderData?.anecdote1 ? `

Anecdote 1: ${orderData.anecdote1}

` : ""} - ${orderData?.anecdote2 ? `

Anecdote 2: ${orderData.anecdote2}

` : ""} - ${orderData?.anecdote3 ? `

Anecdote 3: ${orderData.anecdote3}

` : ""} -
- -
-

Délai de création : Votre chanson unique sera prête et vous sera livrée par email dans les 48 heures.

-
- -

Nous mettons tout notre cœur pour transformer vos histoires en mélodies inoubliables.

-

Si vous avez la moindre question, n'hésitez pas à nous contacter.

- -

- Visiter notre site -

-
- - -
- - -`, - }), - }); - - const data = await res.json(); - - if (!res.ok) { - console.error("Resend API Error (Customer):", data); - customerEmailError = data.message || "Unknown Resend Error"; - } else { - customerEmailResult = data; - } - } else { - if (!RESEND_API_KEY) console.error("RESEND_API_KEY is missing"); - if (!customerEmail) console.error("No customer email available to send confirmation."); - } - - // 5️⃣ Email admin "Nouvelle Commande Reçue" - let adminEmailError = null; - let adminEmailResult = null; - - if (RESEND_API_KEY && ADMIN_EMAIL) { - const adminRes = await fetch("https://api.resend.com/emails", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${RESEND_API_KEY}`, - }, - body: JSON.stringify({ - from: "Dites le en chanson ", - to: [ADMIN_EMAIL], - subject: `Nouvelle commande reçue (ID: ${order?.id?.slice(0,8) ?? "N/A"})`, - html: ` - - - - -Nouvelle Commande Reçue - - -

Nouvelle Commande Reçue !

-

Une nouvelle commande a été passée sur votre site.

- -

ID de Commande : ${order?.id ?? "N/A"}

-

ID de Session Stripe : ${sessionId ?? "N/A"}

-

Email du Client : ${customerEmail ?? "N/A"}

-

Statut : ${finalOrderData.status}

- -

Détails de la commande :

- - - - `, - }), - }); - - const adminData = await adminRes.json(); - - if (!adminRes.ok) { - console.error("Resend API Error (Admin):", adminData); - adminEmailError = adminData.message || "Unknown Resend Error"; - } else { - adminEmailResult = adminData; - } - } else { - if (!ADMIN_EMAIL) console.error("ADMIN_EMAIL is missing"); - } - - return new Response( - JSON.stringify({ - success: true, - orderId: order?.id || "N/A", - customerEmail, - emailSent: !!customerEmailResult, - customerEmailError, - adminEmailSent: !!adminEmailResult, - adminEmailError, - dbError: orderError ? orderError.message : null, - }), - { - status: 200, - headers: { ...corsHeaders, "Content-Type": "application/json" }, - }, - ); - } catch (error) { - console.error("Function Critical Error:", error); - return new Response(JSON.stringify({ error: error.message }), { - status: 500, - headers: { ...corsHeaders, "Content-Type": "application/json" }, - }); - } -});