Skip to content

Order Webhooks (External Notifications)

Flow ID: IN-20 | Module(s): ecommercen/libraries/internal/ | Complexity: Medium

Business Overview

Ecommercen provides an outbound webhook system that notifies external systems (typically ERP or warehouse management systems) when specific order lifecycle events occur. Three webhook types are supported:

  1. ERP Ready -- Fired when an order reaches a status that makes it ready for ERP processing (e.g., payment confirmed, order approved).
  2. Cancel -- Fired when an order is canceled.
  3. Return -- Fired when an order is returned.

The webhooks operate as fire-and-forget GET requests to configurable external endpoints. Each request includes the order ID in the URL path and is authenticated via a Bearer token. The system is designed for integration with external APIs that need real-time order state notifications.

API Reference

Webhook Types

HookClassTraitConfig KeyTrigger
ERP ReadyAdvInternalApiOrderForErpHookOrderForErpHookFireTraiterpReadyOrder reaches ERP-ready status
CancelAdvInternalApiOrderCancelHookOrderCancelHookFireTraitcancelOrder is canceled
ReturnAdvInternalApiOrderReturnHookOrderReturnHookFireTraitreturnOrder is returned

Outbound Request Format

GET {configured_url}/{orderId}
Headers:
  Authorization: Bearer {apiToken}
  Accept: application/json
  Content-Type: application/json
Options:
  connect_timeout: {clientConnectTimeout}
  timeout: {clientConnectTimeout}

Interface Contract

php
interface InternalApiOrderHookInterface
{
    public function fireHook($orderId);
    public function getUrl($orderId): string;
}

Code Flow

Hook Firing Sequence

Order state change occurs (e.g., order confirmed, canceled, returned)
  -> Controller or model uses trait method:
     e.g., $this->internalApiOrderForErpHook($orderId)
  -> Trait creates hook instance:
     (new InternalApiOrderForErpHook())->fireHook($orderId)
  -> FireHookTrait::fireHook($orderId):
     1. isApiEnabled() -- check config_item('internalApi')['useApi']
        -> If false, return immediately (no-op)
     2. getUrl($orderId) -- build URL from config
        -> Read config_item('internalApi')['apiOrderWebHooks']['{type}']
        -> If empty, return '' (no-op)
        -> Append /{orderId} to base URL
     3. call($url, $orderId) -- execute GET request
        -> Create Guzzle client with Bearer token auth
        -> GET request with connect_timeout and timeout
        -> On exception: log error, continue (fire-and-forget)

ERP Ready Hook Usage

Checkout completion / Payment webhook / Admin approval
  -> Adv_checkout::internalApiOrderForErpHook($orderId)
     or Adv_orders_admin::internalApiOrderForErpHook($orderId)
     or AdvSmartCart::internalApiOrderForErpHook($orderId)
     or AdvJcc::internalApiOrderForErpHook($orderId)
     or Cronjob::internalApiOrderForErpHook($orderId)
  -> AdvInternalApiOrderForErpHook->fireHook($orderId)
     -> GET {INTERNAL_API_HOOK_ERP_READY}/{orderId}

Cancel Hook Usage

Admin cancels order / Incomplete order cleanup job
  -> Adv_orders_admin::internalApiOrderCancelHook($orderId)
     or Adv_order_model::internalApiOrderCancelHook($orderId)
     or AdvCancelIncompleteOrders::internalApiOrderCancelHook($orderId)
     or Cronjob::internalApiOrderCancelHook($orderId)
  -> AdvInternalApiOrderCancelHook->fireHook($orderId)
     -> GET {INTERNAL_API_HOOK_CANCEL}/{orderId}

Return Hook Usage

Admin marks order as returned
  -> Controller with OrderReturnHookFireTrait
  -> AdvInternalApiOrderReturnHook->fireHook($orderId)
     -> GET {INTERNAL_API_HOOK_RETURN}/{orderId}

Architecture

ecommercen/libraries/internal/
  InternalApiOrderHookInterface.php    # Contract: fireHook(), getUrl()
  FireHookTrait.php                    # Core logic: enabled check, URL build, call
  IsApiEnabledTrait.php                # Checks config_item('internalApi')['useApi']
  HookGetClientOptionsTrait.php        # Builds Guzzle client with Bearer auth

  AdvInternalApiOrderForErpHook.php    # ERP-ready webhook implementation
  AdvInternalApiOrderCancelHook.php    # Cancel webhook implementation
  AdvInternalApiOrderReturnHook.php    # Return webhook implementation

  OrderForErpHookFireTrait.php         # Convenience trait for controllers/models
  OrderCancelHookFireTrait.php         # Convenience trait for controllers/models
  OrderReturnHookFireTrait.php         # Convenience trait for controllers/models

application/libraries/internal/
  InternalApiOrderForErpHook.php       # Client-overridable (extends Adv* version)
  InternalApiOrderCancelHook.php       # Client-overridable (extends Adv* version)
  InternalApiOrderReturnHook.php       # Client-overridable (extends Adv* version)

Trait Composition

