Skip to main content

Phân tích hệ thống


1. TỔNG QUAN KIẾN TRÚC

1.1. Kiến trúc hệ thống

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   SmartPost     │────▶│ Payment Service │────▶│  Payment Gates  │
│   Projects      │◀────│  (Multi-tenant) │◀────│ VNPay|MoMo|Zalo │
│ [SM,TMS,TICKET] │     └─────────────────┘     └─────────────────┘
└─────────────────┘              │                        │
        │                        ▼                        ▼
        ▼                 ┌─────────────────┐     ┌─────────────────┐
┌─────────────────┐      │   Database      │     │   External      │
│   Load Balancer │      │   (MySQL 8.0)  │     │   Services      │
│   (Nginx/HAProxy)│      └─────────────────┘     │ [Kafka/Redis]   │
└─────────────────┘              │                └─────────────────┘
                                 ▼
                         ┌─────────────────┐
                         │   Monitoring    │
                         │ [ELK + Grafana] │
                         └─────────────────┘

1.2. Tech Stack

  • Framework: Laravel 11.x
  • Architecture: Domain Driven Design (DDD) + CQRS
  • Database: MySQL 8.0 (Master-Slave for read scaling)
  • Cache: Redis Cluster
  • Queue: Redis + Laravel Horizon
  • Search: Elasticsearch 8.x
  • Monitoring: ELK Stack + Grafana + Prometheus
  • Container: Docker + Kubernetes
  • Authentication: Internal service (Network security)
  • API Documentation: OpenAPI 3.0 + Swagger UI
  • Testing: PHPUnit + Pest + Feature Tests

1.3. Security Model

  • Network Security: Service được bảo vệ bởi VPN, Firewall và Service Mesh
  • Request Validation: Validate project_code và app_code từ internal registry
  • Webhook Security: Multi-layer signature validation
  • Rate Limiting: Intelligent rate limiting với Redis
  • Data Encryption: AES-256 cho sensitive data
  • Audit Trail: Comprehensive audit logging
  • GDPR Compliance: Data retention và deletion policies

1.4. Deployment Architecture

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Production    │     │     Staging     │     │   Development   │
│                 │     │                 │     │                 │
│ - 3 App Servers │     │ - 1 App Server  │     │ - Local Docker  │
│ - 2 DB Servers  │     │ - 1 DB Server   │     │ - SQLite/MySQL  │
│ - 3 Redis Nodes │     │ - 1 Redis Node  │     │ - Redis Local   │
│ - Load Balancer │     │ - Simple LB     │     │ - No LB         │
│ - Auto Scaling  │     │ - Manual Scale  │     │ - Manual        │
└─────────────────┘     └─────────────────┘     └─────────────────┘

2. CẤU TRÚC THƯ MỤC LARAVEL + DDD

src/
├── app/
│   ├── Console/
│   │   ├── Commands/
│   │   │   ├── System/
│   │   │   │   ├── CheckSystemHealthCommand.php
│   │   │   │   ├── CleanupOldLogsCommand.php
│   │   │   │   ├── GenerateSystemReportCommand.php
│   │   │   │   └── UpdateSystemConfigCommand.php
│   │   │   ├── Transaction/
│   │   │   │   ├── ProcessFailedTransactionsCommand.php
│   │   │   │   ├── ReconcileTransactionsCommand.php
│   │   │   │   ├── RetryFailedCallbacksCommand.php
│   │   │   │   └── CleanupExpiredTransactionsCommand.php
│   │   │   ├── Webhook/
│   │   │   │   ├── ProcessFailedWebhooksCommand.php
│   │   │   │   ├── RetryWebhookDeliveryCommand.php
│   │   │   │   └── ValidateWebhookConfigCommand.php
│   │   │   └── Maintenance/
│   │   │       ├── ArchiveOldDataCommand.php
│   │   │       ├── OptimizeDatabaseCommand.php
│   │   │       └── BackupCriticalDataCommand.php
│   │   └── Kernel.php
│   │
│   ├── Domain/
│   │   ├── Project/
│   │   │   ├── Entities/
│   │   │   │   ├── Project.php
│   │   │   │   └── ProjectConfiguration.php
│   │   │   ├── ValueObjects/
│   │   │   │   ├── ProjectCode.php
│   │   │   │   ├── ProjectStatus.php
│   │   │   │   └── Currency.php
│   │   │   ├── Repositories/
│   │   │   │   └── ProjectRepositoryInterface.php
│   │   │   ├── Services/
│   │   │   │   ├── ProjectService.php
│   │   │   │   ├── ProjectConfigurationService.php
│   │   │   │   └── ProjectValidationService.php
│   │   │   └── Events/
│   │   │       ├── ProjectCreated.php
│   │   │       ├── ProjectActivated.php
│   │   │       └── ProjectDeactivated.php
│   │   │
│   │   ├── Tenant/
│   │   │   ├── Entities/
│   │   │   │   ├── Tenant.php
│   │   │   │   └── TenantPaymentProvider.php
│   │   │   ├── ValueObjects/
│   │   │   │   ├── AppCode.php
│   │   │   │   ├── TenantStatus.php
│   │   │   │   └── BusinessType.php
│   │   │   ├── Repositories/
│   │   │   │   ├── TenantRepositoryInterface.php
│   │   │   │   └── TenantPaymentProviderRepositoryInterface.php
│   │   │   ├── Services/
│   │   │   │   ├── TenantService.php
│   │   │   │   ├── TenantOnboardingService.php
│   │   │   │   └── TenantConfigurationService.php
│   │   │   └── Events/
│   │   │       ├── TenantCreated.php
│   │   │       ├── TenantProviderConfigured.php
│   │   │       └── TenantLimitUpdated.php
│   │   │
│   │   ├── Payment/
│   │   │   ├── Entities/
│   │   │   │   ├── Transaction.php
│   │   │   │   ├── PaymentProvider.php
│   │   │   │   ├── TransactionEvent.php
│   │   │   │   └── Refund.php
│   │   │   ├── ValueObjects/
│   │   │   │   ├── Money.php
│   │   │   │   ├── TransactionStatus.php
│   │   │   │   ├── PaymentMethod.php
│   │   │   │   └── TransactionReference.php
│   │   │   ├── Repositories/
│   │   │   │   ├── TransactionRepositoryInterface.php
│   │   │   │   ├── PaymentProviderRepositoryInterface.php
│   │   │   │   └── RefundRepositoryInterface.php
│   │   │   ├── Services/
│   │   │   │   ├── TransactionService.php
│   │   │   │   ├── PaymentProviderService.php
│   │   │   │   ├── RefundService.php
│   │   │   │   ├── FraudDetectionService.php
│   │   │   │   └── PaymentProviderFactory.php
│   │   │   ├── Adapters/
│   │   │   │   ├── VNPayAdapter.php
│   │   │   │   ├── MoMoAdapter.php
│   │   │   │   ├── ZaloPayAdapter.php
│   │   │   │   └── NapasAdapter.php
│   │   │   └── Events/
│   │   │       ├── TransactionCreated.php
│   │   │       ├── TransactionCompleted.php
│   │   │       ├── TransactionFailed.php
│   │   │       ├── RefundInitiated.php
│   │   │       └── FraudDetected.php
│   │   │
│   │   ├── Webhook/
│   │   │   ├── Entities/
│   │   │   │   ├── WebhookLog.php
│   │   │   │   ├── WebhookDelivery.php
│   │   │   │   └── WebhookConfiguration.php
│   │   │   ├── ValueObjects/
│   │   │   │   ├── WebhookSignature.php
│   │   │   │   ├── WebhookStatus.php
│   │   │   │   └── WebhookType.php
│   │   │   ├── Repositories/
│   │   │   │   ├── WebhookLogRepositoryInterface.php
│   │   │   │   └── WebhookConfigurationRepositoryInterface.php
│   │   │   ├── Services/
│   │   │   │   ├── WebhookService.php
│   │   │   │   ├── WebhookValidationService.php
│   │   │   │   ├── WebhookDeliveryService.php
│   │   │   │   └── WebhookSecurityService.php
│   │   │   └── Events/
│   │   │       ├── WebhookReceived.php
│   │   │       ├── WebhookProcessed.php
│   │   │       └── WebhookFailed.php
│   │   │
│   │   ├── Reconciliation/
│   │   │   ├── Entities/
│   │   │   │   ├── PaymentReconciliation.php
│   │   │   │   ├── ReconciliationDetail.php
│   │   │   │   └── ReconciliationReport.php
│   │   │   ├── ValueObjects/
│   │   │   │   ├── ReconciliationStatus.php
│   │   │   │   ├── MismatchType.php
│   │   │   │   └── ReconciliationPeriod.php
│   │   │   ├── Repositories/
│   │   │   │   ├── ReconciliationRepositoryInterface.php
│   │   │   │   └── ReconciliationDetailRepositoryInterface.php
│   │   │   ├── Services/
│   │   │   │   ├── ReconciliationService.php
│   │   │   │   ├── ReconciliationReportService.php
│   │   │   │   ├── MismatchResolutionService.php
│   │   │   │   └── AutoReconciliationService.php
│   │   │   └── Events/
│   │   │       ├── ReconciliationStarted.php
│   │   │       ├── ReconciliationCompleted.php
│   │   │       └── MismatchDetected.php
│   │   │
│   │   └── Shared/
│   │       ├── ValueObjects/
│   │       │   ├── EntityId.php
│   │       │   ├── Timestamp.php
│   │       │   ├── IpAddress.php
│   │       │   └── UserAgent.php
│   │       ├── Contracts/
│   │       │   ├── EventPublisherInterface.php
│   │       │   ├── AuditableInterface.php
│   │       │   └── CacheableInterface.php
│   │       ├── Traits/
│   │       │   ├── HasUuid.php
│   │       │   ├── Auditable.php
│   │       │   ├── SoftDeletable.php
│   │       │   └── Cacheable.php
│   │       └── Exceptions/
│   │           ├── DomainException.php
│   │           ├── ValidationException.php
│   │           └── BusinessRuleException.php
│   │
│   ├── Infrastructure/
│   │   ├── Persistence/
│   │   │   ├── Repositories/
│   │   │   │   ├── Eloquent/
│   │   │   │   │   ├── EloquentProjectRepository.php
│   │   │   │   │   ├── EloquentTenantRepository.php
│   │   │   │   │   ├── EloquentTransactionRepository.php
│   │   │   │   │   ├── EloquentWebhookLogRepository.php
│   │   │   │   │   └── EloquentReconciliationRepository.php
│   │   │   │   └── Cache/
│   │   │   │       ├── CachedProjectRepository.php
│   │   │   │       ├── CachedTenantRepository.php
│   │   │   │       └── CachedConfigurationRepository.php
│   │   │   └── Models/
│   │   │       ├── Project.php
│   │   │       ├── Tenant.php
│   │   │       ├── TenantPaymentProvider.php
│   │   │       ├── PaymentProvider.php
│   │   │       ├── Transaction.php
│   │   │       ├── TransactionEvent.php
│   │   │       ├── WebhookLog.php
│   │   │       ├── PaymentReconciliation.php
│   │   │       ├── ReconciliationDetail.php
│   │   │       ├── AuditLog.php
│   │   │       ├── SystemConfiguration.php
│   │   │       └── IdempotencyKey.php
│   │   │
│   │   ├── External/
│   │   │   ├── PaymentProviders/
│   │   │   │   ├── VNPay/
│   │   │   │   │   ├── VNPayClient.php
│   │   │   │   │   ├── VNPaySignature.php
│   │   │   │   │   └── VNPayResponseParser.php
│   │   │   │   ├── MoMo/
│   │   │   │   │   ├── MoMoClient.php
│   │   │   │   │   ├── MoMoSignature.php
│   │   │   │   │   └── MoMoResponseParser.php
│   │   │   │   └── Common/
│   │   │   │       ├── HttpClient.php
│   │   │   │       ├── SignatureValidator.php
│   │   │   │       └── ResponseNormalizer.php
│   │   │   └── Notifications/
│   │   │       ├── SlackNotifier.php
│   │   │       ├── EmailNotifier.php
│   │   │       └── SmsNotifier.php
│   │   │
│   │   ├── Cache/
│   │   │   ├── RedisCache.php
│   │   │   ├── CacheManager.php
│   │   │   └── CacheKeys.php
│   │   │
│   │   └── Queue/
│   │       ├── Jobs/
│   │       │   ├── ProcessWebhookJob.php
│   │       │   ├── SendCallbackJob.php
│   │       │   ├── ProcessReconciliationJob.php
│   │       │   ├── SendNotificationJob.php
│   │       │   └── CleanupExpiredDataJob.php
│   │       └── Processors/
│   │           ├── WebhookProcessor.php
│   │           ├── CallbackProcessor.php
│   │           └── NotificationProcessor.php
│   │
│   ├── Application/
│   │   ├── Commands/
│   │   │   ├── Project/
│   │   │   │   ├── CreateProjectCommand.php
│   │   │   │   ├── UpdateProjectCommand.php
│   │   │   │   └── DeleteProjectCommand.php
│   │   │   ├── Tenant/
│   │   │   │   ├── CreateTenantCommand.php
│   │   │   │   ├── UpdateTenantCommand.php
│   │   │   │   ├── ConfigureTenantProviderCommand.php
│   │   │   │   └── DeactivateTenantCommand.php
│   │   │   ├── Transaction/
│   │   │   │   ├── CreateTransactionCommand.php
│   │   │   │   ├── ProcessTransactionCallbackCommand.php
│   │   │   │   ├── RefundTransactionCommand.php
│   │   │   │   └── CancelTransactionCommand.php
│   │   │   └── Webhook/
│   │   │       ├── ProcessWebhookCommand.php
│   │   │       └── ResendWebhookCommand.php
│   │   │
│   │   ├── Queries/
│   │   │   ├── Project/
│   │   │   │   ├── GetProjectQuery.php
│   │   │   │   └── ListProjectsQuery.php
│   │   │   ├── Tenant/
│   │   │   │   ├── GetTenantQuery.php
│   │   │   │   └── ListTenantsQuery.php
│   │   │   ├── Transaction/
│   │   │   │   ├── GetTransactionQuery.php
│   │   │   │   ├── ListTransactionsQuery.php
│   │   │   │   └── GetTransactionStatisticsQuery.php
│   │   │   └── Reporting/
│   │   │       ├── GenerateReconciliationReportQuery.php
│   │   │       ├── GenerateTransactionReportQuery.php
│   │   │       └── GenerateSystemHealthReportQuery.php
│   │   │
│   │   ├── Handlers/
│   │   │   ├── CommandHandlers/
│   │   │   │   ├── Project/
│   │   │   │   ├── Tenant/
│   │   │   │   ├── Transaction/
│   │   │   │   └── Webhook/
│   │   │   └── QueryHandlers/
│   │   │       ├── Project/
│   │   │       ├── Tenant/
│   │   │       ├── Transaction/
│   │   │       └── Reporting/
│   │   │
│   │   └── Services/
│   │       ├── ApplicationService.php
│   │       ├── CommandBusService.php
│   │       ├── QueryBusService.php
│   │       └── EventBusService.php
│   │
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── API/
│   │   │   │   ├── V1/
│   │   │   │   │   ├── Admin/
│   │   │   │   │   │   ├── ProjectController.php
│   │   │   │   │   │   ├── TenantController.php
│   │   │   │   │   │   ├── SystemController.php
│   │   │   │   │   │   ├── ReconciliationController.php
│   │   │   │   │   │   └── MonitoringController.php
│   │   │   │   │   ├── Client/
│   │   │   │   │   │   ├── TransactionController.php
│   │   │   │   │   │   ├── PaymentController.php
│   │   │   │   │   │   └── StatusController.php
│   │   │   │   │   └── Webhook/
│   │   │   │   │       ├── VNPayWebhookController.php
│   │   │   │   │       ├── MoMoWebhookController.php
│   │   │   │   │       ├── ZaloPayWebhookController.php
│   │   │   │   │       └── BaseWebhookController.php
│   │   │   │   └── BaseApiController.php
│   │   │   └── Web/
│   │   │       ├── DashboardController.php
│   │   │       └── HealthCheckController.php
│   │   │
│   │   ├── Middleware/
│   │   │   ├── Security/
│   │   │   │   ├── RateLimitMiddleware.php
│   │   │   │   ├── IpWhitelistMiddleware.php
│   │   │   │   ├── ValidateSignatureMiddleware.php
│   │   │   │   └── CorsMiddleware.php
│   │   │   ├── Request/
│   │   │   │   ├── ResolveProjectAndTenantMiddleware.php
│   │   │   │   ├── ValidateRequestMiddleware.php
│   │   │   │   ├── TransformRequestMiddleware.php
│   │   │   │   └── IdempotencyMiddleware.php
│   │   │   ├── Logging/
│   │   │   │   ├── AuditLogMiddleware.php
│   │   │   │   ├── RequestLoggingMiddleware.php
│   │   │   │   └── PerformanceLoggingMiddleware.php
│   │   │   └── Maintenance/
│   │   │       ├── MaintenanceModeMiddleware.php
│   │   │       └── FeatureToggleMiddleware.php
│   │   │
│   │   ├── Requests/
│   │   │   ├── BaseRequest.php
│   │   │   ├── Project/
│   │   │   │   ├── CreateProjectRequest.php
│   │   │   │   ├── UpdateProjectRequest.php
│   │   │   │   └── ConfigureProjectProviderRequest.php
│   │   │   ├── Tenant/
│   │   │   │   ├── CreateTenantRequest.php
│   │   │   │   ├── UpdateTenantRequest.php
│   │   │   │   └── ConfigureTenantProviderRequest.php
│   │   │   ├── Transaction/
│   │   │   │   ├── CreateTransactionRequest.php
│   │   │   │   ├── QueryTransactionRequest.php
│   │   │   │   ├── RefundTransactionRequest.php
│   │   │   │   └── CancelTransactionRequest.php
│   │   │   └── Webhook/
│   │   │       ├── VNPayWebhookRequest.php
│   │   │       ├── MoMoWebhookRequest.php
│   │   │       └── BaseWebhookRequest.php
│   │   │
│   │   ├── Resources/
│   │   │   ├── ProjectResource.php
│   │   │   ├── TenantResource.php
│   │   │   ├── TransactionResource.php
│   │   │   ├── PaymentProviderResource.php
│   │   │   ├── ReconciliationResource.php
│   │   │   └── Collections/
│   │   │       ├── ProjectCollection.php
│   │   │       ├── TenantCollection.php
│   │   │       └── TransactionCollection.php
│   │   │
│   │   └── Responses/
│   │       ├── ApiResponse.php
│   │       ├── ErrorResponse.php
│   │       ├── SuccessResponse.php
│   │       └── PaginatedResponse.php
│   │
│   ├── Exceptions/
│   │   ├── Handler.php
│   │   ├── BaseException.php
│   │   ├── BusinessException.php
│   │   ├── ValidationException.php
│   │   ├── PaymentProviderException.php
│   │   ├── WebhookException.php
│   │   ├── ReconciliationException.php
│   │   └── SystemException.php
│   │
│   └── Providers/
│       ├── AppServiceProvider.php
│       ├── RouteServiceProvider.php
│       ├── EventServiceProvider.php
│       ├── BroadcastServiceProvider.php
│       ├── DomainServiceProvider.php
│       ├── InfrastructureServiceProvider.php
│       └── ApplicationServiceProvider.php
│
├── config/
│   ├── app.php
│   ├── database.php
│   ├── cache.php
│   ├── queue.php
│   ├── payment-providers.php
│   ├── webhook.php
│   ├── reconciliation.php
│   ├── audit.php
│   ├── monitoring.php
│   └── security.php
│
├── database/
│   ├── migrations/
│   ├── seeders/
│   ├── factories/
│   └── schema/
│       └── smartpost_payment_service_v4.sql
│
├── routes/
│   ├── api.php
│   ├── web.php
│   ├── webhook.php
│   └── admin.php
│
├── resources/
│   ├── views/
│   │   ├── dashboard/
│   │   ├── reports/
│   │   └── emails/
│   └── lang/
│       ├── en/
│       └── vi/
│
├── storage/
│   ├── app/
│   │   ├── reconciliation-reports/
│   │   ├── transaction-exports/
│   │   └── audit-logs/
│   ├── framework/
│   └── logs/
│
└── tests/
    ├── Unit/
    │   ├── Domain/
    │   ├── Application/
    │   └── Infrastructure/
    ├── Feature/
    │   ├── API/
    │   ├── Webhook/
    │   └── Integration/
    └── Helpers/

