# The Exact Webhook Process Implemented on Our System

This document explains the **exact, real process we implemented for our server, database, and webhooks** on the WhatsApp Commerce Hub.

---

## 1. Our Real Server & Webhook Details

- **Public Domain (Cloudflare Tunnel):** `https://api.jeftertokomere.me`
- **Internal Port Mapping:** Cloudflare routes traffic &rarr; Host port `8002` &rarr; Docker container port `3001` (`whatsapp_commerce_backend`).
- **Active Business ID (Seed Co Shop):** `eb25e1e4-241a-4514-a034-2f1aa15d9ff3`

### Our Active Webhook URLs:

| Webhook | Exact URL on Our Server |
| :--- | :--- |
| **WooCommerce Orders** | `https://api.jeftertokomere.me/webhooks/woocommerce/orders?businessId=eb25e1e4-241a-4514-a034-2f1aa15d9ff3` |
| **WooCommerce Products** | `https://api.jeftertokomere.me/webhooks/woocommerce/products?businessId=eb25e1e4-241a-4514-a034-2f1aa15d9ff3` |
| **WhatsApp Cloud API** | `https://api.jeftertokomere.me/webhooks/whatsapp` (Verify Token: `whatsapp-verify-token`) |
| **PesePay Payments** | `https://api.jeftertokomere.me/webhooks/payments/pesepay` |

---

## 2. The Complete Process We Implemented (Step-by-Step)

### Step 1: Eliminated Manual Prompts from the Frontend
- **File:** `frontend/src/app/dashboard/integrations/page.tsx`
- **What was there before:** A manual button called "Setup Webhooks" that opened a browser prompt (`prompt("Enter your cloudflared public tunnel URL...")`), forcing the user to type or paste a URL.
- **What we did:**
  - Removed the prompt dialog completely.
  - Hooked webhook setup into the **"Save Configuration"** button: whenever the merchant saves WooCommerce keys, webhooks register automatically in the background.
  - Added a 1-click **"Register / Refresh Webhooks"** button that runs automatically without prompting for any URL.

---

### Step 2: Auto-Resolving the Public URL in the Backend
- **Files:**
  - `backend/src/modules/business/business.service.ts`
  - `backend/src/modules/business/business.controller.ts`
- **What we did:**
  - The backend reads `process.env.PUBLIC_URL` or `process.env.BACKEND_URL` (`https://api.jeftertokomere.me`).
  - It automatically builds the delivery URLs with the merchant's business ID:
    ```typescript
    const orderDeliveryUrl = `${backendUrl}/webhooks/woocommerce/orders?businessId=${businessId}`;
    const productDeliveryUrl = `${backendUrl}/webhooks/woocommerce/products?businessId=${businessId}`;
    ```
  - Zero user input required.

---

### Step 3: Automated WooCommerce REST API Webhook Registration
- **File:** `backend/src/modules/connectors/woocommerce/woocommerce.connector.ts` (`registerWebhooks()`)
- **What we did:**
  1. **Prune Outdated Webhooks:** The connector calls `GET /wp-json/wc/v3/webhooks`. If it finds old webhooks whose delivery URL does not match our current domain, it automatically deletes them (`DELETE /wp-json/wc/v3/webhooks/<id>?force=true`).
  2. **Register 5 Webhooks Automatically:** It registers all required topics if they don't already exist:
     - `order.created` &rarr; points to `/webhooks/woocommerce/orders`
     - `order.updated` &rarr; points to `/webhooks/woocommerce/orders`
     - `product.created` &rarr; points to `/webhooks/woocommerce/products`
     - `product.updated` &rarr; points to `/webhooks/woocommerce/products`
     - `product.deleted` &rarr; points to `/webhooks/woocommerce/products`

---

### Step 4: Safely Handled WooCommerce Ping Handshakes
- **File:** `backend/src/modules/commerce/woocommerce-webhook.controller.ts`
- **The Problem:** When WooCommerce activates a webhook, it immediately sends a test ping payload (`{"webhook_id": 123}`). Previously, without an order ID, the server could treat it as an error or create a blank order.
- **What we did:**
  - Added initial ping handshake detection in both `/orders` and `/products`:
    ```typescript
    if (payload?.webhook_id && !payload?.id) {
      return { success: true, message: 'WooCommerce webhook ping received successfully' };
    }
    ```
  - Now WooCommerce marks the webhook as **Active** immediately upon receiving HTTP 200.

---

### Step 5: Real-Time Product Sync to Chatbot via Webhook
- **File:** `backend/src/modules/commerce/woocommerce-webhook.controller.ts` (`@Post('products')`)
- **What we did:**
  - When you add, edit, or delete a product in WooCommerce:
    1. WooCommerce sends the product payload to `/webhooks/woocommerce/products`.
    2. The controller strips HTML tags from descriptions (so they render cleanly in WhatsApp).
    3. Upserts categories and extracts SKU, price, stock status, and images.
    4. Upserts the product and variants into the local database (`Prisma.product`).
    5. If deleted/trashed, it sets status to `INACTIVE`.
  - The WhatsApp chatbot queries this database directly, so new products appear in the chatbot **instantly** without needing a backend restart or manual sync.

---

### Step 6: Scheduled 30-Minute Backup Auto-Sync
- **Files:**
  - `backend/src/app.module.ts`
  - `backend/src/modules/commerce/services/sync-queue.service.ts`
- **What we did:**
  - Initialized `@nestjs/schedule` (`ScheduleModule.forRoot()`).
  - Added a cron job `@Cron(CronExpression.EVERY_30_MINUTES)` in `SyncQueueService` that automatically syncs products and categories across all active WooCommerce stores as a safety net in case network drops a webhook.

---

### Step 7: Resolved WhatsApp Native Catalog Cart Matching (e.g. SC 727)
- **File:** `backend/src/modules/whatsapp/whatsapp.controller.ts`
- **The Problem:** When ordering from the WhatsApp Catalog, Meta sends its own `product_retailer_id` (e.g., `"236"` for `SC 727`), which did not match WooCommerce's internal ID (`27960`), causing the bot to say *"couldn't match the items to our store records"*.
- **What we did:**
  - Added a live Meta Graph API fallback lookup:
    ```typescript
    const metaRes = await axios.get(`https://graph.facebook.com/v19.0/${catalogId}/products`, {
      headers: { Authorization: `Bearer ${token}` },
      params: { filter: JSON.stringify({ retailer_id: { eq: rawId } }) }
    });
    ```
  - When `"236"` arrives, Meta returns product name `"SC 727"` and price `"$14.00"`.
  - The backend instantly matches `"SC 727"` in our database, picks the closest variant (`SC 727 - 2kg` at `$14.74`), and adds it to the customer's WhatsApp cart.

---

### Step 8: Updated Production Docker Environment
- **File:** `docker-compose.yml`
- **What we did:**
  - Set `PUBLIC_URL=https://api.jeftertokomere.me`
  - Set `BACKEND_URL=https://api.jeftertokomere.me`
  - Set `FRONTEND_URL=https://api.jeftertokomere.me`
  - Rebuilt and restarted the `whatsapp_commerce_backend` container on host port `8002`.