Each hook implementation uses two traits:

  • FireHookTrait -- Provides the fireHook() method (enabled check + URL resolution + call)
  • HookGetClientOptionsTrait -- Provides getClient() (Guzzle with auth headers) and defaultRequestOptions() (timeouts)

The FireHookTrait internally uses IsApiEnabledTrait for the feature toggle check.

Consumer Traits

Controllers and models that need to fire hooks use the convenience traits:

TraitMethodUsed By
OrderForErpHookFireTraitinternalApiOrderForErpHook($id)Adv_checkout, Adv_orders_admin, Adv_shopflix_orders_admin, Adv_public_orders_admin, Adv_skroutz_orders_admin, AdvSmartCart, AdvJcc, AdvCancelIncompleteOrders, Cronjob
OrderCancelHookFireTraitinternalApiOrderCancelHook($id)Adv_orders_admin, Adv_order_model, AdvCancelIncompleteOrders, Cronjob
OrderReturnHookFireTraitinternalApiOrderReturnHook($id)Controllers handling order returns

Data Model

No Dedicated Tables

Order webhooks do not store state in the database. They are fire-and-forget: the request is made and any failure is logged but not retried. The order itself is not modified by the webhook system.

Order Status Context

The webhooks fire in response to order status transitions managed by the existing order model:

WebhookTypical Order StatusContext
ERP ReadyCOMPLETED, CONFIRMED, etc.After payment confirmation or admin approval
CancelCANCELEDAdmin action or auto-cancel job
ReturnRETURNAdmin action

Configuration

Environment Variables (.env)

VariableDescriptionDefault
INTERNAL_API_USEMaster toggle for webhook systemfalse
INTERNAL_API_TOKENBearer token for authentication--
INTERNAL_API_HOOK_ERP_READYBase URL for ERP-ready webhook--
INTERNAL_API_HOOK_CANCELBase URL for cancel webhook--
INTERNAL_API_HOOK_RETURNBase URL for return webhook--
INTERNAL_API_CONNECT_TIMEOUTGuzzle connect timeout (seconds)3
INTERNAL_API_TIMEOUTGuzzle request timeout (seconds)3

Config File (application/config/api.php)

php
$config['internalApi'] = [
    'useApi' => env('INTERNAL_API_USE', false),
    'apiToken' => env('INTERNAL_API_TOKEN'),
    'apiOrderWebHooks' => [
        'erpReady' => env('INTERNAL_API_HOOK_ERP_READY'),
        'cancel' => env('INTERNAL_API_HOOK_CANCEL'),
        'return' => env('INTERNAL_API_HOOK_RETURN')
    ],
    'clientConnectTimeout' => env('INTERNAL_API_CONNECT_TIMEOUT', 3),
    'clientTimeout' => env('INTERNAL_API_TIMEOUT', 3),
];

Selective Activation

Each webhook type can be independently enabled or disabled:

  • Set INTERNAL_API_USE=true to enable the system globally.
  • Leave any INTERNAL_API_HOOK_* variable empty to disable that specific webhook type.
  • The getUrl() method returns empty string for unconfigured hooks, causing fireHook() to no-op.

Client Extension Points

Override Hook Classes

Client repos provide application/libraries/internal/InternalApiOrderForErpHook.php (etc.) which extend the Adv* versions. Override these to:

  • Change the HTTP method (e.g., POST instead of GET)
  • Add custom request headers or body
  • Add retry logic
  • Modify URL construction
  • Add response processing

Override Traits

Replace the convenience traits in application/libraries/internal/ if the hook instantiation pattern needs to change (e.g., injecting dependencies, using a different hook class).

Custom Webhooks

To add a new webhook type (e.g., order update, partial refund):

  1. Create a new Adv*Hook class implementing InternalApiOrderHookInterface
  2. Use FireHookTrait and HookGetClientOptionsTrait
  3. Add the URL config key in application/config/api.php
  4. Create a convenience trait for consumer classes
  5. Add the trait to relevant controllers/models

Business Rules

  1. Master toggle: The entire webhook system is disabled when INTERNAL_API_USE is false. No network calls are made.
  2. Fire-and-forget: Webhook failures are logged but do not affect order processing. The order state change completes regardless of webhook success or failure.
  3. No retries: Failed webhooks are not retried. External systems must implement their own reconciliation if they miss a notification.
  4. GET method: All webhooks use HTTP GET with the order ID in the URL path. This design assumes the receiving system uses the order ID to fetch order details from Ecommercen's API.
  5. Bearer token auth: All webhook requests include an Authorization: Bearer header with the configured API token. The receiving system should validate this token.
  6. Timeout protection: Both connect and request timeouts are configured (default 3 seconds each) to prevent webhook calls from blocking order processing.
  7. Idempotency: The same webhook may fire multiple times for the same order (e.g., if the status transition is triggered by multiple code paths). External systems should handle duplicate notifications idempotently.
  8. Marketplace integration: The ERP Ready hook is used by marketplace order controllers (Skroutz, Shopflix, SmartCart) ensuring external ERPs are notified regardless of the order source.