# Shopify Webhooks — Setup & Troubleshooting

## How it works

When an order is created in Shopify, Shopify POSTs the order payload to:

```
POST /webhooks/shopify/orders
```

Cydekick's `ShopifyOrdersController` receives it, verifies the HMAC signature, and creates the order in `channel_orders_orders` with its line items. This happens in real time — no polling needed.

The webhook URL is built from `APP_URL` in `.env`:

```php
$base = rtrim(config('app.url'), '/');
// e.g. https://cydekick.co.uk/webhooks/shopify/orders
```

---

## Registering webhooks

1. Go to **Channels → Edit → (scroll down)**
2. Optionally paste the **Webhook Secret** from Shopify (see below)
3. Click **Register Webhooks**

This registers two topics in Shopify:
- `orders/create` → `/webhooks/shopify/orders`
- `orders/fulfilled` → `/webhooks/shopify/fulfillments`

If they are already registered at the correct URL, the button reports "Already set" and does nothing — this is fine.

---

## Required Shopify app scope

The Shopify app must have these scopes (set in Partner Dashboard → App → Configuration):

```
read_products,write_products,read_inventory,write_inventory,
read_locations,read_orders,read_all_orders,
read_merchant_managed_fulfillment_orders,
write_merchant_managed_fulfillment_orders
```

`read_all_orders` is required to fetch orders older than 60 days via the REST API. Without it the Import Orders sync only returns the last ~33 orders.

After adding a scope in the Partner Dashboard, you must click **Re-authenticate Shopify** in Cydekick to issue a new token with the updated scopes.

---

## Webhook HMAC verification

Shopify signs all API-registered webhooks using the app's **client secret** (the API Secret / Client Secret from the Partner Dashboard). Cydekick verifies this automatically using the stored `api_secret` — no extra configuration needed.

> The "webhook signing secret" shown in Shopify Admin → Settings → Notifications is only used for webhooks set up manually through the admin UI. Our webhooks are registered via the API, so that secret is irrelevant and should not be entered anywhere.

---

## Production (cydekick.co.uk)

Webhooks work automatically provided:

- `APP_URL=https://cydekick.co.uk` is set in `/var/www/html/Cydekick/.env`
- The supervisor queue worker is running (orders are created synchronously in the controller, not queued, so this is not strictly required for order creation)
- The Webhook Secret matches what Shopify has

After every deployment, if you changed channel code, restart the supervisor worker:

```bash
sudo supervisorctl restart cydekick-worker:cydekick-worker_00
```

---

## Local development (localhost)

Shopify cannot POST to `http://localhost` — it has no route to your machine. You must use a tunnel.

### Using ngrok

**Every time your PC restarts:**

1. Start ngrok:
   ```bash
   ngrok http 80
   ```

2. Copy the HTTPS forwarding URL (e.g. `https://xxxx.ngrok.io`)

3. Update `.env`:
   ```
   APP_URL=https://xxxx.ngrok.io
   ```

4. Clear config cache:
   ```bash
   php artisan config:clear
   ```

5. Go to the channel **Edit** page → click **Register Webhooks**
   - This updates the registered URL in Shopify to your new ngrok address
   - The old URL from the previous session will be replaced

6. Create a test order in Shopify — it should appear in Cydekick within seconds

**Note:** ngrok generates a new URL on every restart unless you have a paid account with a fixed domain. Steps 2–5 must be repeated each time.

### Alternative: skip webhooks locally

For local dev you can just use **Import Orders** on the Order Sync page instead of relying on webhooks. Webhooks are more important to get right on the production server.

---

## Troubleshooting

### Orders not appearing after creation in Shopify

1. Check the registered webhook URL in Shopify:
   - Shopify Admin → Settings → Notifications → Webhooks
   - The address should be `https://cydekick.co.uk/webhooks/shopify/orders` (not localhost)

2. Check Laravel logs:
   ```bash
   grep "Shopify webhook" /var/www/html/Cydekick/storage/logs/laravel.log
   ```
   - HMAC failure → secret mismatch, re-save the Webhook Secret and re-register
   - No log entries at all → Shopify isn't reaching the URL (wrong APP_URL or DNS issue)
   - 500 errors → check the full trace in the log

3. Shopify shows webhook delivery failures:
   - Shopify Admin → Settings → Notifications → Webhooks → click the webhook → Recent deliveries
   - Shows the HTTP response code Cydekick returned

### Import Orders only returns ~33 orders

The `since_id` cursor is cached. Click **Reset Cursor** on the Order Sync page, then **Import Orders**. If still limited to ~33, the app is missing the `read_all_orders` scope — see above.

### Orders imported in wrong order (newest first)

The Shopify API returns orders oldest-first when `order=id asc` is passed (set in `ShopifyClient::getOrders()`). If order numbers look reversed, check that the code has been deployed and the supervisor restarted.