3. DANH SÁCH API ENDPOINTS

3.1. Admin APIs (Internal Management)

3.1.1. Project Management

POST   /api/v1/admin/projects                           # Tạo dự án mới
GET    /api/v1/admin/projects                           # Danh sách dự án
GET    /api/v1/admin/projects/{project_id}              # Chi tiết dự án
PUT    /api/v1/admin/projects/{project_id}              # Cập nhật dự án
DELETE /api/v1/admin/projects/{project_id}              # Xóa dự án
POST   /api/v1/admin/projects/{project_id}/providers    # Cấu hình cổng thanh toán cho dự án

3.1.2. Tenant Management

POST   /api/v1/admin/tenants                            # Tạo tenant mới
GET    /api/v1/admin/tenants                            # Danh sách tenant
GET    /api/v1/admin/tenants/{tenant_id}                # Chi tiết tenant
PUT    /api/v1/admin/tenants/{tenant_id}                # Cập nhật tenant
DELETE /api/v1/admin/tenants/{tenant_id}                # Xóa tenant
POST   /api/v1/admin/tenants/{tenant_id}/providers      # Cấu hình cổng thanh toán cho tenant
PUT    /api/v1/admin/tenants/{tenant_id}/limits         # Cập nhật hạn mức tenant

3.1.3. System Management

GET    /api/v1/admin/system/health                      # Kiểm tra sức khỏe hệ thống
GET    /api/v1/admin/system/metrics                     # Metrics hệ thống
GET    /api/v1/admin/system/configurations              # Danh sách cấu hình hệ thống
PUT    /api/v1/admin/system/configurations/{key}       # Cập nhật cấu hình
POST   /api/v1/admin/system/maintenance                 # Bật/tắt chế độ bảo trì

3.2. Client APIs (Business Operations)

3.2.1. Transaction APIs

POST   /api/v1/{project_code}/{app_code}/transactions                    # Tạo giao dịch
GET    /api/v1/{project_code}/{app_code}/transactions/{transaction_id}   # Chi tiết giao dịch
GET    /api/v1/{project_code}/{app_code}/transactions/ref/{reference_id} # Tìm giao dịch theo reference
POST   /api/v1/{project_code}/{app_code}/transactions/{transaction_id}/refund  # Hoàn tiền
POST   /api/v1/{project_code}/{app_code}/transactions/{transaction_id}/cancel  # Hủy giao dịch
GET    /api/v1/{project_code}/{app_code}/transactions                    # Danh sách giao dịch (có filter)

3.2.2. Payment Provider APIs

GET    /api/v1/{project_code}/{app_code}/providers                       # Danh sách cổng thanh toán khả dụng
GET    /api/v1/{project_code}/{app_code}/providers/{provider_code}       # Chi tiết cổng thanh toán

3.2.3. Status & Health APIs

GET    /api/v1/{project_code}/{app_code}/status                          # Trạng thái tenant
GET    /api/v1/status                                                    # Health check chung
GET    /api/v1/version                                                   # Thông tin version

3.3. Webhook APIs

3.3.1. Payment Provider Webhooks

POST   /api/v1/webhooks/vnpay                                           # VNPay webhook
GET    /api/v1/webhooks/vnpay/return                                    # VNPay return URL
POST   /api/v1/webhooks/momo                                            # MoMo webhook
GET    /api/v1/webhooks/momo/return                                     # MoMo return URL
POST   /api/v1/webhooks/zalopay                                         # ZaloPay webhook
GET    /api/v1/webhooks/zalopay/return                                  # ZaloPay return URL
POST   /api/v1/webhooks/napas                                           # NAPAS webhook
GET    /api/v1/webhooks/napas/return                                    # NAPAS return URL

3.3.2. Generic Webhook APIs

POST   /api/v1/webhooks/{provider_code}                                 # Generic webhook handler
POST   /api/v1/webhooks/{provider_code}/test                            # Test webhook
GET    /api/v1/webhooks/logs                                            # Webhook logs
POST   /api/v1/webhooks/{webhook_id}/retry                              # Retry webhook

3.4. Reconciliation APIs

3.4.1. Reconciliation Management

GET    /api/v1/admin/reconciliations                                    # Danh sách đối soát
POST   /api/v1/admin/reconciliations                                    # Tạo đối soát mới
GET    /api/v1/admin/reconciliations/{reconciliation_id}               # Chi tiết đối soát
PUT    /api/v1/admin/reconciliations/{reconciliation_id}/resolve       # Giải quyết mismatch
GET    /api/v1/admin/reconciliations/{reconciliation_id}/details       # Chi tiết mismatch
POST   /api/v1/admin/reconciliations/{reconciliation_id}/export        # Export báo cáo

3.5. Monitoring & Reporting APIs

3.5.1. Audit & Logging

GET    /api/v1/admin/audit-logs                                         # Audit logs
GET    /api/v1/admin/webhook-logs                                       # Webhook logs
GET    /api/v1/admin/transaction-logs                                   # Transaction logs
GET    /api/v1/admin/system-logs                                        # System logs

3.5.2. Analytics & Reports

GET    /api/v1/admin/reports/transactions                               # Báo cáo giao dịch
GET    /api/v1/admin/reports/revenue                                    # Báo cáo doanh thu
GET    /api/v1/admin/reports/providers                                  # Báo cáo theo cổng thanh toán
GET    /api/v1/admin/reports/tenants                                    # Báo cáo theo tenant
POST   /api/v1/admin/reports/custom                                     # Báo cáo tùy chỉnh

4. CHI TIẾT API REQUEST/RESPONSE

4.1. Transaction Management APIs

4.1.1. Tạo giao dịch mới

POST /api/v1/{project_code}/{app_code}/transactions

Request:

