Автоматизація обробки замовлень через Webhook-ланцюжки
Webhook-автоматизація перетворює ручні операції з замовленнями в event-driven ланцюжки: замовлення створено → сповіщення складу → оновлення CRM → відправка покупцю трекінгу. Кожен крок незалежний, відмовостійкий та логується.
Архітектура событійних ланцюжків
Магазин (событие) → Webhook → Queue → Handler → Зовнішні системи
↓
Dead Letter Queue (при ошибках)
Ключевий принцип: webhook приймається миттєво, обробляється асинхронно. Endpoint повертає 200 OK за мілісекунди, реальна робота — в черзі.
Laravel: приймання та постановка в чергу
// routes/api.php
Route::post('/webhooks/orders', [OrderWebhookController::class, 'handle'])
->middleware('webhook.signature');
// app/Http/Controllers/OrderWebhookController.php
class OrderWebhookController extends Controller
{
public function handle(Request $request): JsonResponse
{
// Миттєво в чергу — відповідь < 50ms
ProcessOrderWebhook::dispatch(
$request->input('event'),
$request->all()
)->onQueue('webhooks');
return response()->json(['status' => 'queued'], 200);
}
}
Перевірка підпису webhook
// app/Http/Middleware/VerifyWebhookSignature.php
class VerifyWebhookSignature
{
public function handle(Request $request, Closure $next): mixed
{
$signature = $request->header('X-Webhook-Signature');
$payload = $request->getContent();
$secret = config('webhooks.secret');
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $signature ?? '')) {
return response()->json(['error' => 'Invalid signature'], 403);
}
return $next($request);
}
}
Основний обробник з ланцюжком дій
// app/Jobs/ProcessOrderWebhook.php
class ProcessOrderWebhook implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $backoff = 60; // секунд між спробами
public function __construct(
private string $event,
private array $payload
) {}
public function handle(OrderWebhookPipeline $pipeline): void
{
match ($this->event) {
'order.created' => $pipeline->onOrderCreated($this->payload),
'order.paid' => $pipeline->onOrderPaid($this->payload),
'order.shipped' => $pipeline->onOrderShipped($this->payload),
'order.cancelled' => $pipeline->onOrderCancelled($this->payload),
default => Log::warning("Unknown webhook event: {$this->event}"),
};
}
public function failed(\Throwable $e): void
{
Log::error("Webhook processing failed", [
'event' => $this->event,
'error' => $e->getMessage(),
'payload' => $this->payload,
]);
// Сповіщення в Slack/Telegram при остаточному провалі
Notification::route('slack', config('webhooks.slack_url'))
->notify(new WebhookFailedNotification($this->event, $e));
}
}
Pipeline обробки замовлення
// app/Services/OrderWebhookPipeline.php
class OrderWebhookPipeline
{
public function onOrderCreated(array $payload): void
{
$order = $this->findOrCreateOrder($payload);
// Паралельний запуск незалежних задач
Bus::batch([
new SyncOrderToCrm($order),
new NotifyWarehouse($order),
new SendOrderConfirmation($order),
new UpdateInventoryReservation($order),
])->allowFailures()->dispatch();
}
public function onOrderPaid(array $payload): void
{
$order = Order::findByExternalId($payload['order']['id']);
// Послідовна ланцюжок: кожен крок залежит від попереднього
Bus::chain([
new MarkOrderAsPaid($order),
new GenerateInvoice($order),
new TriggerFulfillment($order),
new SendPaymentConfirmation($order),
])->dispatch();
}
public function onOrderShipped(array $payload): void
{
$order = Order::findByExternalId($payload['order']['id']);
$tracking = $payload['shipment']['tracking_number'] ?? null;
Bus::chain([
new UpdateOrderTracking($order, $tracking),
new SendShippingNotification($order, $tracking),
new UpdateCrmOrderStatus($order, 'shipped'),
])->dispatch();
}
}
Синхронізація з 1С/CRM
// app/Jobs/SyncOrderToCrm.php
class SyncOrderToCrm implements ShouldQueue
{
use Queueable;
public int $tries = 5;
public array $backoff = [30, 60, 120, 300, 600]; // Експоненціальний backoff
public function __construct(private Order $order) {}
public function handle(AmoCrmClient $crm): void
{
$lead = $crm->leads()->create([
'name' => "Замовлення #{$this->order->increment_id}",
'price' => $this->order->grand_total,
'status_id' => config('amocrm.new_order_status'),
'custom_fields_values' => [
['field_code' => 'ORDER_ID', 'values' => [['value' => $this->order->id]]],
['field_code' => 'CUSTOMER_EMAIL', 'values' => [['value' => $this->order->customer_email]]],
],
]);
$this->order->update(['crm_lead_id' => $lead['id']]);
}
}
Вихідні webhooks партнерам
Деякі інтеграції вимагають відправки webhook при зміні статусу замовлення:
// app/Listeners/OrderStatusChangedListener.php
class OrderStatusChangedListener
{
public function handle(OrderStatusChanged $event): void
{
$webhooks = WebhookSubscription::where('event', 'order.status_changed')
->where('is_active', true)
->get();
foreach ($webhooks as $webhook) {
SendOutgoingWebhook::dispatch($webhook, [
'event' => 'order.status_changed',
'order' => [
'id' => $event->order->id,
'status' => $event->order->status,
],
'timestamp' => now()->toIso8601String(),
])->onQueue('outgoing-webhooks');
}
}
}
Моніторинг та логування
Весь поток займає 2-4 тижні реалізації та 3-5 днів налаштування моніторингу.







