feat: Implement Slack, Trello, and Courier (ShipLogic) integration
- Add 14 domain events for order lifecycle (OrderCreated, OrderPacked, ShipmentCreated, ParcelDelivered, etc.)
- Create SlackNotifierService with channels for orders, design, production, shipping, ops-alerts
- Create TrelloService to create cards, move cards between lists, attach files, check items
- Create CourierService to integrate with ShipLogic API for shipment creation and document retrieval
- Create PackingController to explicitly capture packing dimensions and weight
- Create ShippingController with multi-layer guards: packing validation, payment/approval verification, idempotency
- Create TrelloWebhookController to handle incoming Trello webhooks as intent signals
- Create CourierWebhookController to handle Shiplogic status updates
- Create event listeners for Slack notifications and Trello card updates
- Create EventServiceProvider to register all events and listeners
- Add database migration for packing, courier, and Trello data columns
- Create config files for slack, trello, and courier integration
- Update .env with integration secrets placeholders
- Add routes for /orders/{id}/pack, /orders/{id}/ship, /api/webhooks/trello, /api/webhooks/courier
Key architectural decisions:
- Packing is explicit ops action (not automatic from status)
- Shipment creation only after: Trello intent + packing confirmed + payment/approval rules met
- Courier API failures keep order in Ready to Ship state (safe retry)
- Trello and Slack are mirrors of backend state, not decision makers
- All side effects flow through event listeners, maintaining separation of concerns
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class SlackNotifierService
|
||||
{
|
||||
protected array $webhooks = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->webhooks = [
|
||||
'orders' => config('slack.webhooks.orders'),
|
||||
'design' => config('slack.webhooks.design'),
|
||||
'production' => config('slack.webhooks.production'),
|
||||
'shipping' => config('slack.webhooks.shipping'),
|
||||
'ops_alerts' => config('slack.webhooks.ops_alerts'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to #orders channel
|
||||
*/
|
||||
public function orders(string $message, array $blocks = []): void
|
||||
{
|
||||
$this->send($this->webhooks['orders'], $message, $blocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to #design channel
|
||||
*/
|
||||
public function design(string $message, array $blocks = []): void
|
||||
{
|
||||
$this->send($this->webhooks['design'], $message, $blocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to #production channel
|
||||
*/
|
||||
public function production(string $message, array $blocks = []): void
|
||||
{
|
||||
$this->send($this->webhooks['production'], $message, $blocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to #shipping channel
|
||||
*/
|
||||
public function shipping(string $message, array $blocks = []): void
|
||||
{
|
||||
$this->send($this->webhooks['shipping'], $message, $blocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to #ops-alerts channel
|
||||
*/
|
||||
public function opsAlerts(string $message, array $blocks = []): void
|
||||
{
|
||||
$this->send($this->webhooks['ops_alerts'], $message, $blocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send payload to Slack webhook
|
||||
*/
|
||||
protected function send(string $webhook, string $message, array $blocks = []): void
|
||||
{
|
||||
if (! $webhook) {
|
||||
Log::warning('Slack webhook not configured for channel', ['message' => $message]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$payload = ['text' => $message];
|
||||
|
||||
if (! empty($blocks)) {
|
||||
$payload['blocks'] = $blocks;
|
||||
}
|
||||
|
||||
Http::post($webhook, $payload);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Failed to send Slack notification', [
|
||||
'webhook' => substr($webhook, 0, 20).'...',
|
||||
'message' => $message,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user