Refactor payment flow to use database triggers (Clean Architecture)

BREAKING CHANGE: Complete refactor of payment handling

New Architecture:
1. pakasir-webhook (120 lines -> was 535 lines)
   - Only verifies signature and updates order status
   - Removed: SMTP, email templates, notification logic

2. Database Trigger (NEW)
   - Automatically fires when payment_status = 'paid'
   - Calls handle-order-paid edge function
   - Works for webhook AND manual admin updates

3. handle-order-paid (NEW edge function)
   - Grants user access for products
   - Creates Google Meet events for consulting
   - Sends notifications via send-email-v2
   - Triggers webhooks

Benefits:
- Single Responsibility: Each function has one clear purpose
- Trigger works for both webhook and manual admin actions
- Easier to debug and maintain
- Reusable notification system

Migration required: Run 20241223_payment_trigger.sql

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
dwindown
2025-12-23 16:59:13 +07:00
parent 9d7d76b04d
commit 01579ac299
4 changed files with 386 additions and 417 deletions

View File

@@ -0,0 +1,93 @@
-- ============================================================================
-- 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;