{
    "order_id": "ORD-20250703-001",
    "amount": 100000,
    "currency": "VND",
    "description": "Thanh toán đơn hàng #12345",
    "provider_code": "vnpay",
    "payment_method": "card",
    "customer_info": {
        "name": "Nguyễn Văn A",
        "email": "nguyenvana@example.com",
        "phone": "0901234567",
        "address": "123 Đường ABC, Quận 1, TP.HCM"
    },
    "return_url": "https://yourapp.com/payment/success",
    "cancel_url": "https://yourapp.com/payment/cancel",
    "callback_url": "https://yourapp.com/api/webhooks/payment",
    "expires_in_minutes": 15,
    "metadata": {
        "internal_order_id": "12345",
        "promotion_code": "SUMMER2025",
        "user_id": "USER-001"
    }
}

Response Success (201):

{
    "success": true,
    "message": "Transaction created successfully",
    "data": {
        "transaction_id": "txn_7k8l9m0n1o2p3q4r5s6t",
        "order_id": "ORD-20250703-001",
        "amount": 100000,
        "currency": "VND",
        "status": "pending",
        "provider_transaction_id": null,
        "payment_url": "https://sandbox.vnpayment.vn/paymentv2/vpcpay.html?...",
        "expires_at": "2025-07-03T10:30:00+07:00",
        "created_at": "2025-07-03T10:15:00+07:00"
    },
    "request_id": "req_1a2b3c4d5e6f7g8h9i0j"
}

Response Error (400):

{
    "success": false,
    "error_code": "INVALID_AMOUNT",
    "message": "Số tiền phải lớn hơn 10,000 VND và nhỏ hơn 500,000,000 VND",
    "details": {
        "field": "amount",
        "value": 5000,
        "min_amount": 10000,
        "max_amount": 500000000
    },
    "request_id": "req_1a2b3c4d5e6f7g8h9i0j"
}

4.1.2. Lấy thông tin giao dịch

GET /api/v1/{project_code}/{app_code}/transactions/{transaction_id}

Response Success (200):

{
    "success": true,
    "data": {
        "transaction_id": "txn_7k8l9m0n1o2p3q4r5s6t",
        "order_id": "ORD-20250703-001",
        "amount": 100000,
        "currency": "VND",
        "description": "Thanh toán đơn hàng #12345",
        "status": "completed",
        "payment_method": "card",
        "provider_code": "vnpay",
        "provider_transaction_id": "14339599",
        "provider_order_id": "ORD20250703001",
        "customer_info": {
            "name": "Nguyễn Văn A",
            "email": "nguyenvana@example.com",
            "phone": "0901234567"
        },
        "transaction_fee": 2300,
        "net_amount": 97700,
        "paid_at": "2025-07-03T10:18:32+07:00",
        "events": [
            {
                "event_type": "created",
                "created_at": "2025-07-03T10:15:00+07:00",
                "data": {}
            },
            {
                "event_type": "payment_initiated",
                "created_at": "2025-07-03T10:15:30+07:00",
                "data": {
                    "payment_url": "https://sandbox.vnpayment.vn/..."
                }
            },
            {
                "event_type": "paid",
                "created_at": "2025-07-03T10:18:32+07:00",
                "data": {
                    "provider_response_code": "00",
                    "bank_code": "NCB"
                }
            }
        ],
        "metadata": {
            "internal_order_id": "12345",
            "promotion_code": "SUMMER2025",
            "user_id": "USER-001"
        },
        "created_at": "2025-07-03T10:15:00+07:00",
        "updated_at": "2025-07-03T10:18:32+07:00"
    },
    "request_id": "req_2b3c4d5e6f7g8h9i0j1k"
}

4.1.3. Hoàn tiền giao dịch

POST /api/v1/{project_code}/{app_code}/transactions/{transaction_id}/refund

Request:

{
    "refund_amount": 50000,
    "reason": "Khách hàng yêu cầu hoàn trả một phần",
    "internal_refund_id": "REFUND-001",
    "notification_url": "https://yourapp.com/api/webhooks/refund"
}

Response Success (200):

{
    "success": true,
    "message": "Refund initiated successfully",
    "data": {
        "refund_id": "rfn_9a8b7c6d5e4f3g2h1i0j",
        "transaction_id": "txn_7k8l9m0n1o2p3q4r5s6t",
        "refund_amount": 50000,
        "remaining_amount": 50000,
        "status": "processing",
        "provider_refund_id": null,
        "reason": "Khách hàng yêu cầu hoàn trả một phần",
        "created_at": "2025-07-03T14:30:00+07:00"
    },
    "request_id": "req_3c4d5e6f7g8h9i0j1k2l"
}

4.2. Webhook Processing

4.2.1. VNPay Webhook

POST /api/v1/webhooks/vnpay

Request từ VNPay:

{
    "vnp_Amount": "10000000",
    "vnp_BankCode": "NCB",
    "vnp_BankTranNo": "VNP14339599",
    "vnp_CardType": "ATM",
    "vnp_OrderInfo": "Thanh toan don hang %3A ORD-20250703-001",
    "vnp_PayDate": "20250703101832",
    "vnp_ResponseCode": "00",
    "vnp_TmnCode": "VNPAY_TMN",
    "vnp_TransactionNo": "14339599",
    "vnp_TransactionStatus": "00",
    "vnp_TxnRef": "ORD20250703001",
    "vnp_SecureHash": "3d4f8b2a1c5e7f9d0a2b4c6e8f1a3b5c"
}

Response tới VNPay:

{
    "RspCode": "00",
    "Message": "Confirm Success"
}

4.3. Error Responses

4.3.1. Standard Error Format

{
    "success": false,
    "error_code": "ERROR_CODE_CONSTANT",
    "message": "Human readable error message",
    "details": {
        "field": "field_name",
        "value": "invalid_value",
        "constraint": "validation_rule"
    },
    "request_id": "req_unique_identifier",
    "timestamp": "2025-07-03T10:15:00+07:00",
    "trace_id": "trace_for_debugging"
}

4.3.2. Common Error Codes

{
    "SYSTEM_ERROR": "Lỗi hệ thống, vui lòng thử lại sau",
    "VALIDATION_ERROR": "Dữ liệu đầu vào không hợp lệ",
    "PROJECT_NOT_FOUND": "Không tìm thấy dự án",
    "TENANT_NOT_FOUND": "Không tìm thấy tenant",
    "TRANSACTION_NOT_FOUND": "Không tìm thấy giao dịch",
    "PROVIDER_NOT_CONFIGURED": "Cổng thanh toán chưa được cấu hình",
    "AMOUNT_INVALID": "Số tiền không hợp lệ",
    "TRANSACTION_EXPIRED": "Giao dịch đã hết hạn",
    "DUPLICATE_ORDER_ID": "Mã đơn hàng đã tồn tại",
    "INSUFFICIENT_BALANCE": "Số dư không đủ",
    "RATE_LIMIT_EXCEEDED": "Vượt quá giới hạn số lượng request",
    "MAINTENANCE_MODE": "Hệ thống đang bảo trì"
}

5. MIDDLEWARE VÀ AUTHENTICATION

5.1. Middleware Chain

5.1.1. API Request Middleware Stack

// Cho các API request từ client applications
[
    'cors',                           // CORS headers
    'throttle:api',                   // Rate limiting (100 req/min per IP)
    'request.logging',                // Log incoming requests
    'resolve.project.tenant',         // Resolve project và tenant context
    'validate.project.tenant',        // Validate project/tenant permissions
    'idempotency',                    // Idempotency check
    'audit.log',                      // Audit logging
    'maintenance.check',              // Check maintenance mode
    'feature.toggle'                  // Feature toggle check
]

5.1.2. Webhook Middleware Stack

// Cho webhook từ payment providers
[
    'webhook.rate.limit',             // Webhook-specific rate limiting
    'webhook.ip.whitelist',           // IP whitelist validation
    'webhook.signature.validation',   // Signature verification
    'webhook.logging',                // Log webhook requests
    'webhook.idempotency',            // Prevent duplicate webhook processing
    'audit.webhook'                   // Webhook audit logging
]

5.1.3. Admin Middleware Stack

// Cho admin/management APIs
[
    'cors',
    'throttle:admin',                 // Higher rate limit for admin (1000 req/min)
    'admin.ip.whitelist',             // Admin IP whitelist
    'request.logging',
    'audit.admin',                    // Admin action logging
    'maintenance.bypass'              // Allow admin during maintenance
]

5.2. Middleware Implementation Details

5.2.1. ResolveProjectAndTenantMiddleware

<?php

namespace App\Http\Middleware\Request;

use Closure;
use Illuminate\Http\Request;
use App\Domain\Project\Repositories\ProjectRepositoryInterface;
use App\Domain\Tenant\Repositories\TenantRepositoryInterface;
use App\Exceptions\ProjectNotFoundException;
use App\Exceptions\TenantNotFoundException;

class ResolveProjectAndTenantMiddleware
{
    private $projectRepository;
    private $tenantRepository;
    
    public function __construct(
        ProjectRepositoryInterface $projectRepository,
        TenantRepositoryInterface $tenantRepository
    ) {
        $this->projectRepository = $projectRepository;
        $this->tenantRepository = $tenantRepository;
    }
    
    public function handle(Request $request, Closure $next)
    {
        $projectCode = $request->route('project_code');
        $appCode = $request->route('app_code');
        
        if (!$projectCode || !$appCode) {
            return response()->json([
                'success' => false,
                'error_code' => 'MISSING_PROJECT_OR_APP_CODE',
                'message' => 'Project code và app code là bắt buộc'
            ], 400);
        }
        
        // Resolve project
        $project = $this->projectRepository->findByCode($projectCode);
        if (!$project) {
            throw new ProjectNotFoundException("Project not found: {$projectCode}");
        }
        
        if (!$project->isActive()) {
            return response()->json([
                'success' => false,
                'error_code' => 'PROJECT_INACTIVE',
                'message' => 'Dự án đã bị vô hiệu hóa'
            ], 403);
        }
        
        // Resolve tenant
        $tenant = $this->tenantRepository->findByAppCode($appCode, $project->getId());
        if (!$tenant) {
            throw new TenantNotFoundException("Tenant not found: {$appCode}");
        }
        
        if (!$tenant->isActive()) {
            return response()->json([
                'success' => false,
                'error_code' => 'TENANT_INACTIVE',
                'message' => 'Tenant đã bị vô hiệu hóa'
            ], 403);
        }
        
        // Add to request context
        $request->merge([
            'resolved_project' => $project,
            'resolved_tenant' => $tenant,
            'project_id' => $project->getId(),
            'tenant_id' => $tenant->getId()
        ]);
        
        // Add to app context for easy access
        app()->instance('current.project', $project);
        app()->instance('current.tenant', $tenant);
        
        return $next($request);
    }
}

5.2.2. AuditLogMiddleware

<?php

namespace App\Http\Middleware\Logging;

use Closure;
use Illuminate\Http\Request;
use App\Domain\Shared\Services\AuditLogService;
use Illuminate\Support\Str;

class AuditLogMiddleware
{
    private $auditService;
    
    public function __construct(AuditLogService $auditService)
    {
        $this->auditService = $auditService;
    }
    
    public function handle(Request $request, Closure $next)
    {
        $startTime = microtime(true);
        $requestId = Str::uuid();
        
        // Add request ID to request for tracking
        $request->headers->set('X-Request-ID', $requestId);
        
        // Log request start
        $this->auditService->logRequest([
            'request_id' => $requestId,
            'entity_type' => $this->getEntityType($request),
            'action' => $this->getAction($request),
            'endpoint' => $request->path(),
            'method' => $request->method(),
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'project_code' => $request->route('project_code'),
            'app_code' => $request->route('app_code'),
            'tenant_id' => $request->get('tenant_id'),
            'request_headers' => $this->sanitizeHeaders($request->headers->all()),
            'request_body' => $this->sanitizeRequestBody($request->all()),
            'received_at' => now()
        ]);
        
        // Process request
        $response = $next($request);
        
        // Calculate response time
        $responseTime = round((microtime(true) - $startTime) * 1000);
        
        // Log response
        $this->auditService->logResponse([
            'request_id' => $requestId,
            'response_status' => $response->getStatusCode(),
            'response_body' => $this->sanitizeResponseBody($response),
            'response_time_ms' => $responseTime,
            'completed_at' => now()
        ]);
        
        // Add request ID to response headers
        $response->headers->set('X-Request-ID', $requestId);
        
        return $response;
    }
    
    private function getEntityType(Request $request): string
    {
        $path = $request->path();
        
        if (str_contains($path, 'transactions')) return 'transaction';
        if (str_contains($path, 'projects')) return 'project';
        if (str_contains($path, 'tenants')) return 'tenant';
        if (str_contains($path, 'webhooks')) return 'webhook';
        if (str_contains($path, 'reconciliations')) return 'reconciliation';
        
        return 'unknown';
    }
    
    private function getAction(Request $request): string
    {
        $method = $request->method();
        $path = $request->path();
        
        return match($method) {
            'GET' => str_contains($path, '/') && !str_ends_with($path, 's') ? 'view' : 'list',
            'POST' => 'create',
            'PUT', 'PATCH' => 'update',
            'DELETE' => 'delete',
            default => 'unknown'
        };
    }
    
