import { serve } from "https://deno.land/std@0.190.0/http/server.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type", }; interface HandlePaidOrderRequest { order_id: string; user_id: string; total_amount: number; payment_method?: string; payment_provider?: string; } serve(async (req: Request): Promise => { if (req.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } try { const body: HandlePaidOrderRequest = await req.json(); const { order_id } = body; console.log("[HANDLE-PAID] Processing paid order:", order_id); const supabaseUrl = Deno.env.get("SUPABASE_URL")!; const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; const supabase = createClient(supabaseUrl, supabaseServiceKey); // Get full order details with items AND consulting slots const { data: order, error: orderError } = await supabase .from("orders") .select(` *, profiles(email, full_name), order_items ( product_id, product:products (title, type) ), consulting_slots ( id, date, start_time, end_time, status ) `) .eq("id", order_id) .single(); if (orderError || !order) { console.error("[HANDLE-PAID] Order not found:", order_id, orderError); return new Response( JSON.stringify({ success: false, error: "Order not found" }), { status: 404, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } console.log("[HANDLE-PAID] Order found:", JSON.stringify({ id: order.id, payment_status: order.payment_status, order_items_count: order.order_items?.length || 0, consulting_slots_count: order.consulting_slots?.length || 0, consulting_slots: order.consulting_slots })); const userEmail = order.profiles?.email || ""; const userName = order.profiles?.full_name || "Pelanggan"; const orderItems = order.order_items as Array<{ product_id: string; product: { title: string; type: string }; }>; // Check if this is a consulting order by checking consulting_slots const consultingSlots = order.consulting_slots as Array<{ id: string; date: string; start_time: string; end_time: string; status: string; meet_link?: string; }>; const isConsultingOrder = consultingSlots && consultingSlots.length > 0; console.log("[HANDLE-PAID] isConsultingOrder:", isConsultingOrder, "consultingSlots:", consultingSlots); if (isConsultingOrder) { console.log("[HANDLE-PAID] Consulting order detected, processing slots"); // Update consulting slots status from pending_payment to confirmed const { error: updateError } = await supabase .from("consulting_slots") .update({ status: "confirmed" }) .eq("order_id", order_id) .in("status", ["pending_payment"]); console.log("[HANDLE-PAID] Slot update result:", { updateError, order_id }); if (updateError) { console.error("[HANDLE-PAID] Failed to update slots:", updateError); } if (consultingSlots && consultingSlots.length > 0) { for (const slot of consultingSlots) { try { console.log("[HANDLE-PAID] Creating Google Meet for slot:", slot.id); const topic = "Konsultasi 1-on-1"; const meetResponse = await fetch( `${supabaseUrl}/functions/v1/create-google-meet-event`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${Deno.env.get("SUPABASE_ANON_KEY")}`, }, body: JSON.stringify({ slot_id: slot.id, date: slot.date, start_time: slot.start_time, end_time: slot.end_time, client_name: userName, client_email: userEmail, topic: topic, }), } ); if (meetResponse.ok) { const meetData = await meetResponse.json(); if (meetData.success) { console.log("[HANDLE-PAID] Meet created:", meetData.meet_link); } } } catch (error) { console.error("[HANDLE-PAID] Meet creation failed:", error); // Don't fail the entire process } } // Send consulting notification with the consultingSlots data await sendNotification(supabase, "consulting_scheduled", { nama: userName, email: userEmail, order_id: order_id.substring(0, 8), tanggal_pesanan: new Date().toLocaleDateString("id-ID"), total: `Rp ${order.total_amount.toLocaleString("id-ID")}`, metode_pembayaran: order.payment_method || "Unknown", tanggal_konsultasi: consultingSlots[0]?.date || "", jam_konsultasi: consultingSlots.map(s => s.start_time.substring(0, 5)).join(", "), link_meet: consultingSlots[0]?.meet_link || "Akan dikirim terpisah", event: "consulting_scheduled", order_id, user_id: order.user_id, user_name: userName, slots: consultingSlots, }); } else { // Regular product order - grant access console.log("[HANDLE-PAID] Regular product order, granting access"); for (const item of orderItems) { // Check if access already exists const { data: existingAccess } = await supabase .from("user_access") .select("id") .eq("user_id", order.user_id) .eq("product_id", item.product_id) .maybeSingle(); if (!existingAccess) { await supabase .from("user_access") .insert({ user_id: order.user_id, product_id: item.product_id, }); console.log("[HANDLE-PAID] Access granted for product:", item.product_id); } } const productTitles = orderItems.map(i => i.product.title); // Send payment success notification await sendNotification(supabase, "payment_success", { nama: userName, email: userEmail, order_id: order_id.substring(0, 8), tanggal_pesanan: new Date().toLocaleDateString("id-ID"), total: `Rp ${order.total_amount.toLocaleString("id-ID")}`, metode_pembayaran: order.payment_method || "Unknown", produk: productTitles.join(", "), link_akses: `${Deno.env.get("SITE_URL") || ""}/access`, event: "payment_success", order_id, user_id: order.user_id, user_name: userName, products: productTitles, }); // Send access granted notification await sendNotification(supabase, "access_granted", { nama: userName, email: userEmail, produk: productTitles.join(", "), event: "access_granted", order_id, user_id: order.user_id, user_name: userName, products: productTitles, }); } return new Response( JSON.stringify({ success: true, order_id }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } catch (error: any) { console.error("[HANDLE-PAID] Error:", error); return new Response( JSON.stringify({ success: false, error: error.message || "Internal server error" }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } }); // Helper function to send notification async function sendNotification( supabase: any, templateKey: string, data: Record ): Promise { console.log("[HANDLE-PAID] Sending notification:", templateKey); // Fetch template const { data: template } = await supabase .from("notification_templates") .select("*") .eq("key", templateKey) .single(); if (!template) { console.log("[HANDLE-PAID] Template not found:", templateKey); return; } // Send webhook if configured if (template.webhook_url) { try { await fetch(template.webhook_url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); console.log("[HANDLE-PAID] Webhook sent to:", template.webhook_url); } catch (error) { console.error("[HANDLE-PAID] Webhook failed:", error); } } // Skip email if template is inactive if (!template.is_active) { console.log("[HANDLE-PAID] Template inactive, skipping email"); return; } // Send email via Mailketing await fetch(`${Deno.env.get("SUPABASE_URL")}/functions/v1/send-email-v2`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${Deno.env.get("SUPABASE_ANON_KEY")}`, }, body: JSON.stringify({ to: data.email, subject: template.email_subject, html: template.email_body_html, shortcodeData: data, }), }); }