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); console.log("[HANDLE-PAID] Request body:", JSON.stringify(body)); 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 sessions // Use maybeSingle() in case there are no related records const { data: order, error: orderError } = await supabase .from("orders") .select(` *, profiles(email, name), order_items ( product_id, product:products (title, type) ), consulting_sessions ( id, session_date, start_time, end_time, status, topic_category ) `) .eq("id", order_id) .maybeSingle(); if (orderError) { console.error("[HANDLE-PAID] Database error:", orderError); return new Response( JSON.stringify({ success: false, error: "Database error", details: orderError.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } if (!order) { console.error("[HANDLE-PAID] Order not found:", order_id); return new Response( JSON.stringify({ success: false, error: "Order not found", order_id }), { 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_sessions_count: order.consulting_sessions?.length || 0, consulting_sessions: order.consulting_sessions })); const userEmail = order.profiles?.email || ""; const userName = order.profiles?.name || userEmail.split('@')[0] || "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_sessions const consultingSessions = order.consulting_sessions as Array<{ id: string; session_date: string; start_time: string; end_time: string; status: string; topic_category?: string; meet_link?: string; }>; const isConsultingOrder = consultingSessions && consultingSessions.length > 0; console.log("[HANDLE-PAID] isConsultingOrder:", isConsultingOrder, "consultingSessions:", consultingSessions); if (isConsultingOrder) { console.log("[HANDLE-PAID] Consulting order detected, processing sessions"); // Update consulting sessions status from pending_payment to confirmed const { error: updateError } = await supabase .from("consulting_sessions") .update({ status: "confirmed" }) .eq("order_id", order_id) .in("status", ["pending_payment"]); console.log("[HANDLE-PAID] Session update result:", { updateError, order_id }); if (updateError) { console.error("[HANDLE-PAID] Failed to update sessions:", updateError); } if (consultingSessions && consultingSessions.length > 0) { try { console.log("[HANDLE-PAID] Creating Google Meet for order:", order_id); // Use the first session for Meet creation const session = consultingSessions[0]; const topic = session.topic_category || "Konsultasi 1-on-1"; console.log("[HANDLE-PAID] Session time:", `${session.start_time} - ${session.end_time}`); 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: session.id, date: session.session_date, start_time: session.start_time, end_time: session.end_time, client_name: userName, client_email: userEmail, topic: topic, notes: `Session ID: ${session.id}`, }), } ); console.log("[HANDLE-PAID] Meet response status:", meetResponse.status); if (meetResponse.ok) { const meetData = await meetResponse.json(); console.log("[HANDLE-PAID] Meet response data:", meetData); if (meetData.success) { console.log("[HANDLE-PAID] Meet created:", meetData.meet_link); // Update session with meet link const { error: updateError } = await supabase .from("consulting_sessions") .update({ meet_link: meetData.meet_link }) .eq("order_id", order_id); if (updateError) { console.error("[HANDLE-PAID] Failed to update meet_link:", updateError); } else { console.log("[HANDLE-PAID] Meet link updated for session:", order_id); } } else { console.error("[HANDLE-PAID] Meet creation returned success=false:", meetData); } } else { const errorText = await meetResponse.text(); console.error("[HANDLE-PAID] Meet creation failed with status:", meetResponse.status); console.error("[HANDLE-PAID] Error response:", errorText); } } catch (error) { console.error("[HANDLE-PAID] Meet creation exception:", error); // Don't fail the entire process } } // Send consulting notification with the consultingSessions data await sendNotification(supabase, "consulting_scheduled", { nama: userName, email: userEmail, order_id_short: 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: consultingSessions[0]?.session_date || "", jam_konsultasi: consultingSessions.map(s => `${s.start_time.substring(0, 5)} - ${s.end_time.substring(0, 5)}`).join(", "), link_meet: consultingSessions[0]?.meet_link || "Akan dikirim terpisah", event: "consulting_scheduled", order_id, user_id: order.user_id, user_name: userName, slots: consultingSessions, }); } 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_short: 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, }), }); }