    private function sanitizeHeaders(array $headers): array
    {
        $sensitive = ['authorization', 'cookie', 'x-api-key', 'x-signature'];
        
        $sanitized = [];
        foreach ($headers as $key => $value) {
            if (in_array(strtolower($key), $sensitive)) {
                $sanitized[$key] = '[REDACTED]';
            } else {
                $sanitized[$key] = is_array($value) ? $value[0] : $value;
            }
        }
        
        return $sanitized;
    }
    
    private function sanitizeRequestBody(array $body): array
    {
        $sensitive = [
            'password', 'secret_key', 'api_key', 'secret', 'token',
            'webhook_secret', 'signature', 'hash', 'private_key'
        ];
        
        return $this->recursiveSanitize($body, $sensitive);
    }
    
    private function sanitizeResponseBody($response): ?array
    {
        if (!$response instanceof \Illuminate\Http\JsonResponse) {
            return null;
        }
        
        $data = $response->getData(true);
        
        // Don't log large response bodies
        if (json_encode($data) && strlen(json_encode($data)) > 10000) {
            return ['message' => 'Response body too large to log'];
        }
        
        $sensitive = ['secret_key', 'api_key', 'token', 'signature'];
        
        return $this->recursiveSanitize($data, $sensitive);
    }
    
    private function recursiveSanitize(array $data, array $sensitive): array
    {
        $sanitized = [];
        
        foreach ($data as $key => $value) {
            if (in_array(strtolower($key), $sensitive)) {
                $sanitized[$key] = '[REDACTED]';
            } elseif (is_array($value)) {
                $sanitized[$key] = $this->recursiveSanitize($value, $sensitive);
            } else {
                $sanitized[$key] = $value;
            }
        }
        
        return $sanitized;
    }
}

5.2.3. IdempotencyMiddleware

<?php

namespace App\Http\Middleware\Request;

use Closure;
use Illuminate\Http\Request;
use App\Infrastructure\Cache\CacheManager;
use App\Domain\Shared\Services\IdempotencyService;

class IdempotencyMiddleware
{
    private $idempotencyService;
    
    public function __construct(IdempotencyService $idempotencyService)
    {
        $this->idempotencyService = $idempotencyService;
    }
    
    public function handle(Request $request, Closure $next)
    {
        // Only check for POST, PUT, PATCH requests
        if (!in_array($request->method(), ['POST', 'PUT', 'PATCH'])) {
            return $next($request);
        }
        
        $idempotencyKey = $request->header('X-Idempotency-Key');
        
        if (!$idempotencyKey) {
            // Generate one for internal use
            $idempotencyKey = $this->generateIdempotencyKey($request);
        }
        
        // Check if this request was already processed
        $existingResult = $this->idempotencyService->getResult(
            $idempotencyKey,
            $request->get('tenant_id')
        );
        
        if ($existingResult) {
            // Return the cached result
            return response()->json($existingResult['response'], $existingResult['status_code'])
                ->withHeaders(['X-Idempotency-Cache' => 'HIT']);
        }
        
        // Process the request
        $response = $next($request);
        
        // Cache the result for successful operations
        if ($response->getStatusCode() < 400) {
            $this->idempotencyService->storeResult(
                $idempotencyKey,
                $request->get('tenant_id'),
                $response->getData(true),
                $response->getStatusCode(),
                now()->addHours(24) // Cache for 24 hours
            );
        }
        
        return $response->withHeaders(['X-Idempotency-Cache' => 'MISS']);
    }
    
    private function generateIdempotencyKey(Request $request): string
    {
        $components = [
            $request->method(),
            $request->path(),
            $request->get('tenant_id'),
            md5(json_encode($request->all()))
        ];
        
        return 'auto_' . md5(implode('|', $components));
    }
}

6. XỬ LÝ WEBHOOK

6.1. Webhook Security

6.1.1. Multi-layer Validation

<?php

namespace App\Http\Middleware\Security;

use Closure;
use Illuminate\Http\Request;
use App\Domain\Webhook\Services\WebhookSecurityService;

class ValidateWebhookSignatureMiddleware
{
    private $webhookSecurity;
    
    public function __construct(WebhookSecurityService $webhookSecurity)
    {
        $this->webhookSecurity = $webhookSecurity;
    }
    
    public function handle(Request $request, Closure $next)
    {
        $providerCode = $request->route('provider') ?? $this->detectProvider($request);
        
        if (!$providerCode) {
            return response()->json([
                'error' => 'Provider not detected'
            ], 400);
        }
        
        // Step 1: IP Whitelist validation
        if (!$this->webhookSecurity->validateIP($providerCode, $request->ip())) {
            \Log::warning('Webhook IP validation failed', [
                'provider' => $providerCode,
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent()
            ]);
            
            return response()->json(['error' => 'Invalid source'], 403);
        }
        
        // Step 2: Signature validation
        if (!$this->webhookSecurity->validateSignature($providerCode, $request)) {
            \Log::warning('Webhook signature validation failed', [
                'provider' => $providerCode,
                'ip' => $request->ip(),
                'headers' => $request->headers->all()
            ]);
            
            return response()->json(['error' => 'Invalid signature'], 403);
        }
        
        // Step 3: Timestamp validation (prevent replay attacks)
        if (!$this->webhookSecurity->validateTimestamp($request)) {
            \Log::warning('Webhook timestamp validation failed', [
                'provider' => $providerCode,
                'timestamp' => $request->header('timestamp')
            ]);
            
            return response()->json(['error' => 'Request too old'], 403);
        }
        
        $request->merge(['validated_provider' => $providerCode]);
        
        return $next($request);
    }
    
    private function detectProvider(Request $request): ?string
    {
        $path = $request->path();
        $userAgent = $request->userAgent();
        
        if (str_contains($path, 'vnpay')) return 'vnpay';
        if (str_contains($path, 'momo')) return 'momo';
        if (str_contains($path, 'zalopay')) return 'zalopay';
        if (str_contains($path, 'napas')) return 'napas';
        
        // Detect by User-Agent
        if (str_contains($userAgent, 'VNPay')) return 'vnpay';
        if (str_contains($userAgent, 'MoMo')) return 'momo';
        
        return null;
    }
}

6.2. Webhook Processing

6.2.1. Generic Webhook Controller

<?php

namespace App\Http\Controllers\API\V1\Webhook;

use Illuminate\Http\Request;
use App\Http\Controllers\API\BaseApiController;
use App\Domain\Webhook\Services\WebhookService;
use App\Infrastructure\Queue\Jobs\ProcessWebhookJob;

class BaseWebhookController extends BaseApiController
{
    private $webhookService;
    
    public function __construct(WebhookService $webhookService)
    {
        $this->webhookService = $webhookService;
    }
    
    public function handle(Request $request, string $provider)
    {
        try {
            // Log incoming webhook
            $webhookLog = $this->webhookService->logIncomingWebhook([
                'provider_code' => $provider,
                'source_ip' => $request->ip(),
                'headers' => $request->headers->all(),
                'payload' => $request->all(),
                'received_at' => now()
            ]);
            
            // Process webhook asynchronously
            ProcessWebhookJob::dispatch($webhookLog->id, $provider, $request->all())
                ->onQueue('webhooks');
            
            // Return immediate response to provider
            return $this->getProviderSuccessResponse($provider);
            
        } catch (\Exception $e) {
            \Log::error('Webhook processing error', [
                'provider' => $provider,
                'error' => $e->getMessage(),
                'payload' => $request->all()
            ]);
            
            return $this->getProviderErrorResponse($provider, $e->getMessage());
        }
    }
    
    private function getProviderSuccessResponse(string $provider): \Illuminate\Http\JsonResponse
    {
        return match($provider) {
            'vnpay' => response()->json(['RspCode' => '00', 'Message' => 'Confirm Success']),
            'momo' => response()->json(['status' => 0, 'message' => 'success']),
            'zalopay' => response()->json(['return_code' => 1, 'return_message' => 'success']),
            'napas' => response()->json(['responseCode' => '00', 'desc' => 'success']),
            default => response()->json(['status' => 'success'])
        };
    }
    
    private function getProviderErrorResponse(string $provider, string $error): \Illuminate\Http\JsonResponse
    {
        return match($provider) {
            'vnpay' => response()->json(['RspCode' => '99', 'Message' => 'Error'], 500),
            'momo' => response()->json(['status' => -1, 'message' => 'error']),
            'zalopay' => response()->json(['return_code' => 0, 'return_message' => 'error']),
            'napas' => response()->json(['responseCode' => '99', 'desc' => 'error']),
            default => response()->json(['status' => 'error', 'message' => $error], 500)
        };
    }
}

6.2.2. Webhook Processing Job

<?php

namespace App\Infrastructure\Queue\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Domain\Webhook\Services\WebhookService;
use App\Domain\Payment\Services\TransactionService;
use App\Infrastructure\Queue\Jobs\SendCallbackJob;

class ProcessWebhookJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
    public $tries = 3;
    public $backoff = [10, 30, 60]; // seconds
    
    private $webhookLogId;
    private $provider;
    private $payload;
    
    public function __construct(string $webhookLogId, string $provider, array $payload)
    {
        $this->webhookLogId = $webhookLogId;
        $this->provider = $provider;
        $this->payload = $payload;
    }
    
    public function handle(
        WebhookService $webhookService,
        TransactionService $transactionService
    ) {
        try {
            // Update webhook log status
            $webhookService->updateStatus($this->webhookLogId, 'processing');
            
            // Process based on provider
            $result = match($this->provider) {
                'vnpay' => $this->processVNPayWebhook($transactionService),
                'momo' => $this->processMoMoWebhook($transactionService),
                'zalopay' => $this->processZaloPayWebhook($transactionService),
                'napas' => $this->processNapasWebhook($transactionService),
                default => throw new \Exception("Unsupported provider: {$this->provider}")
            };
            
            // Update webhook log with result
            $webhookService->updateStatus($this->webhookLogId, 'processed', $result);
            
            // Send callback to tenant if needed
            if ($result['transaction_id'] && $result['callback_url']) {
                SendCallbackJob::dispatch(
                    $result['transaction_id'],
                    $result['callback_url'],
                    $result['callback_data']
                )->onQueue('callbacks');
            }
            
        } catch (\Exception $e) {
            $webhookService->updateStatus($this->webhookLogId, 'failed', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            
            throw $e; // Re-throw for retry mechanism
        }
    }
    
    private function processVNPayWebhook(TransactionService $transactionService): array
    {
        $transactionRef = $this->payload['vnp_TxnRef'] ?? null;
        $responseCode = $this->payload['vnp_ResponseCode'] ?? null;
        $transactionNo = $this->payload['vnp_TransactionNo'] ?? null;
        $amount = $this->payload['vnp_Amount'] ?? null;
        
        if (!$transactionRef) {
            throw new \Exception('Missing vnp_TxnRef in webhook');
        }
        
        // Find transaction by provider reference
        $transaction = $transactionService->findByProviderReference($transactionRef);
        
        if (!$transaction) {
            throw new \Exception("Transaction not found: {$transactionRef}");
        }
        
        // Update transaction based on response code
        $status = $responseCode === '00' ? 'completed' : 'failed';
        
        $updateData = [
            'status' => $status,
            'provider_transaction_id' => $transactionNo,
            'provider_response' => $this->payload,
            'processed_at' => now()
        ];
        
        if ($status === 'completed') {
            $updateData['paid_at'] = now();
            $updateData['net_amount'] = ($amount / 100) - $transaction->transaction_fee;
        } else {
            $updateData['failed_at'] = now();
            $updateData['failure_reason'] = $this->getVNPayErrorMessage($responseCode);
        }
        
        $transactionService->updateTransaction($transaction->id, $updateData);
        
        return [
            'transaction_id' => $transaction->id,
            'status' => $status,
            'callback_url' => $transaction->callback_url,
            'callback_data' => [
                'transaction_id' => $transaction->id,
                'order_id' => $transaction->order_id,
                'status' => $status,
                'amount' => $transaction->amount,
                'provider_transaction_id' => $transactionNo,
                'processed_at' => now()->toISOString()
            ]
        ];
    }
    
    private function getVNPayErrorMessage(string $responseCode): string
    {
        return match($responseCode) {
            '07' => 'Trừ tiền thành công. Giao dịch bị nghi ngờ (liên quan tới lừa đảo, giao dịch bất thường).',
            '09' => 'Giao dịch không thành công do: Thẻ/Tài khoản của khách hàng chưa đăng ký dịch vụ InternetBanking tại ngân hàng.',
            '10' => 'Giao dịch không thành công do: Khách hàng xác thực thông tin thẻ/tài khoản không đúng quá 3 lần',
            '11' => 'Giao dịch không thành công do: Đã hết hạn chờ thanh toán. Xin quý khách vui lòng thực hiện lại giao dịch.',
            '12' => 'Giao dịch không thành công do: Thẻ/Tài khoản của khách hàng bị khóa.',
            '13' => 'Giao dịch không thành công do Quý khách nhập sai mật khẩu xác thực giao dịch (OTP).',
            '24' => 'Giao dịch không thành công do: Khách hàng hủy giao dịch',
            '51' => 'Giao dịch không thành công do: Tài khoản của quý khách không đủ số dư để thực hiện giao dịch.',
            '65' => 'Giao dịch không thành công do: Tài khoản của Quý khách đã vượt quá hạn mức giao dịch trong ngày.',
            '75' => 'Ngân hàng thanh toán đang bảo trì.',
            '79' => 'Giao dịch không thành công do: KH nhập sai mật khẩu thanh toán quá số lần quy định.',
            '99' => 'Các lỗi khác (lỗi còn lại, không có trong danh sách mã lỗi đã liệt kê)',
            default => "Giao dịch thất bại với mã lỗi: {$responseCode}"
        };
    }
}

