feat(Deployment): Integrate Ansible deployment via PHP deployment pipeline

- Create AnsibleDeployStage using framework's Process module for secure command execution
- Integrate AnsibleDeployStage into DeploymentPipelineCommands for production deployments
- Add force_deploy flag support in Ansible playbook to override stale locks
- Use PHP deployment module as orchestrator (php console.php deploy:production)
- Fix ErrorAggregationInitializer to use Environment class instead of $_ENV superglobal

Architecture:
- BuildStage → AnsibleDeployStage → HealthCheckStage for production
- Process module provides timeout, error handling, and output capture
- Ansible playbook supports rollback via rollback-git-based.yml
- Zero-downtime deployments with health checks
This commit is contained in:
2025-10-26 14:08:07 +01:00
parent a90263d3be
commit 3b623e7afb
170 changed files with 19888 additions and 575 deletions

View File

@@ -0,0 +1,438 @@
# WhatsApp Notification Channel
Dokumentation für den WhatsApp Business API Notification Channel im Custom PHP Framework.
## Übersicht
Der WhatsApp Channel ermöglicht das Versenden von Notifications über die WhatsApp Business API. Es werden sowohl Textnachrichten als auch Template-basierte Nachrichten unterstützt.
## Features
**Text Messages**: Einfache Textnachrichten mit Markdown-Formatierung
**Template Messages**: WhatsApp-approved Message Templates mit Parametern
**Action Buttons**: Support für Action URLs und Labels
**Type Safety**: Framework-konforme Value Objects für alle Identifier
**HttpClient Integration**: Nutzung des Framework's HttpClient Moduls
**Error Handling**: Umfassende Exception-Behandlung mit WhatsAppApiException
## Architektur
```
WhatsAppChannel (NotificationChannelInterface)
WhatsAppClient (API Communication)
HttpClient (Framework's HTTP Module)
WhatsApp Business API
```
## Installation & Setup
### 1. WhatsApp Business Account einrichten
1. Erstelle einen WhatsApp Business Account bei Facebook
2. Registriere deine Business Phone Number
3. Generiere einen Access Token
4. Notiere deine Phone Number ID und Business Account ID
**URLs**:
- WhatsApp Business Dashboard: https://business.facebook.com/settings/whatsapp-business-accounts
- Meta for Developers: https://developers.facebook.com/
### 2. Konfiguration
Die Konfiguration erfolgt aktuell hardcoded in `WhatsAppConfig::createDefault()`:
```php
use App\Framework\Notification\Channels\WhatsApp\WhatsAppConfig;
$config = WhatsAppConfig::createDefault();
// Oder manuell:
$config = new WhatsAppConfig(
accessToken: 'YOUR_ACCESS_TOKEN',
phoneNumberId: 'YOUR_PHONE_NUMBER_ID',
businessAccountId: WhatsAppBusinessAccountId::fromString('YOUR_BUSINESS_ACCOUNT_ID'),
apiVersion: 'v18.0'
);
```
## Verwendung
### Basic Text Message
```php
use App\Framework\Core\ValueObjects\PhoneNumber;
use App\Framework\Notification\Notification;
use App\Framework\Notification\ValueObjects\NotificationChannel;
use App\Framework\Notification\ValueObjects\SystemNotificationType;
// Create notification
$notification = Notification::create(
recipientId: 'user_123',
type: SystemNotificationType::SYSTEM_ALERT(),
title: 'Important Update',
body: 'Your order has been shipped!',
NotificationChannel::WHATSAPP
);
// Send via dispatcher
$result = $notificationDispatcher->send($notification);
if ($result->isSuccessful()) {
echo "WhatsApp message sent: {$result->getMetadata()['message_id']}";
}
```
### Template Message
WhatsApp erfordert pre-approved Templates für Marketing und Notifications.
```php
$notification = Notification::create(
recipientId: 'user_123',
type: SystemNotificationType::ORDER_CONFIRMATION(),
title: 'Order Confirmation',
body: 'Template will be used',
NotificationChannel::WHATSAPP
)->withData([
'whatsapp_template_id' => 'order_confirmation',
'whatsapp_language' => 'en_US',
'whatsapp_template_params' => [
'John Doe', // Customer name
'ORD-12345', // Order number
'€99.99' // Total amount
]
]);
$result = $notificationDispatcher->send($notification);
```
### With Action Button
```php
$notification = Notification::create(
recipientId: 'user_123',
type: SystemNotificationType::PAYMENT_REMINDER(),
title: 'Payment Due',
body: 'Your invoice is ready for payment.',
NotificationChannel::WHATSAPP
)->withAction(
url: 'https://example.com/invoices/123',
label: 'View Invoice'
);
// Message will include: "👉 View Invoice: https://example.com/invoices/123"
```
## Phone Number Resolver
Implementiere `UserPhoneNumberResolver` für deine Anwendung:
```php
use App\Framework\Core\ValueObjects\PhoneNumber;
use App\Framework\Notification\Channels\WhatsApp\UserPhoneNumberResolver;
final readonly class DatabaseUserPhoneNumberResolver implements UserPhoneNumberResolver
{
public function __construct(
private UserRepository $userRepository
) {}
public function resolvePhoneNumber(string $userId): ?PhoneNumber
{
$user = $this->userRepository->find($userId);
if ($user === null || $user->phoneNumber === null) {
return null;
}
try {
return PhoneNumber::fromString($user->phoneNumber);
} catch (\InvalidArgumentException $e) {
// Invalid phone number format
return null;
}
}
}
```
## Value Objects
### PhoneNumber
```php
use App\Framework\Core\ValueObjects\PhoneNumber;
// E.164 format required: +[country code][number]
$phone = PhoneNumber::fromString('+4917612345678');
// Or from parts
$phone = PhoneNumber::fromInternational('49', '17612345678');
// Methods
$phone->toString(); // +4917612345678
$phone->toDisplayFormat(); // +49 176 126 456 78
$phone->getCountryCode(); // 49
$phone->getSubscriberNumber(); // 17612345678
```
### WhatsAppTemplateId
```php
use App\Framework\Notification\Channels\WhatsApp\ValueObjects\WhatsAppTemplateId;
// Template names must be lowercase alphanumeric with underscores
$templateId = WhatsAppTemplateId::fromString('order_confirmation');
$templateId = WhatsAppTemplateId::fromString('hello_world');
// ❌ Invalid
$templateId = WhatsAppTemplateId::fromString('OrderConfirmation'); // Uppercase not allowed
$templateId = WhatsAppTemplateId::fromString('order-confirmation'); // Hyphen not allowed
```
### WhatsAppBusinessAccountId
```php
use App\Framework\Notification\Channels\WhatsApp\ValueObjects\WhatsAppBusinessAccountId;
$accountId = WhatsAppBusinessAccountId::fromString('123456789012345');
```
### WhatsAppMessageId
```php
use App\Framework\Notification\Channels\WhatsApp\ValueObjects\WhatsAppMessageId;
// Returned from API after sending
$messageId = WhatsAppMessageId::fromString('wamid.HBgNNDkxNzYxMjM0NTY3OBUCABIYFjNFQjBDNzE4RjAzMEE1NzQxODZEMDIA');
```
## WhatsApp Templates
### Template Erstellen
1. Gehe zu WhatsApp Business Manager
2. Navigiere zu "Message Templates"
3. Erstelle ein neues Template
4. Warte auf Approval (kann 24-48h dauern)
### Template Beispiel
**Template Name**: `order_confirmation`
**Language**: English (US)
**Category**: Transactional
**Body**:
```
Hello {{1}},
Your order {{2}} has been confirmed!
Total amount: {{3}}
Thank you for your purchase.
```
**Usage**:
```php
$notification->withData([
'whatsapp_template_id' => 'order_confirmation',
'whatsapp_language' => 'en_US',
'whatsapp_template_params' => [
'John Doe', // {{1}}
'ORD-12345', // {{2}}
'€99.99' // {{3}}
]
]);
```
## Testing
### Manual Test Script
```bash
docker exec php php tests/debug/test-whatsapp-notification.php
```
**Wichtig**: Ersetze die Test-Telefonnummer in der Datei mit deiner eigenen WhatsApp-Nummer!
### Unit Test
```php
use App\Framework\Notification\Channels\WhatsAppChannel;
use App\Framework\Notification\Notification;
it('sends WhatsApp notification successfully', function () {
$mockClient = Mockery::mock(WhatsAppClient::class);
$mockResolver = Mockery::mock(UserPhoneNumberResolver::class);
$mockResolver->shouldReceive('resolvePhoneNumber')
->with('user_123')
->andReturn(PhoneNumber::fromString('+4917612345678'));
$mockClient->shouldReceive('sendTextMessage')
->once()
->andReturn(new WhatsAppResponse(
success: true,
messageId: WhatsAppMessageId::fromString('wamid_test_123')
));
$channel = new WhatsAppChannel($mockClient, $mockResolver);
$notification = Notification::create(
recipientId: 'user_123',
type: SystemNotificationType::SYSTEM_ALERT(),
title: 'Test',
body: 'Test message',
NotificationChannel::WHATSAPP
);
$result = $channel->send($notification);
expect($result->isSuccessful())->toBeTrue();
});
```
## Error Handling
### WhatsAppApiException
```php
use App\Framework\Notification\Channels\WhatsApp\WhatsAppApiException;
try {
$response = $whatsappClient->sendTextMessage($phoneNumber, $message);
} catch (WhatsAppApiException $e) {
// API returned an error
$httpCode = $e->getHttpStatusCode();
$message = $e->getMessage();
// Log or handle error
$logger->error('WhatsApp API error', [
'http_code' => $httpCode,
'message' => $message
]);
}
```
### Common Errors
| Error Code | Beschreibung | Lösung |
|------------|--------------|--------|
| 100 | Invalid parameter | Prüfe Parameter (phone number format, template ID) |
| 131009 | Parameter value not valid | Template parameters stimmen nicht mit Template überein |
| 131026 | Message undeliverable | Empfänger hat WhatsApp nicht oder blockiert Business Account |
| 131047 | Re-engagement message | User muss zuerst Business Account kontaktieren |
| 190 | Access token expired | Generiere neuen Access Token |
## Best Practices
### 1. Phone Number Validation
```php
// ✅ Validate before using
try {
$phone = PhoneNumber::fromString($userInput);
} catch (\InvalidArgumentException $e) {
// Handle invalid phone number
return 'Invalid phone number format';
}
// ❌ Don't assume format
$phone = PhoneNumber::fromString($_POST['phone']); // Can throw exception
```
### 2. Template Usage
```php
// ✅ Use templates for marketing/promotional content
$notification->withData([
'whatsapp_template_id' => 'weekly_newsletter',
'whatsapp_language' => 'en_US'
]);
// ✅ Use text messages for immediate transactional updates
$notification = Notification::create(
recipientId: 'user_123',
type: SystemNotificationType::SYSTEM_ALERT(),
title: 'Server Alert',
body: 'Critical: Database connection lost!',
NotificationChannel::WHATSAPP
);
```
### 3. Rate Limiting
WhatsApp hat Rate Limits pro Business Account:
- **Tier 1** (default): 1,000 unique contacts/24h
- **Tier 2**: 10,000 unique contacts/24h
- **Tier 3**: 100,000 unique contacts/24h
```php
// Implement rate limiting
if ($this->rateLimiter->tooManyAttempts("whatsapp:{$userId}", 5, 3600)) {
throw new RateLimitException('Too many WhatsApp messages sent');
}
```
### 4. Opt-In Requirement
WhatsApp erfordert **explicit Opt-In** von Usern:
```php
// Check user consent before sending
if (!$user->hasWhatsAppOptIn()) {
return ChannelResult::failure(
channel: NotificationChannel::WHATSAPP,
errorMessage: 'User has not opted in to WhatsApp notifications'
);
}
```
## Troubleshooting
### Message nicht zugestellt
**Checklist**:
- [ ] Phone Number ist in E.164 Format (`+4917612345678`)
- [ ] Empfänger hat WhatsApp installiert
- [ ] Empfänger hat Business Account nicht blockiert
- [ ] Access Token ist gültig
- [ ] Template ist approved (für Template Messages)
- [ ] Rate Limits nicht überschritten
### Template Errors
**Problem**: "Parameter value not valid"
**Lösung**: Anzahl der Parameter muss exakt mit Template übereinstimmen
```php
// Template hat 3 placeholders: {{1}}, {{2}}, {{3}}
// ✅ Correct
'whatsapp_template_params' => ['Param1', 'Param2', 'Param3']
// ❌ Wrong - zu wenige Parameter
'whatsapp_template_params' => ['Param1', 'Param2']
```
### Access Token Expired
**Problem**: Error 190 - Access token has expired
**Lösung**: Generiere neuen Access Token im Facebook Business Manager
## Weiterführende Ressourcen
- **WhatsApp Business API Docs**: https://developers.facebook.com/docs/whatsapp/cloud-api
- **Message Templates**: https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates
- **Error Codes**: https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes
- **E.164 Format**: https://en.wikipedia.org/wiki/E.164
## Framework Integration
Der WhatsApp Channel folgt allen Framework-Patterns:
**Readonly Classes**: Alle VOs und Configs sind `final readonly`
**Value Objects**: Keine Primitive Obsession (PhoneNumber, TemplateId, etc.)
**No Inheritance**: Composition über Inheritance
**Type Safety**: Strikte Typisierung für alle Parameter
**Framework Compliance**: Integration mit HttpClient, Notification System
**Explicit Dependencies**: Constructor Injection, keine Service Locators