-- ============================================================================ -- Payment Trigger Architecture -- ============================================================================ -- This refactors the payment flow to use database triggers instead of -- handling everything in the webhook function. -- -- Flow: -- 1. pakasir-webhook or admin updates order.payment_status = 'paid' -- 2. Trigger fires -> calls handle_paid_order() function -- 3. handle_paid_order() calls handle-order-paid edge function -- 4. Edge function handles: access grants, notifications, meet links -- ============================================================================ -- Enable pg_net extension for HTTP calls from PostgreSQL CREATE EXTENSION IF NOT EXISTS pg_net; -- ============================================================================ -- Function: handle_paid_order -- Purpose: Called by trigger when order payment_status becomes 'paid' -- Calls the edge function to handle all post-payment actions -- ============================================================================ CREATE OR REPLACE FUNCTION handle_paid_order() RETURNS TRIGGER AS $$ DECLARE edge_function_url TEXT; edge_function_response TEXT; order_data JSON; BEGIN -- Only proceed if payment_status changed to 'paid' IF (NEW.payment_status != 'paid' OR OLD.payment_status = 'paid') THEN RETURN NEW; END IF; -- Log the payment event RAISE NOTICE 'Order % payment status changed to paid', NEW.id; -- Get the edge function URL from environment edge_function_url := current_setting('app.base_url', true) || '/functions/v1/handle-order-paid'; -- Prepare order data order_data := json_build_object( 'order_id', NEW.id, 'user_id', NEW.user_id, 'total_amount', NEW.total_amount, 'payment_method', NEW.payment_method, 'payment_provider', NEW.payment_provider ); -- Call the edge function asynchronously via pg_net -- We use pg_net to avoid blocking the transaction PERFORM net.http_post( url := edge_function_url, headers := json_build_object( 'Content-Type', 'application/json', 'Authorization', 'Bearer ' || current_setting('app.service_role_key', true) ), body := order_data ); RAISE NOTICE 'Called handle-order-paid for order %', NEW.id; RETURN NEW; EXCEPTION WHEN OTHERS THEN -- Log error but don't fail the transaction RAISE WARNING 'Failed to call handle-order-paid for order %: %', NEW.id, SQLERRM; RETURN NEW; END; $$ LANGUAGE plpgsql; -- ============================================================================ -- Trigger: on_order_paid -- Purpose: Fires handle_paid_order() when order payment status changes -- ============================================================================ DROP TRIGGER IF EXISTS on_order_paid ON orders; CREATE TRIGGER on_order_paid AFTER UPDATE ON orders FOR EACH ROW WHEN (NEW.payment_status = 'paid' AND OLD.payment_status IS DISTINCT FROM NEW.payment_status) EXECUTE FUNCTION handle_paid_order(); -- ============================================================================ -- Comments for documentation -- ============================================================================ COMMENT ON FUNCTION handle_paid_order() IS 'Triggered when order payment_status becomes "paid". Calls handle-order-paid edge function to handle access grants, notifications, and Meet link creation.'; COMMENT ON TRIGGER on_order_paid ON orders IS 'Fires handle_paid_order() function when payment status changes to paid'; -- ============================================================================ -- Grant necessary permissions -- ============================================================================ GRANT EXECUTE ON FUNCTION handle_paid_order() TO postgres; GRANT USAGE ON SCHEMA net TO postgres;