7. XỬ LÝ CÁC TRƯỜNG HỢP ĐẶC BIỆT

7.1. Fraud Detection

7.1.1. Real-time Fraud Detection Service

<?php

namespace App\Domain\Payment\Services;

use App\Domain\Payment\Entities\Transaction;
use App\Domain\Payment\Events\FraudDetected;

class FraudDetectionService
{
    private $rules = [];
    
    public function __construct()
    {
        $this->initializeRules();
    }
    
    public function analyzeTransaction(Transaction $transaction): array
    {
        $riskScore = 0;
        $flags = [];
        
        foreach ($this->rules as $rule) {
            $result = $rule->analyze($transaction);
            $riskScore += $result['score'];
            
            if ($result['triggered']) {
                $flags[] = $result['flag'];
            }
        }
        
        $riskLevel = $this->calculateRiskLevel($riskScore);
        
        if ($riskLevel === 'HIGH') {
            event(new FraudDetected($transaction, $riskScore, $flags));
        }
        
        return [
            'risk_score' => $riskScore,
            'risk_level' => $riskLevel,
            'flags' => $flags,
            'action' => $this->getRecommendedAction($riskLevel)
        ];
    }
    
    private function initializeRules(): void
    {
        $this->rules = [
            new VelocityRule(),           // Tần suất giao dịch
            new AmountRule(),             // Số tiền bất thường
            new GeolocationRule(),        // Địa lý bất thường
            new DeviceFingerprintRule(),  // Thiết bị lạ
            new TimePatternRule(),        // Thời gian bất thường
            new BehaviorRule()            // Hành vi bất thường
        ];
    }
    
    private function calculateRiskLevel(int $score): string
    {
        return match(true) {
            $score >= 80 => 'HIGH',
            $score >= 50 => 'MEDIUM',
            $score >= 20 => 'LOW',
            default => 'MINIMAL'
        };
    }
    
    private function getRecommendedAction(string $riskLevel): string
    {
        return match($riskLevel) {
            'HIGH' => 'BLOCK',
            'MEDIUM' => 'REVIEW',
            'LOW' => 'MONITOR',
            default => 'ALLOW'
        };
    }
}

7.2. Circuit Breaker Pattern

7.2.2. Payment Provider Circuit Breaker

<?php

namespace App\Infrastructure\External\Common;

use App\Infrastructure\Cache\CacheManager;

class CircuitBreaker
{
    private $cache;
    private $failureThreshold;
    private $recoveryTimeout;
    private $monitoringPeriod;
    
    public function __construct(
        CacheManager $cache,
        int $failureThreshold = 5,
        int $recoveryTimeout = 60,
        int $monitoringPeriod = 300
    ) {
        $this->cache = $cache;
        $this->failureThreshold = $failureThreshold;
        $this->recoveryTimeout = $recoveryTimeout;
        $this->monitoringPeriod = $monitoringPeriod;
    }
    
    public function call(string $service, callable $callback)
    {
        $state = $this->getState($service);
        
        if ($state === 'OPEN') {
            if ($this->shouldAttemptReset($service)) {
                $this->setState($service, 'HALF_OPEN');
            } else {
                throw new \Exception("Circuit breaker is OPEN for service: {$service}");
            }
        }
        
        try {
            $result = $callback();
            $this->recordSuccess($service);
            
            if ($state === 'HALF_OPEN') {
                $this->setState($service, 'CLOSED');
            }
            
            return $result;
            
        } catch (\Exception $e) {
            $this->recordFailure($service);
            
            if ($this->shouldOpenCircuit($service)) {
                $this->setState($service, 'OPEN');
                $this->setLastFailureTime($service, time());
            }
            
            throw $e;
        }
    }
    
    private function getState(string $service): string
    {
        return $this->cache->get("circuit_breaker:{$service}:state", 'CLOSED');
    }
    
    private function setState(string $service, string $state): void
    {
        $this->cache->put("circuit_breaker:{$service}:state", $state, $this->monitoringPeriod);
    }
    
    private function recordFailure(string $service): void
    {
        $key = "circuit_breaker:{$service}:failures";
        $failures = $this->cache->get($key, 0);
        $this->cache->put($key, $failures + 1, $this->monitoringPeriod);
    }
    
    private function recordSuccess(string $service): void
    {
        $this->cache->forget("circuit_breaker:{$service}:failures");
    }
    
    private function shouldOpenCircuit(string $service): bool
    {
        $failures = $this->cache->get("circuit_breaker:{$service}:failures", 0);
        return $failures >= $this->failureThreshold;
    }
    
    private function shouldAttemptReset(string $service): bool
    {
        $lastFailure = $this->cache->get("circuit_breaker:{$service}:last_failure", 0);
        return (time() - $lastFailure) >= $this->recoveryTimeout;
    }
    
    private function setLastFailureTime(string $service, int $time): void
    {
        $this->cache->put("circuit_breaker:{$service}:last_failure", $time, $this->recoveryTimeout * 2);
    }
}

7.3. Retry Mechanism

7.3.1. Exponential Backoff Retry

<?php

namespace App\Infrastructure\External\Common;

class RetryHandler
{
    private $maxAttempts;
    private $baseDelay;
    private $maxDelay;
    private $multiplier;
    
    public function __construct(
        int $maxAttempts = 3,
        int $baseDelay = 1000,  // milliseconds
        int $maxDelay = 30000,  // milliseconds
        float $multiplier = 2.0
    ) {
        $this->maxAttempts = $maxAttempts;
        $this->baseDelay = $baseDelay;
        $this->maxDelay = $maxDelay;
        $this->multiplier = $multiplier;
    }
    
    public function execute(callable $callback, array $retryableExceptions = [])
    {
        $attempt = 1;
        $lastException = null;
        
        while ($attempt <= $this->maxAttempts) {
            try {
                return $callback();
                
            } catch (\Exception $e) {
                $lastException = $e;
                
                // Check if this exception is retryable
                if (!$this->isRetryable($e, $retryableExceptions)) {
                    throw $e;
                }
                
                // Don't retry on last attempt
                if ($attempt === $this->maxAttempts) {
                    break;
                }
                
                // Calculate delay with jitter
                $delay = $this->calculateDelay($attempt);
                $this->sleep($delay);
                
                \Log::warning('Retrying operation', [
                    'attempt' => $attempt,
                    'max_attempts' => $this->maxAttempts,
                    'delay_ms' => $delay,
                    'exception' => $e->getMessage()
                ]);
                
                $attempt++;
            }
        }
        
        throw $lastException;
    }
    
    private function isRetryable(\Exception $e, array $retryableExceptions): bool
    {
        if (empty($retryableExceptions)) {
            // Default retryable exceptions
            return $e instanceof \GuzzleHttp\Exception\ConnectException ||
                   $e instanceof \GuzzleHttp\Exception\ServerException ||
                   $e instanceof \Illuminate\Http\Client\ConnectionException;
        }
        
        foreach ($retryableExceptions as $exceptionClass) {
            if ($e instanceof $exceptionClass) {
                return true;
            }
        }
        
        return false;
    }
    
    private function calculateDelay(int $attempt): int
    {
        // Exponential backoff with jitter
        $exponentialDelay = $this->baseDelay * pow($this->multiplier, $attempt - 1);
        $delayWithJitter = $exponentialDelay + random_int(0, (int)($exponentialDelay * 0.1));
        
        return min($delayWithJitter, $this->maxDelay);
    }
    
    private function sleep(int $milliseconds): void
    {
        usleep($milliseconds * 1000);
    }
}

8. AUDIT LOG VÀ MONITORING

8.1. Comprehensive Audit System

8.1.1. Audit Log Service

<?php

namespace App\Domain\Shared\Services;

use App\Infrastructure\Persistence\Models\AuditLog;
use Illuminate\Support\Facades\Auth;

class AuditLogService
{
    public function logAction(array $data): AuditLog
    {
        return AuditLog::create([
            'id' => \Str::uuid(),
            'tenant_id' => $data['tenant_id'] ?? null,
            'entity_type' => $data['entity_type'],
            'entity_id' => $data['entity_id'],
            'action' => $data['action'],
            'old_values' => $data['old_values'] ?? null,
            'new_values' => $data['new_values'] ?? null,
            'changed_fields' => $data['changed_fields'] ?? null,
            'user_id' => $data['user_id'] ?? 'system',
            'user_type' => $data['user_type'] ?? 'system',
            'ip_address' => $data['ip_address'] ?? request()->ip(),
            'user_agent' => $data['user_agent'] ?? request()->userAgent(),
            'request_id' => $data['request_id'] ?? request()->header('X-Request-ID'),
            'additional_data' => $data['additional_data'] ?? null,
            'created_at' => now()
        ]);
    }
    
    public function logEntityChange(
        string $entityType,
        string $entityId,
        string $action,
        array $oldValues = null,
        array $newValues = null,
        string $tenantId = null
    ): AuditLog {
        $changedFields = null;
        
        if ($oldValues && $newValues) {
            $changedFields = array_keys(array_diff_assoc($newValues, $oldValues));
        }
        
        return $this->logAction([
            'tenant_id' => $tenantId,
            'entity_type' => $entityType,
            'entity_id' => $entityId,
            'action' => $action,
            'old_values' => $oldValues,
            'new_values' => $newValues,
            'changed_fields' => $changedFields
        ]);
    }
    
    public function logTransactionEvent(
        string $transactionId,
        string $eventType,
        array $eventData = [],
        string $source = 'system'
    ): AuditLog {
        return $this->logAction([
            'tenant_id' => app('current.tenant')->id ?? null,
            'entity_type' => 'transaction',
            'entity_id' => $transactionId,
            'action' => $eventType,
            'new_values' => $eventData,
            'additional_data' => [
                'source' => $source,
                'timestamp' => now()->toISOString()
            ]
        ]);
    }
    
    public function logWebhookEvent(
        string $webhookId,
        string $provider,
        array $payload,
        string $status = 'received'
    ): AuditLog {
        return $this->logAction([
            'entity_type' => 'webhook',
            'entity_id' => $webhookId,
            'action' => "webhook_{$status}",
            'new_values' => [
                'provider' => $provider,
                'payload_hash' => md5(json_encode($payload)),
                'status' => $status
            ],
            'additional_data' => [
                'provider' => $provider,
                'payload_size' => strlen(json_encode($payload))
            ]
        ]);
    }
}

8.2. System Health Monitoring

8.2.1. Health Check Service

<?php

namespace App\Domain\System\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use App\Infrastructure\External\PaymentProviders\VNPay\VNPayClient;

class SystemHealthService
{
    public function getSystemHealth(): array
    {
        return [
            'overall_status' => $this->getOverallStatus(),
            'timestamp' => now()->toISOString(),
            'version' => config('app.version'),
            'environment' => config('app.env'),
            'checks' => [
                'database' => $this->checkDatabase(),
                'redis' => $this->checkRedis(),
                'queue' => $this->checkQueue(),
                'storage' => $this->checkStorage(),
                'payment_providers' => $this->checkPaymentProviders(),
                'external_apis' => $this->checkExternalAPIs()
            ]
        ];
    }
    
    private function checkDatabase(): array
    {
        try {
            $start = microtime(true);
            DB::select('SELECT 1');
            $responseTime = round((microtime(true) - $start) * 1000, 2);
            
            return [
                'status' => 'healthy',
                'response_time_ms' => $responseTime,
                'connection' => DB::connection()->getName()
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'unhealthy',
                'error' => $e->getMessage()
            ];
        }
    }
    
    private function checkRedis(): array
    {
        try {
            $start = microtime(true);
            Redis::ping();
            $responseTime = round((microtime(true) - $start) * 1000, 2);
            
            return [
                'status' => 'healthy',
                'response_time_ms' => $responseTime,
                'memory_usage' => Redis::info('memory')['used_memory_human'] ?? 'unknown'
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'unhealthy',
                'error' => $e->getMessage()
            ];
        }
    }
    
    private function checkQueue(): array
    {
        try {
            $queueSizes = [
                'default' => \Queue::size('default'),
                'webhooks' => \Queue::size('webhooks'),
                'callbacks' => \Queue::size('callbacks'),
                'reconciliation' => \Queue::size('reconciliation')
            ];
            
            $totalPending = array_sum($queueSizes);
            $status = $totalPending > 1000 ? 'warning' : 'healthy';
            
            return [
                'status' => $status,
                'queue_sizes' => $queueSizes,
                'total_pending' => $totalPending
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'unhealthy',
                'error' => $e->getMessage()
            ];
        }
    }
    
    private function checkPaymentProviders(): array
    {
        $providers = ['vnpay', 'momo', 'zalopay', 'napas'];
        $results = [];
        
        foreach ($providers as $provider) {
            $results[$provider] = $this->checkProviderHealth($provider);
        }
        
        return $results;
    }
    
