# OrderCourier Hooks API

This document describes all the filters and actions available in OrderCourier FREE for extending functionality through add-ons like OrderCourier PRO.

## Table of Contents

- [Agent Selection](#agent-selection)
- [Message Building](#message-building)
- [Settings & UI](#settings--ui)
- [Order Processing](#order-processing)
- [Events & Reports](#events--reports)
- [Admin Pages](#admin-pages)

---

## Agent Selection

### `ordercourier_resolve_agent`

**Type:** Filter  
**Since:** 1.0.0

Resolve which agent (phone number) should be used for a specific order or cart context.

**FREE Precedence (when PRO is not active):**
1. Product override (`_oc_agent_phone` on product) - applies only to single-item carts/orders
2. Category override (`_oc_agent_phone` on term) - first matching category in cart order
3. Default phone from settings

**PRO Precedence (when PRO is active):**
1. PRO agent with matching rule (product/category/country) - in agent priority order, must be online
2. FREE product override - if no PRO rule matched and cart has single item
3. FREE category override - if no PRO rule matched  
4. PRO agent without rules - in priority order, must be online
5. Default phone from settings

**Multi-item carts:** When cart has multiple items, `product_id` is null. Categories from all items are merged. The first category (in cart order) that has an override wins.

**Parameters:**
- `$agent_phone` (string) - Resolved phone from FREE (product override, category override, or default)
- `$context` (array) - Context data with keys:
  - `product_id` (int|null) - Product ID if single product, null for multi-item carts
  - `category_ids` (array) - Category IDs from cart/product (parent categories for variations)
  - `country` (string) - Customer country code
  - `language` (string) - Current language code (WPML/Polylang)
  - `cart` (WC_Cart|null) - Cart object if available
  - `order` (WC_Order|null) - Order object if available
  - `_resolved_source` (string) - How FREE resolved the phone: 'product_override', 'category_override', or 'default'

**Return:** `string` - Phone number in E.164 format

**Example:**

```php
add_filter( 'ordercourier_resolve_agent', function( $agent_phone, $context ) {
	// Route US customers to a specific number
	if ( isset( $context['country'] ) && 'US' === $context['country'] ) {
		return '+1234567890';
	}
	return $agent_phone;
}, 10, 2 );
```

---

### `ordercourier_agent_online_status`

**Type:** Filter  
**Since:** 1.0.0

Determine if an agent is currently online based on office hours or other criteria.

**Parameters:**
- `$is_online` (bool) - Default online status (always true in FREE)
- `$agent_phone` (string) - Agent phone number
- `$context` (array) - Same context as `ordercourier_resolve_agent`

**Return:** `bool` - True if online, false if offline

**Example:**

```php
add_filter( 'ordercourier_agent_online_status', function( $is_online, $agent_phone, $context ) {
	// Check custom office hours logic
	$current_hour = (int) current_time( 'H' );
	if ( $current_hour < 9 || $current_hour >= 17 ) {
		return false;
	}
	return $is_online;
}, 10, 3 );
```

---

## Message Building

### `ordercourier_message_parts`

**Type:** Filter  
**Since:** 1.0.0

Modify or add to the message parts before they are assembled into the final message.

**Parameters:**
- `$parts` (array) - Associative array of message parts:
  - `greeting` (string) - Greeting text
  - `items` (string) - Formatted items list
  - `subtotal` (string) - Subtotal line
  - `coupons` (string) - Applied coupons
  - `fees` (string) - Fees
  - `shipping` (string) - Shipping info
  - `tax` (string) - Tax amount
  - `total` (string) - Total amount
  - `customer` (string) - Customer info
  - `note` (string) - Customer note
  - `footer` (string) - Footer text
- `$type` (string) - Message type: 'product', 'cart', 'order', 'quote'
- `$data` (object) - WC_Product, WC_Cart, or WC_Order object

**Return:** `array` - Modified message parts

**Example:**

```php
add_filter( 'ordercourier_message_parts', function( $parts, $type, $data ) {
	if ( 'order' === $type ) {
		// Add custom tracking info
		$parts['tracking'] = 'Tracking: ' . $data->get_meta( '_tracking_number' );
	}
	return $parts;
}, 10, 3 );
```

---

### `ordercourier_message_placeholders`

**Type:** Filter  
**Since:** 1.0.0

Add custom placeholders that can be used in message templates.

**Parameters:**
- `$placeholders` (array) - Key => value pairs of placeholders
- `$type` (string) - Message type
- `$data` (object) - Context data object

**Return:** `array` - Modified placeholders

**Example:**

```php
add_filter( 'ordercourier_message_placeholders', function( $placeholders, $type, $data ) {
	if ( 'order' === $type && $data instanceof WC_Order ) {
		$placeholders['{estimated_delivery}'] = $data->get_meta( '_delivery_date' );
	}
	return $placeholders;
}, 10, 3 );
```

---

### `ordercourier_settings_fields`

**Type:** Filter  
**Since:** 1.0.0

Filters the settings fields shown in an OrderCourier settings section. Add-ons can use this to add their own settings fields.

**Parameters:**
- `$settings` (array) - Array of settings fields for the current section
- `$current_section` (string) - Current settings section ID ('', 'templates', 'buttons', 'widget', 'advanced')

**Return:** `array` - Modified settings fields array

**Example:**

```php
add_filter( 'ordercourier_settings_fields', function( $settings, $section ) {
	if ( 'buttons' !== $section ) {
		return $settings;
	}
	
	$field = array(
		'title'   => __( 'My Custom Setting', 'my-addon' ),
		'type'    => 'checkbox',
		'default' => 'yes',
		'desc'    => __( 'Enable my custom feature.', 'my-addon' ),
		'id'      => 'ordercourier_settings[my_custom_setting]',
	);
	
	// Insert before the sectionend.
	array_splice( $settings, count( $settings ) - 1, 0, array( $field ) );
	
	return $settings;
}, 10, 2 );
```

---

### `ordercourier_message_template`

**Type:** Filter  
**Since:** 1.0.0

Modify the template used for a specific message type.

**Parameters:**
- `$template` (string) - Template string with placeholders
- `$type` (string) - Message type
- `$context` (array) - Additional context

**Return:** `string` - Modified template

**Example:**

```php
add_filter( 'ordercourier_message_template', function( $template, $type, $context ) {
	if ( 'quote' === $type ) {
		// Use custom quote template
		$template = "Custom quote request:\n{items}\n\nReply with pricing please.";
	}
	return $template;
}, 10, 3 );
```

---

## Settings & UI

### `ordercourier_settings_tabs`

**Type:** Filter  
**Since:** 1.0.0

Add custom settings tabs to the OrderCourier settings page.

**Parameters:**
- `$tabs` (array) - Array of tab arrays with keys:
  - `id` (string) - Tab ID
  - `label` (string) - Tab label
  - `callback` (callable) - Function to render tab content

**Return:** `array` - Modified tabs array

**Example:**

```php
add_filter( 'ordercourier_settings_tabs', function( $tabs ) {
	$tabs[] = array(
		'id'       => 'pro_features',
		'label'    => __( 'PRO Features', 'ordercourier-pro' ),
		'callback' => 'my_pro_settings_callback',
	);
	return $tabs;
} );
```

---

### `ordercourier_settings_sections`

**Type:** Filter  
**Since:** 1.0.0

Add settings sections to existing tabs.

**Parameters:**
- `$sections` (array) - Array of section definitions
- `$current_tab` (string) - Current tab ID

**Return:** `array` - Modified sections

---

### `ordercourier_get_settings_{$tab_id}`

**Type:** Filter  
**Since:** 1.0.0

Add settings fields to a specific tab. Replace `{$tab_id}` with the actual tab ID.

**Parameters:**
- `$settings` (array) - Array of WooCommerce-style setting definitions
- `$current_section` (string) - Current section ID

**Return:** `array` - Modified settings array

**Example:**

```php
add_filter( 'ordercourier_get_settings_general', function( $settings ) {
	$settings[] = array(
		'id'      => 'ordercourier_pro_feature',
		'title'   => __( 'PRO Feature', 'ordercourier-pro' ),
		'type'    => 'checkbox',
		'default' => 'no',
	);
	return $settings;
} );
```

---

### `ordercourier_admin_notice_dismissed`

**Type:** Action  
**Since:** 1.0.0

Fires when an admin notice is dismissed.

**Parameters:**
- `$notice_id` (string) - ID of the dismissed notice

**Example:**

```php
add_action( 'ordercourier_admin_notice_dismissed', function( $notice_id ) {
	if ( 'pro_upgrade' === $notice_id ) {
		// Handle PRO upgrade notice dismissal
	}
} );
```

---

## Order Processing

### `ordercourier_before_create_order`

**Type:** Action  
**Since:** 1.0.0

Fires before an order is created from the cart.

**Parameters:**
- `$cart` (WC_Cart) - Cart object
- `$data` (array) - Order data (billing, shipping, note, etc.)

**Example:**

```php
add_action( 'ordercourier_before_create_order', function( $cart, $data ) {
	// Log or validate before order creation
	do_action( 'custom_order_validation', $cart );
}, 10, 2 );
```

---

### `ordercourier_after_create_order`

**Type:** Action  
**Since:** 1.0.0

Fires after an order is successfully created.

**Parameters:**
- `$order_id` (int) - Created order ID
- `$cart` (WC_Cart) - Cart object used
- `$data` (array) - Order data that was used

**Example:**

```php
add_action( 'ordercourier_after_create_order', function( $order_id, $cart, $data ) {
	$order = wc_get_order( $order_id );
	$order->add_order_note( 'Created via chat integration' );
}, 10, 3 );
```

---

### `ordercourier_order_status_for_new_order`

**Type:** Filter  
**Since:** 1.0.0

Modify the status assigned to newly created orders.

**Parameters:**
- `$status` (string) - Default status (from settings)
- `$cart` (WC_Cart) - Cart object
- `$data` (array) - Order data

**Return:** `string` - Order status without 'wc-' prefix

**Example:**

```php
add_filter( 'ordercourier_order_status_for_new_order', function( $status, $cart, $data ) {
	// Set custom status for large orders
	if ( $cart->get_total( 'raw' ) > 1000 ) {
		return 'pending-approval';
	}
	return $status;
}, 10, 3 );
```

---

## Events & Reports

### `ordercourier_event_data`

**Type:** Filter  
**Since:** 1.0.0

Modify event data before it's inserted into the database.

**Parameters:**
- `$insert_data` (array) - Data to be inserted into wp_oc_events table
  - `created_gmt` (string) - Event timestamp in GMT
  - `event` (string) - Event type
  - `source` (string) - Source context
  - `product_id` (int|null) - Product ID
  - `order_id` (int|null) - Order ID
  - `agent_id` (int|null) - Agent ID
  - `session_hash` (string) - Session identifier
- `$event_type` (string) - Event type
- `$data` (array) - Original event data passed to Events::log()

**Return:** `array` - Modified insert data

**Example:**

```php
add_filter( 'ordercourier_event_data', function( $insert_data, $event_type, $data ) {
	// Add agent_id from order meta when logging order events
	if ( 'order_created' === $event_type && ! empty( $data['order_id'] ) ) {
		$order = wc_get_order( $data['order_id'] );
		if ( $order ) {
			$agent_id = $order->get_meta( '_oc_agent_id' );
			if ( $agent_id ) {
				$insert_data['agent_id'] = $agent_id;
			}
		}
	}
	return $insert_data;
}, 10, 3 );
```

---

### `ordercourier_log_event`

**Type:** Action  
**Since:** 1.0.0

Fires when an event is logged to the events table.

**Parameters:**
- `$event_type` (string) - Event type (click, order_created, redirected)
- `$data` (array) - Event data
  - `source` (string) - Source context
  - `product_id` (int|null)
  - `order_id` (int|null)
  - `agent_id` (int|null)

**Example:**

```php
add_action( 'ordercourier_log_event', function( $event_type, $data ) {
	if ( 'order_created' === $event_type ) {
		// Send notification or track in external analytics
		do_action( 'custom_analytics_track', 'chat_order', $data );
	}
}, 10, 2 );
```

---

### `ordercourier_reports_data`

**Type:** Filter  
**Since:** 1.0.0

Modify or enhance the reports data before display.

**Parameters:**
- `$reports_data` (array) - Array of report rows
- `$filters` (array) - Active filters (from, to, source, etc.)

**Return:** `array` - Modified reports data

**Example:**

```php
add_filter( 'ordercourier_reports_data', function( $reports_data, $filters ) {
	// Add conversion rate calculation
	foreach ( $reports_data as &$row ) {
		$row['conversion_rate'] = $row['orders'] / max( $row['clicks'], 1 ) * 100;
	}
	return $reports_data;
}, 10, 2 );
```

---

### `ordercourier_reports_columns`

**Type:** Filter  
**Since:** 1.0.0

Add or modify columns in the reports table.

**Parameters:**
- `$columns` (array) - Column definitions (id => label)

**Return:** `array` - Modified columns

**Example:**

```php
add_filter( 'ordercourier_reports_columns', function( $columns ) {
	$columns['agent'] = __( 'Agent', 'ordercourier-pro' );
	$columns['conversion'] = __( 'Conversion %', 'ordercourier-pro' );
	return $columns;
} );
```

---

## Admin Pages

### `ordercourier_admin_menu`

**Type:** Action  
**Since:** 1.0.0

Fires when admin menu items are being registered. Use this to add submenu pages.

**Example:**

```php
add_action( 'ordercourier_admin_menu', function() {
	add_submenu_page(
		'woocommerce',
		__( 'Advanced Reports', 'ordercourier-pro' ),
		__( 'Advanced Reports', 'ordercourier-pro' ),
		'manage_woocommerce',
		'ordercourier-pro-reports',
		'my_advanced_reports_callback'
	);
} );
```

---

### `ordercourier_admin_enqueue_scripts`

**Type:** Action  
**Since:** 1.0.0

Fires when admin scripts are enqueued for OrderCourier pages.

**Parameters:**
- `$hook` (string) - Current admin page hook

**Example:**

```php
add_action( 'ordercourier_admin_enqueue_scripts', function( $hook ) {
	if ( 'woocommerce_page_ordercourier-reports' === $hook ) {
		wp_enqueue_script( 'ordercourier-pro-charts', plugins_url( 'assets/charts.js', __FILE__ ), array( 'jquery' ), '1.0.0', true );
	}
} );
```

---

### `ordercourier_product_meta_fields`

**Type:** Filter  
**Since:** 1.0.0

Add custom fields to the product edit meta box.

**Parameters:**
- `$fields` (array) - Array of field definitions

**Return:** `array` - Modified fields array

**Example:**

```php
add_filter( 'ordercourier_product_meta_fields', function( $fields ) {
	$fields[] = array(
		'id'    => '_oc_priority_agent',
		'label' => __( 'Priority Agent', 'ordercourier-pro' ),
		'type'  => 'select',
		'options' => array( /* agent options */ ),
	);
	return $fields;
} );
```

---

## Widget & Frontend

### `ordercourier_widget_data`

**Type:** Filter  
**Since:** 1.0.0

Modify data passed to the frontend widget.

**Parameters:**
- `$widget_data` (array) - Widget configuration
  - `enabled` (bool)
  - `position` (string)
  - `greeting` (string)
  - `agents` (array) - Available agents with online status
  - `buttonColor` (string)
  - `buttonTextColor` (string)

**Return:** `array` - Modified widget data

**Example:**

```php
add_filter( 'ordercourier_widget_data', function( $widget_data ) {
	// Add custom greeting based on time of day
	$hour = (int) current_time( 'H' );
	if ( $hour < 12 ) {
		$widget_data['greeting'] = __( 'Good morning! How can we help?', 'ordercourier-pro' );
	}
	return $widget_data;
} );
```

---

### `ordercourier_button_attributes`

**Type:** Filter  
**Since:** 1.0.0

Modify HTML attributes for chat buttons.

**Parameters:**
- `$attributes` (array) - HTML attributes (class, style, data-*, etc.)
- `$context` (string) - Button context (product, cart, checkout, widget)
- `$product_id` (int|null) - Product ID if applicable

**Return:** `array` - Modified attributes

**Example:**

```php
add_filter( 'ordercourier_button_attributes', function( $attributes, $context ) {
	if ( 'product' === $context ) {
		$attributes['data-track'] = 'product-chat-click';
	}
	return $attributes;
}, 10, 2 );
```

---

## Privacy & Data

### `ordercourier_privacy_exporters`

**Type:** Filter  
**Since:** 1.0.0

Add custom privacy data exporters.

**Parameters:**
- `$exporters` (array) - Array of exporter definitions

**Return:** `array` - Modified exporters

---

### `ordercourier_privacy_erasers`

**Type:** Filter  
**Since:** 1.0.0

Add custom privacy data erasers.

**Parameters:**
- `$erasers` (array) - Array of eraser definitions

**Return:** `array` - Modified erasers

---

## Utility Hooks

### `ordercourier_phone_validate`

**Type:** Filter  
**Since:** 1.0.0

Validate or modify a phone number before it's saved or used.

**Parameters:**
- `$phone` (string) - Phone number to validate
- `$context` (string) - Where the validation is happening

**Return:** `string` - Validated/sanitized phone number

---

### `ordercourier_rate_limit_check`

**Type:** Filter  
**Since:** 1.0.0

Override rate limiting logic.

**Parameters:**
- `$is_limited` (bool) - Whether the current request should be rate limited
- `$identifier` (string) - Rate limit identifier (IP/session)
- `$action` (string) - Action being rate limited

**Return:** `bool` - True to rate limit, false to allow

---

## Notes for PRO Development

### Best Practices

1. **Always check if FREE is active:**
   ```php
   if ( ! class_exists( 'Prologic365\OrderCourier\Plugin' ) ) {
       return; // FREE not active
   }
   ```

2. **Use high priority for essential hooks:**
   - Use priority 5 or lower for critical functionality
   - Use priority 10 (default) for standard extensions
   - Use priority 20+ for enhancements

3. **Namespace your data:**
   - Prefix custom meta keys: `_oc_pro_feature_name`
   - Prefix options: `ordercourier_pro_setting_name`
   - Use unique event names in `ordercourier_log_event`

4. **Handle missing FREE gracefully:**
   - PRO should deactivate itself with an admin notice if FREE is not installed
   - Don't assume FREE functions/classes exist without checking

5. **Data compatibility:**
   - When a setting moves from FREE to PRO, read the old key first for BC
   - Store PRO-exclusive data separately, don't overwrite FREE data

### Deprecated Hooks

None yet. Starting from version 1.0.0, deprecated hooks will be listed here with their replacement and removal version.

---

**Last Updated:** October 30, 2026  
**Version:** 1.0.0