    private function checkProviderHealth(string $provider): array
    {
        try {
            // Test với health check endpoint nếu có
            $start = microtime(true);
            
            switch ($provider) {
                case 'vnpay':
                    // VNPay không có health check endpoint public
                    // Kiểm tra connectivity thông qua DNS resolution
                    $host = parse_url(config("payment-providers.{$provider}.api_url"), PHP_URL_HOST);
                    if (!gethostbyname($host)) {
                        throw new \Exception('Cannot resolve hostname');
                    }
                    break;
                    
                default:
                    // Generic connectivity check
                    $host = parse_url(config("payment-providers.{$provider}.api_url"), PHP_URL_HOST);
                    if (!gethostbyname($host)) {
                        throw new \Exception('Cannot resolve hostname');
                    }
                    break;
            }
            
            $responseTime = round((microtime(true) - $start) * 1000, 2);
            
            return [
                'status' => 'healthy',
                'response_time_ms' => $responseTime
            ];
            
        } catch (\Exception $e) {
            return [
                'status' => 'unhealthy',
                'error' => $e->getMessage()
            ];
        }
    }
    
    private function getOverallStatus(): string
    {
        $checks = [
            $this->checkDatabase()['status'],
            $this->checkRedis()['status'],
            $this->checkQueue()['status']
        ];
        
        if (in_array('unhealthy', $checks)) {
            return 'unhealthy';
        }
        
        if (in_array('warning', $checks)) {
            return 'warning';
        }
        
        return 'healthy';
    }
}

9. JOBS VÀ QUEUE PROCESSING

9.1. Queue Configuration

9.1.1. Queue Structure

// config/queue.php
return [
    'default' => env('QUEUE_CONNECTION', 'redis'),
    
    'connections' => [
        'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
            'queue' => 'default',
            'retry_after' => 90,
            'block_for' => null,
        ],
    ],
    
    'batching' => [
        'database' => env('DB_CONNECTION', 'mysql'),
        'table' => 'job_batches',
    ],
    
    // Custom queue definitions
    'queues' => [
        'high_priority' => ['webhooks', 'callbacks'],
        'medium_priority' => ['reconciliation', 'notifications'],
        'low_priority' => ['reports', 'cleanup']
    ]
];

9.2. Critical Jobs

9.2.1. SendCallbackJob

<?php

namespace App\Infrastructure\Queue\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
use App\Domain\Payment\Repositories\TransactionRepositoryInterface;
use App\Domain\Shared\Services\AuditLogService;
use App\Infrastructure\External\Common\RetryHandler;

class SendCallbackJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
    public $tries = 5;
    public $backoff = [10, 30, 60, 300, 900]; // seconds: 10s, 30s, 1m, 5m, 15m
    public $timeout = 30;
    
    private $transactionId;
    private $callbackUrl;
    private $callbackData;
    private $attempt;
    
    public function __construct(
        string $transactionId, 
        string $callbackUrl, 
        array $callbackData,
        int $attempt = 1
    ) {
        $this->transactionId = $transactionId;
        $this->callbackUrl = $callbackUrl;
        $this->callbackData = $callbackData;
        $this->attempt = $attempt;
        
        // Set queue based on priority
        $this->onQueue('callbacks');
    }
    
    public function handle(
        TransactionRepositoryInterface $transactionRepository,
        AuditLogService $auditService,
        RetryHandler $retryHandler
    ) {
        $transaction = $transactionRepository->findById($this->transactionId);
        
        if (!$transaction) {
            \Log::error('Transaction not found for callback', [
                'transaction_id' => $this->transactionId
            ]);
            return;
        }
        
        try {
            // Log callback attempt
            $auditService->logAction([
                'tenant_id' => $transaction->tenant_id,
                'entity_type' => 'transaction_callback',
                'entity_id' => $this->transactionId,
                'action' => 'callback_attempt',
                'additional_data' => [
                    'callback_url' => $this->callbackUrl,
                    'attempt' => $this->attempt,
                    'max_attempts' => $this->tries
                ]
            ]);
            
            // Send callback with retry mechanism
            $response = $retryHandler->execute(function() {
                return Http::timeout($this->timeout)
                    ->withHeaders([
                        'Content-Type' => 'application/json',
                        'User-Agent' => 'SmartPost-Payment-Service/1.0',
                        'X-Callback-Signature' => $this->generateSignature(),
                        'X-Callback-Timestamp' => now()->timestamp,
                        'X-Callback-Attempt' => $this->attempt
                    ])
                    ->post($this->callbackUrl, $this->callbackData);
            });
            
            // Check response
            if ($response->successful()) {
                $auditService->logAction([
                    'tenant_id' => $transaction->tenant_id,
                    'entity_type' => 'transaction_callback',
                    'entity_id' => $this->transactionId,
                    'action' => 'callback_success',
                    'additional_data' => [
                        'response_status' => $response->status(),
                        'response_time_ms' => $response->handlerStats()['total_time'] * 1000,
                        'attempt' => $this->attempt
                    ]
                ]);
                
                // Update transaction callback status
                $transactionRepository->updateCallback($this->transactionId, [
                    'callback_status' => 'delivered',
                    'callback_delivered_at' => now(),
                    'callback_attempts' => $this->attempt,
                    'callback_response' => [
                        'status' => $response->status(),
                        'body' => $response->body()
                    ]
                ]);
                
            } else {
                throw new \Exception("HTTP {$response->status()}: {$response->body()}");
            }
            
        } catch (\Exception $e) {
            \Log::error('Callback delivery failed', [
                'transaction_id' => $this->transactionId,
                'callback_url' => $this->callbackUrl,
                'attempt' => $this->attempt,
                'error' => $e->getMessage()
            ]);
            
            $auditService->logAction([
                'tenant_id' => $transaction->tenant_id,
                'entity_type' => 'transaction_callback',
                'entity_id' => $this->transactionId,
                'action' => 'callback_failed',
                'additional_data' => [
                    'error' => $e->getMessage(),
                    'attempt' => $this->attempt,
                    'will_retry' => $this->attempt < $this->tries
                ]
            ]);
            
            // Update callback status on final failure
            if ($this->attempt >= $this->tries) {
                $transactionRepository->updateCallback($this->transactionId, [
                    'callback_status' => 'failed',
                    'callback_failed_at' => now(),
                    'callback_attempts' => $this->attempt,
                    'callback_error' => $e->getMessage()
                ]);
            }
            
            throw $e; // Re-throw for retry mechanism
        }
    }
    
    public function failed(\Exception $exception)
    {
        \Log::error('Callback job finally failed', [
            'transaction_id' => $this->transactionId,
            'callback_url' => $this->callbackUrl,
            'attempts' => $this->tries,
            'error' => $exception->getMessage()
        ]);
        
        // Send alert to monitoring system
        \Event::dispatch(new \App\Events\CallbackDeliveryFailed(
            $this->transactionId,
            $this->callbackUrl,
            $exception->getMessage()
        ));
    }
    
    private function generateSignature(): string
    {
        $secret = config('app.callback_secret');
        $payload = json_encode($this->callbackData);
        
        return hash_hmac('sha256', $payload, $secret);
    }
}

9.2.2. ProcessReconciliationJob

<?php

namespace App\Infrastructure\Queue\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Domain\Reconciliation\Services\ReconciliationService;
use Carbon\Carbon;

class ProcessReconciliationJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
    public $tries = 3;
    public $timeout = 600; // 10 minutes
    
    private $tenantId;
    private $providerId;
    private $reconciliationDate;
    
    public function __construct(string $tenantId, string $providerId, Carbon $reconciliationDate)
    {
        $this->tenantId = $tenantId;
        $this->providerId = $providerId;
        $this->reconciliationDate = $reconciliationDate;
        
        $this->onQueue('reconciliation');
    }
    
    public function handle(ReconciliationService $reconciliationService)
    {
        try {
            \Log::info('Starting reconciliation process', [
                'tenant_id' => $this->tenantId,
                'provider_id' => $this->providerId,
                'date' => $this->reconciliationDate->toDateString()
            ]);
            
            $result = $reconciliationService->reconcileForTenant(
                $this->tenantId,
                $this->providerId,
                $this->reconciliationDate
            );
            
            \Log::info('Reconciliation completed', [
                'tenant_id' => $this->tenantId,
                'provider_id' => $this->providerId,
                'result' => $result
            ]);
            
            // Send notification if there are mismatches
            if ($result['mismatched_transactions'] > 0) {
                \Event::dispatch(new \App\Events\ReconciliationMismatchDetected(
                    $this->tenantId,
                    $this->providerId,
                    $result
                ));
            }
            
        } catch (\Exception $e) {
            \Log::error('Reconciliation process failed', [
                'tenant_id' => $this->tenantId,
                'provider_id' => $this->providerId,
                'error' => $e->getMessage()
            ]);
            
            throw $e;
        }
    }
}

9.2.3. CleanupExpiredDataJob

<?php

namespace App\Infrastructure\Queue\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Domain\Shared\Services\DataRetentionService;

class CleanupExpiredDataJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
    public $tries = 1;
    public $timeout = 3600; // 1 hour
    
    public function __construct()
    {
        $this->onQueue('cleanup');
    }
    
    public function handle(DataRetentionService $dataRetentionService)
    {
        try {
            \Log::info('Starting data cleanup process');
            
            $results = [
                'audit_logs' => $dataRetentionService->cleanupAuditLogs(),
                'webhook_logs' => $dataRetentionService->cleanupWebhookLogs(),
                'transaction_events' => $dataRetentionService->cleanupTransactionEvents(),
                'idempotency_keys' => $dataRetentionService->cleanupIdempotencyKeys(),
                'expired_transactions' => $dataRetentionService->cleanupExpiredTransactions()
            ];
            
            \Log::info('Data cleanup completed', $results);
            
        } catch (\Exception $e) {
            \Log::error('Data cleanup failed', [
                'error' => $e->getMessage()
            ]);
            
            throw $e;
        }
    }
}

10. ERROR HANDLING

10.1. Exception Hierarchy

10.1.1. Base Exception Classes

<?php

namespace App\Exceptions;

abstract class BaseException extends \Exception
{
    protected $errorCode;
    protected $errorData;
    protected $httpStatusCode;
    
    public function __construct(
        string $message = '',
        string $errorCode = '',
        array $errorData = [],
        int $httpStatusCode = 400,
        \Exception $previous = null
    ) {
        parent::__construct($message, 0, $previous);
        
        $this->errorCode = $errorCode ?: static::getDefaultErrorCode();
        $this->errorData = $errorData;
        $this->httpStatusCode = $httpStatusCode;
    }
    
    abstract protected static function getDefaultErrorCode(): string;
    
    public function getErrorCode(): string
    {
        return $this->errorCode;
    }
    
    public function getErrorData(): array
    {
        return $this->errorData;
    }
    
    public function getHttpStatusCode(): int
    {
        return $this->httpStatusCode;
    }
    
    public function toArray(): array
    {
        return [
            'success' => false,
            'error_code' => $this->getErrorCode(),
            'message' => $this->getMessage(),
            'details' => $this->getErrorData(),
            'timestamp' => now()->toISOString()
        ];
    }
}

class BusinessException extends BaseException
{
    protected static function getDefaultErrorCode(): string
    {
        return 'BUSINESS_RULE_VIOLATION';
    }
}

class ValidationException extends BaseException
{
    protected static function getDefaultErrorCode(): string
    {
        return 'VALIDATION_ERROR';
    }
}

class PaymentProviderException extends BaseException
{
    protected static function getDefaultErrorCode(): string
    {
        return 'PAYMENT_PROVIDER_ERROR';
    }
}

10.2. Global Exception Handler

10.2.1. Custom Exception Handler

<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Validation\ValidationException as LaravelValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Throwable;

class Handler extends ExceptionHandler
{
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
        'secret_key',
        'api_key',
        'webhook_secret'
    ];
    
    protected $dontReport = [
        ValidationException::class,
        LaravelValidationException::class,
        NotFoundHttpException::class,
        MethodNotAllowedHttpException::class
    ];
    
    public function register()
    {
        $this->reportable(function (Throwable $e) {
            if (app()->bound('sentry')) {
                app('sentry')->captureException($e);
            }
        });
    }
    
    public function render($request, Throwable $e)
    {
        // API requests should always return JSON
        if ($request->expectsJson() || $request->is('api/*')) {
            return $this->renderApiException($request, $e);
        }
        
        return parent::render($request, $e);
    }
    
    private function renderApiException(Request $request, Throwable $e): JsonResponse
    {
        // Handle custom exceptions
        if ($e instanceof BaseException) {
            return response()->json($e->toArray(), $e->getHttpStatusCode());
        }
        
        // Handle Laravel validation exceptions
        if ($e instanceof LaravelValidationException) {
            return response()->json([
                'success' => false,
                'error_code' => 'VALIDATION_ERROR',
                'message' => 'Dữ liệu đầu vào không hợp lệ',
                'details' => [
                    'errors' => $e->errors()
                ],
                'timestamp' => now()->toISOString()
            ], 422);
        }
        
        // Handle 404 errors
        if ($e instanceof NotFoundHttpException) {
            return response()->json([
                'success' => false,
                'error_code' => 'NOT_FOUND',
                'message' => 'Không tìm thấy tài nguyên được yêu cầu',
                'timestamp' => now()->toISOString()
            ], 404);
        }
        
        // Handle 405 errors
        if ($e instanceof MethodNotAllowedHttpException) {
            return response()->json([
                'success' => false,
                'error_code' => 'METHOD_NOT_ALLOWED',
                'message' => 'Phương thức HTTP không được hỗ trợ',
                'timestamp' => now()->toISOString()
            ], 405);
        }
        
        // Handle generic exceptions
        $statusCode = method_exists($e, 'getStatusCode') ? $e->getStatusCode() : 500;
        $errorCode = $statusCode === 500 ? 'INTERNAL_SERVER_ERROR' : 'CLIENT_ERROR';
        
        $response = [
            'success' => false,
            'error_code' => $errorCode,
            'message' => $statusCode === 500 ? 'Đã xảy ra lỗi hệ thống' : $e->getMessage(),
            'timestamp' => now()->toISOString()
        ];
        
        // Add debug info in non-production environments
        if (config('app.debug') && $statusCode === 500) {
            $response['debug'] = [
                'exception' => get_class($e),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString()
            ];
        }
        
        // Add request ID for tracking
        if ($requestId = $request->header('X-Request-ID')) {
            $response['request_id'] = $requestId;
        }
        
        return response()->json($response, $statusCode);
    }
    
    protected function context()
    {
        return array_merge(parent::context(), [
            'tenant_id' => app('current.tenant')->id ?? null,
            'project_id' => app('current.project')->id ?? null,
            'request_id' => request()->header('X-Request-ID')
        ]);
    }
}

10.3. Error Code Registry

10.3.1. Centralized Error Codes

<?php

namespace App\Constants;

class ErrorCodes
{
    // System Errors (1000-1999)
    const SYSTEM_ERROR = 'SYSTEM_ERROR';
    const DATABASE_ERROR = 'DATABASE_ERROR';
    const NETWORK_ERROR = 'NETWORK_ERROR';
    const CACHE_ERROR = 'CACHE_ERROR';
    const QUEUE_ERROR = 'QUEUE_ERROR';
    
    // Authentication & Authorization Errors (2000-2999)
    const UNAUTHORIZED = 'UNAUTHORIZED';
    const FORBIDDEN = 'FORBIDDEN';
    const INVALID_API_KEY = 'INVALID_API_KEY';
    const RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED';
    
    // Validation Errors (3000-3999)
    const VALIDATION_ERROR = 'VALIDATION_ERROR';
    const INVALID_AMOUNT = 'INVALID_AMOUNT';
    const INVALID_CURRENCY = 'INVALID_CURRENCY';
    const INVALID_PAYMENT_METHOD = 'INVALID_PAYMENT_METHOD';
    const DUPLICATE_ORDER_ID = 'DUPLICATE_ORDER_ID';
    
    // Business Logic Errors (4000-4999)
    const PROJECT_NOT_FOUND = 'PROJECT_NOT_FOUND';
    const PROJECT_INACTIVE = 'PROJECT_INACTIVE';
    const PROJECT_HAS_TENANTS = 'PROJECT_HAS_TENANTS';
    
    const TENANT_NOT_FOUND = 'TENANT_NOT_FOUND';
    const TENANT_INACTIVE = 'TENANT_INACTIVE';
    const TENANT_LIMIT_EXCEEDED = 'TENANT_LIMIT_EXCEEDED';
    
    const TRANSACTION_NOT_FOUND = 'TRANSACTION_NOT_FOUND';
    const TRANSACTION_EXPIRED = 'TRANSACTION_EXPIRED';
    const TRANSACTION_ALREADY_PROCESSED = 'TRANSACTION_ALREADY_PROCESSED';
    const TRANSACTION_CANNOT_BE_REFUNDED = 'TRANSACTION_CANNOT_BE_REFUNDED';
    
    // Payment Provider Errors (5000-5999)
    const PROVIDER_NOT_CONFIGURED = 'PROVIDER_NOT_CONFIGURED';
    const PROVIDER_INACTIVE = 'PROVIDER_INACTIVE';
    const PROVIDER_CONNECTION_ERROR = 'PROVIDER_CONNECTION_ERROR';
    const PROVIDER_INVALID_RESPONSE = 'PROVIDER_INVALID_RESPONSE';
    const PROVIDER_AUTHENTICATION_FAILED = 'PROVIDER_AUTHENTICATION_FAILED';
    
    // Webhook Errors (6000-6999)
    const WEBHOOK_INVALID_SIGNATURE = 'WEBHOOK_INVALID_SIGNATURE';
    const WEBHOOK_INVALID_IP = 'WEBHOOK_INVALID_IP';
    const WEBHOOK_PROCESSING_ERROR = 'WEBHOOK_PROCESSING_ERROR';
    const WEBHOOK_DELIVERY_FAILED = 'WEBHOOK_DELIVERY_FAILED';
    
    // Reconciliation Errors (7000-7999)
    const RECONCILIATION_IN_PROGRESS = 'RECONCILIATION_IN_PROGRESS';
    const RECONCILIATION_FAILED = 'RECONCILIATION_FAILED';
    const RECONCILIATION_DATA_MISMATCH = 'RECONCILIATION_DATA_MISMATCH';
    
    public static function getMessage(string $code): string
    {
        return match($code) {
            // System Errors
            self::SYSTEM_ERROR => 'Đã xảy ra lỗi hệ thống, vui lòng thử lại sau',
            self::DATABASE_ERROR => 'Lỗi kết nối cơ sở dữ liệu',
            self::NETWORK_ERROR => 'Lỗi kết nối mạng',
            self::CACHE_ERROR => 'Lỗi hệ thống cache',
            self::QUEUE_ERROR => 'Lỗi hệ thống queue',
            
            // Authentication & Authorization
            self::UNAUTHORIZED => 'Không có quyền truy cập',
            self::FORBIDDEN => 'Truy cập bị từ chối',
            self::INVALID_API_KEY => 'API key không hợp lệ',
            self::RATE_LIMIT_EXCEEDED => 'Vượt quá giới hạn số lượng request',
            
            // Validation
            self::VALIDATION_ERROR => 'Dữ liệu đầu vào không hợp lệ',
            self::INVALID_AMOUNT => 'Số tiền không hợp lệ',
            self::INVALID_CURRENCY => 'Đơn vị tiền tệ không được hỗ trợ',
            self::INVALID_PAYMENT_METHOD => 'Phương thức thanh toán không hợp lệ',
            self::DUPLICATE_ORDER_ID => 'Mã đơn hàng đã tồn tại',
            
            // Business Logic
            self::PROJECT_NOT_FOUND => 'Không tìm thấy dự án',
            self::PROJECT_INACTIVE => 'Dự án đã bị vô hiệu hóa',
            self::PROJECT_HAS_TENANTS => 'Không thể xóa dự án vì còn tồn tại tenant',
            
            self::TENANT_NOT_FOUND => 'Không tìm thấy tenant',
            self::TENANT_INACTIVE => 'Tenant đã bị vô hiệu hóa',
            self::TENANT_LIMIT_EXCEEDED => 'Vượt quá hạn mức của tenant',
            
            self::TRANSACTION_NOT_FOUND => 'Không tìm thấy giao dịch',
            self::TRANSACTION_EXPIRED => 'Giao dịch đã hết hạn',
            self::TRANSACTION_ALREADY_PROCESSED => 'Giao dịch đã được xử lý',
            self::TRANSACTION_CANNOT_BE_REFUNDED => 'Giao dịch không thể hoàn tiền',
            
            // Payment Provider
            self::PROVIDER_NOT_CONFIGURED => 'Cổng thanh toán chưa được cấu hình',
            self::PROVIDER_INACTIVE => 'Cổng thanh toán đã bị vô hiệu hóa',
            self::PROVIDER_CONNECTION_ERROR => 'Không thể kết nối đến cổng thanh toán',
            self::PROVIDER_INVALID_RESPONSE => 'Phản hồi từ cổng thanh toán không hợp lệ',
            self::PROVIDER_AUTHENTICATION_FAILED => 'Xác thực với cổng thanh toán thất bại',
            
            // Webhook
            self::WEBHOOK_INVALID_SIGNATURE => 'Chữ ký webhook không hợp lệ',
            self::WEBHOOK_INVALID_IP => 'IP address không được phép',
            self::WEBHOOK_PROCESSING_ERROR => 'Lỗi xử lý webhook',
            self::WEBHOOK_DELIVERY_FAILED => 'Gửi webhook thất bại',
            
            // Reconciliation
            self::RECONCILIATION_IN_PROGRESS => 'Đối soát đang được thực hiện',
            self::RECONCILIATION_FAILED => 'Đối soát thất bại',
            self::RECONCILIATION_DATA_MISMATCH => 'Dữ liệu đối soát không khớp',
            
            default => 'Đã xảy ra lỗi không xác định'
        };
    }
    
    public static function getHttpStatusCode(string $code): int
    {
        return match($code) {
            // 400 Bad Request
            self::VALIDATION_ERROR,
            self::INVALID_AMOUNT,
            self::INVALID_CURRENCY,
            self::INVALID_PAYMENT_METHOD,
            self::DUPLICATE_ORDER_ID,
            self::TRANSACTION_EXPIRED,
            self::TRANSACTION_ALREADY_PROCESSED => 400,
            
            // 401 Unauthorized
            self::UNAUTHORIZED,
            self::INVALID_API_KEY => 401,
            
            // 403 Forbidden
            self::FORBIDDEN,
            self::PROJECT_INACTIVE,
            self::TENANT_INACTIVE,
            self::WEBHOOK_INVALID_IP => 403,
            
            // 404 Not Found
            self::PROJECT_NOT_FOUND,
            self::TENANT_NOT_FOUND,
            self::TRANSACTION_NOT_FOUND => 404,
            
            // 409 Conflict
            self::PROJECT_HAS_TENANTS,
            self::RECONCILIATION_IN_PROGRESS => 409,
            
            // 422 Unprocessable Entity
            self::PROVIDER_NOT_CONFIGURED,
            self::TRANSACTION_CANNOT_BE_REFUNDED => 422,
            
            // 429 Too Many Requests
            self::RATE_LIMIT_EXCEEDED,
            self::TENANT_LIMIT_EXCEEDED => 429,
            
            // 502 Bad Gateway
            self::PROVIDER_CONNECTION_ERROR,
            self::PROVIDER_INVALID_RESPONSE => 502,
            
            // 503 Service Unavailable
            self::PROVIDER_INACTIVE => 503,
            
            // 500 Internal Server Error (default)
            default => 500
        };
    }
}

11. PERFORMANCE VÀ SCALING

11.1. Database Optimization

11.1.1. Query Optimization Strategies

<?php

namespace App\Infrastructure\Persistence\Repositories\Eloquent;

use App\Domain\Payment\Repositories\TransactionRepositoryInterface;
use App\Infrastructure\Persistence\Models\Transaction;
use Illuminate\Pagination\LengthAwarePaginator;

class EloquentTransactionRepository implements TransactionRepositoryInterface
{
    public function findWithPagination(
        array $filters = [],
        int $perPage = 20,
        int $page = 1,
        array $with = []
    ): LengthAwarePaginator {
        $query = Transaction::query();
        
        // Always eager load commonly used relationships
        $defaultWith = ['tenant', 'paymentProvider', 'events'];
        $with = array_merge($defaultWith, $with);
        $query->with($with);
        
        // Apply filters with proper indexing
        if (!empty($filters['tenant_id'])) {
            $query->where('tenant_id', $filters['tenant_id']);
        }
        
        if (!empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }
        
        if (!empty($filters['date_from'])) {
            $query->where('created_at', '>=', $filters['date_from']);
        }
        
        if (!empty($filters['date_to'])) {
            $query->where('created_at', '<=', $filters['date_to']);
        }
        
        if (!empty($filters['amount_from'])) {
            $query->where('amount', '>=', $filters['amount_from']);
        }
        
        if (!empty($filters['amount_to'])) {
            $query->where('amount', '<=', $filters['amount_to']);
        }
        
        if (!empty($filters['provider_code'])) {
            $query->whereHas('paymentProvider', function($q) use ($filters) {
                $q->where('code', $filters['provider_code']);
            });
        }
        
        if (!empty($filters['search'])) {
            $query->where(function($q) use ($filters) {
                $q->where('order_id', 'like', "%{$filters['search']}%")
                  ->orWhere('provider_transaction_id', 'like', "%{$filters['search']}%")
                  ->orWhereJsonContains('customer_info->email', $filters['search']);
            });
        }
        
        // Use appropriate indexes for sorting
        $sortField = $filters['sort'] ?? 'created_at';
        $sortDirection = $filters['direction'] ?? 'desc';
        
        $query->orderBy($sortField, $sortDirection);
        
        // Add secondary sort for consistency
        if ($sortField !== 'id') {
            $query->orderBy('id', 'desc');
        }
        
        return $query->paginate($perPage, ['*'], 'page', $page);
    }
    
    public function getStatistics(array $filters = []): array
    {
        $query = Transaction::query();
        
        // Apply same filters as pagination
        $this->applyFilters($query, $filters);
        
        // Use database aggregation for better performance
        $stats = $query->selectRaw('
            COUNT(*) as total_transactions,
            COUNT(CASE WHEN status = "completed" THEN 1 END) as completed_count,
            COUNT(CASE WHEN status = "failed" THEN 1 END) as failed_count,
            COUNT(CASE WHEN status = "pending" THEN 1 END) as pending_count,
            SUM(CASE WHEN status = "completed" THEN amount ELSE 0 END) as total_amount,
            SUM(CASE WHEN status = "completed" THEN transaction_fee ELSE 0 END) as total_fees,
            AVG(CASE WHEN status = "completed" THEN amount END) as avg_amount
        ')->first();
        
        return [
            'total_transactions' => (int) $stats->total_transactions,
            'completed_count' => (int) $stats->completed_count,
            'failed_count' => (int) $stats->failed_count,
            'pending_count' => (int) $stats->pending_count,
            'total_amount' => (float) $stats->total_amount,
            'total_fees' => (float) $stats->total_fees,
            'average_amount' => (float) $stats->avg_amount,
            'success_rate' => $stats->total_transactions > 0 
                ? round(($stats->completed_count / $stats->total_transactions) * 100, 2) 
                : 0
        ];
    }
}

11.2. Caching Strategy

11.2.1. Multi-layer Caching Implementation

<?php

namespace App\Infrastructure\Cache;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;

class CacheManager
{
    private const DEFAULT_TTL = 3600; // 1 hour
    private const SHORT_TTL = 300;    // 5 minutes
    private const LONG_TTL = 86400;   // 24 hours
    
    // Cache configuration for different entities
    private const CACHE_CONFIG = [
        'projects' => ['ttl' => self::LONG_TTL, 'tags' => ['projects']],
        'tenants' => ['ttl' => self::LONG_TTL, 'tags' => ['tenants']],
        'payment_providers' => ['ttl' => self::LONG_TTL, 'tags' => ['providers']],
        'tenant_providers' => ['ttl' => self::DEFAULT_TTL, 'tags' => ['tenants', 'providers']],
        'transactions' => ['ttl' => self::SHORT_TTL, 'tags' => ['transactions']],
        'system_config' => ['ttl' => self::LONG_TTL, 'tags' => ['config']],
        'statistics' => ['ttl' => self::SHORT_TTL, 'tags' => ['stats']]
    ];
    
    public function get(string $key, $default = null)
    {
        return Cache::get($key, $default);
    }
    
    public function put(string $key, $value, string $type = 'default', int $ttl = null): bool
    {
        $config = self::CACHE_CONFIG[$type] ?? ['ttl' => self::DEFAULT_TTL, 'tags' => []];
        $ttl = $ttl ?? $config['ttl'];
        
        if (!empty($config['tags'])) {
            return Cache::tags($config['tags'])->put($key, $value, $ttl);
        }
        
        return Cache::put($key, $value, $ttl);
    }
    
    public function remember(string $key, callable $callback, string $type = 'default', int $ttl = null)
    {
        $config = self::CACHE_CONFIG[$type] ?? ['ttl' => self::DEFAULT_TTL, 'tags' => []];
        $ttl = $ttl ?? $config['ttl'];
        
        if (!empty($config['tags'])) {
            return Cache::tags($config['tags'])->remember($key, $ttl, $callback);
        }
        
        return Cache::remember($key, $ttl, $callback);
    }
    
    public function forget(string $key): bool
    {
        return Cache::forget($key);
    }
    
    public function flush(array $tags = []): bool
    {
        if (empty($tags)) {
            return Cache::flush();
        }
        
        return Cache::tags($tags)->flush();
    }
    
    // High-performance methods using Redis directly
    public function getFromRedis(string $key, $default = null)
    {
        $value = Redis::get($key);
        
        if ($value === null) {
            return $default;
        }
        
        return json_decode($value, true);
    }
    
    public function putToRedis(string $key, $value, int $ttl = self::DEFAULT_TTL): bool
    {
        return Redis::setex($key, $ttl, json_encode($value));
    }
    
    // Atomic operations for counters
    public function increment(string $key, int $value = 1): int
    {
        return Redis::incrby($key, $value);
    }
    
    public function decrement(string $key, int $value = 1): int
    {
        return Redis::decrby($key, $value);
    }
    
    // Lock mechanism for critical sections
    public function lock(string $key, int $ttl = 30): bool
    {
        return Redis::set("lock:{$key}", 1, 'EX', $ttl, 'NX');
    }
    
    public function unlock(string $key): bool
    {
        return Redis::del("lock:{$key}") > 0;
    }
    
    // Bulk operations for better performance
    public function multiGet(array $keys): array
    {
        $values = Redis::mget($keys);
        $result = [];
        
        foreach ($keys as $index => $key) {
            $result[$key] = $values[$index] ? json_decode($values[$index], true) : null;
        }
        
        return $result;
    }
    
    public function multiSet(array $data, int $ttl = self::DEFAULT_TTL): bool
    {
        $pipeline = Redis::pipeline();
        
        foreach ($data as $key => $value) {
            $pipeline->setex($key, $ttl, json_encode($value));
        }
        
        $results = $pipeline->execute();
        
        return !in_array(false, $results, true);
    }
}

11.3. Auto-scaling Configuration

11.3.1. Kubernetes Deployment Configuration

# kubernetes/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: smartpost-payment-service
  labels:
    app: smartpost-payment-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: smartpost-payment-service
  template:
    metadata:
      labels:
        app: smartpost-payment-service
    spec:
      containers:
      - name: app
        image: smartpost/payment-service:latest
        ports:
        - containerPort: 80
        env:
        - name: APP_ENV
          value: "production"
        - name: DB_HOST
          valueFrom:
            secretKeyRef:
              name: database-secret
              key: host
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /api/v1/status
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /api/v1/status
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: smartpost-payment-service
spec:
  selector:
    app: smartpost-payment-service
  ports:
  - port: 80
    targetPort: 80
  type: LoadBalancer

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: smartpost-payment-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: smartpost-payment-service
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 100
        periodSeconds: 60
      - type: Pods
        value: 5
        periodSeconds: 60
      selectPolicy: Max

12. SECURITY VÀ COMPLIANCE

12.1. Data Encryption

12.1.1. Sensitive Data Encryption Service

<?php

namespace App\Domain\Shared\Services;

use Illuminate\Encryption\Encrypter;

class EncryptionService
{
    private $encrypter;
    private $keyRotationEnabled;
    
    public function __construct()
    {
        $this->encrypter = new Encrypter(
            config('app.encryption_key'),
            config('app.cipher')
        );
        $this->keyRotationEnabled = config('app.key_rotation_enabled', false);
    }
    
    public function encrypt(string $value, string $context = ''): string
    {
        $data = [
            'value' => $value,
            'context' => $context,
            'timestamp' => now()->timestamp,
            'version' => config('app.encryption_version', 1)
        ];
        
        return $this->encrypter->encrypt($data);
    }
    
    public function decrypt(string $encryptedValue): string
    {
        $data = $this->encrypter->decrypt($encryptedValue);
        
        // Handle key rotation
        if ($this->keyRotationEnabled && $data['version'] < config('app.encryption_version')) {
            // Re-encrypt with new key
            $newEncrypted = $this->encrypt($data['value'], $data['context']);
            // Update database record (implement based on context)
            $this->updateEncryptedValue($encryptedValue, $newEncrypted, $data['context']);
        }
        
        return $data['value'];
    }
    
    public function hash(string $value, string $salt = ''): string
    {
        return hash_hmac('sha256', $value, config('app.key') . $salt);
    }
    
    public function generateSecureToken(int $length = 32): string
    {
        return bin2hex(random_bytes($length));
    }
    
    private function updateEncryptedValue(string $old, string $new, string $context): void
    {
        // Implement based on context - update database records
        // This would typically involve identifying the record and updating it
        \Log::info('Key rotation: Re-encrypted value', [
            'context' => $context,
            'old_hash' => substr(md5($old), 0, 8),
            'new_hash' => substr(md5($new), 0, 8)
        ]);
    }
}

12.2. Compliance Framework

12.2.1. GDPR Compliance Service

<?php

namespace App\Domain\Shared\Services;

use App\Infrastructure\Persistence\Models\AuditLog;
use Carbon\Carbon;

class ComplianceService
{
    private $auditService;
    private $encryptionService;
    
    public function __construct(
        AuditLogService $auditService,
        EncryptionService $encryptionService
    ) {
        $this->auditService = $auditService;
        $this->encryptionService = $encryptionService;
    }
    
    public function anonymizePersonalData(string $tenantId, string $dataSubject): array
    {
        $results = [];
        
        // Anonymize transaction data
        $results['transactions'] = $this->anonymizeTransactionData($tenantId, $dataSubject);
        
        // Anonymize audit logs
        $results['audit_logs'] = $this->anonymizeAuditLogs($tenantId, $dataSubject);
        
        // Anonymize webhook logs
        $results['webhook_logs'] = $this->anonymizeWebhookLogs($tenantId, $dataSubject);
        
        // Log the anonymization action
        $this->auditService->logAction([
            'tenant_id' => $tenantId,
            'entity_type' => 'personal_data',
            'entity_id' => $dataSubject,
            'action' => 'anonymized',
            'additional_data' => [
                'anonymization_results' => $results,
                'requested_at' => now()->toISOString(),
                'retention_policy' => 'gdpr_article_17'
            ]
        ]);
        
        return $results;
    }
    
    public function exportPersonalData(string $tenantId, string $dataSubject): array
    {
        $exportData = [
            'data_subject' => $dataSubject,
            'tenant_id' => $tenantId,
            'export_date' => now()->toISOString(),
            'data_categories' => []
        ];
        
        // Export transaction data
        $exportData['data_categories']['transactions'] = $this->exportTransactionData($tenantId, $dataSubject);
        
        // Export customer information
        $exportData['data_categories']['customer_info'] = $this->exportCustomerInfo($tenantId, $dataSubject);
        
        // Export audit trail
        $exportData['data_categories']['audit_trail'] = $this->exportAuditTrail($tenantId, $dataSubject);
        
        // Log the export action
        $this->auditService->logAction([
            'tenant_id' => $tenantId,
            'entity_type' => 'personal_data',
            'entity_id' => $dataSubject,
            'action' => 'exported',
            'additional_data' => [
                'export_size' => strlen(json_encode($exportData)),
                'categories_count' => count($exportData['data_categories']),
                'gdpr_article' => 'article_15'
            ]
        ]);
        
        return $exportData;
    }
    
    public function deletePersonalData(string $tenantId, string $dataSubject, string $legalBasis): array
    {
        $deletionResults = [];
        
        // Delete from transactions (customer_info)
        $deletionResults['transactions'] = \DB::table('transactions')
            ->where('tenant_id', $tenantId)
            ->whereJsonContains('customer_info->email', $dataSubject)
            ->update([
                'customer_info' => json_encode(['deleted' => true, 'deleted_at' => now()]),
                'updated_at' => now()
            ]);
        
        // Delete from audit logs
        $deletionResults['audit_logs'] = \DB::table('audit_logs')
            ->where('tenant_id', $tenantId)
            ->where('additional_data->customer_email', $dataSubject)
            ->delete();
        
        // Log the deletion
        $this->auditService->logAction([
            'tenant_id' => $tenantId,
            'entity_type' => 'personal_data',
            'entity_id' => $dataSubject,
            'action' => 'deleted',
            'additional_data' => [
                'legal_basis' => $legalBasis,
                'deletion_results' => $deletionResults,
                'gdpr_article' => 'article_17'
            ]
        ]);
        
        return $deletionResults;
    }
    
    public function applyDataRetentionPolicy(): array
    {
        $retentionDays = config('app.data_retention_days', 2555); // 7 years default
        $cutoffDate = now()->subDays($retentionDays);
        
        $results = [
            'cutoff_date' => $cutoffDate->toISOString(),
            'deleted_records' => []
        ];
        
        // Archive old transactions
        $results['deleted_records']['transactions'] = $this->archiveOldTransactions($cutoffDate);
        
        // Clean up old audit logs
        $results['deleted_records']['audit_logs'] = $this->cleanupOldAuditLogs($cutoffDate);
        
        // Clean up old webhook logs
        $results['deleted_records']['webhook_logs'] = $this->cleanupOldWebhookLogs($cutoffDate);
        
        return $results;
    }
    
    private function anonymizeTransactionData(string $tenantId, string $dataSubject): int
    {
        return \DB::table('transactions')
            ->where('tenant_id', $tenantId)
            ->whereJsonContains('customer_info->email', $dataSubject)
            ->update([
                'customer_info' => json_encode([
                    'name' => 'ANONYMIZED',
                    'email' => 'anonymized@example.com',
                    'phone' => 'ANONYMIZED',
                    'address' => 'ANONYMIZED',
                    'anonymized_at' => now()->toISOString()
                ]),
                'updated_at' => now()
            ]);
    }
    
    private function exportTransactionData(string $tenantId, string $dataSubject): array
    {
        return \DB::table('transactions')
            ->where('tenant_id', $tenantId)
            ->whereJsonContains('customer_info->email', $dataSubject)
            ->select([
                'id', 'order_id', 'amount', 'currency', 'status',
                'customer_info', 'created_at', 'paid_at'
            ])
            ->get()
            ->toArray();
    }
}