Notification service
Centralized notification delivery: email (AWS SES), templates, delivery tracking, unsubscribe handling, and processing with retries. The API is fire-and-forget (acceptance is not final delivery). See services/notification-service/README.md for module entry points.
SES SNS webhook (spec)
Endpoint: POST /notifications/webhooks/ses — raw SNS JSON body. Return 200 OK for all valid requests (including subscription confirmations and unknown message IDs). Security: @PermitAll; authenticity via SNS signature verification only.
1. SNS message handling
- Type = SubscriptionConfirmation — HTTP GET
SubscribeURLto confirm. No DB changes. Return 200. - Type = UnsubscribeConfirmation — Optional GET UnsubscribeURL or ignore. Return 200.
- Type = Notification — Verify signature (cert from
SigningCertURL, canonical string per AWS docs). Reject 4xx if invalid. ParseMessageas JSON (SES event). Extractmail.messageId,notificationType(Bounce | Complaint | Delivery). If not Bounce/Complaint/Delivery, return 200 and skip.
2. Linking to our notification
- Add NotificationRepository
findByProviderMessageId(String)(and index onnotifications.provider_message_idif needed). Look up bymail.messageId. If not found, return 200 and skip (idempotent).
3. Persisting outcome
- Map: Delivery →
DeliveryEvent.DELIVERED,NotificationStatus.DELIVERED; Bounce →DeliveryEvent.BOUNCED,NotificationStatus.BOUNCED; Complaint →DeliveryEvent.COMPLAINT,NotificationStatus.COMPLAINT. Insert DeliveryStatusRecord; update NotificationRecord.status. Idempotent where possible (e.g. same messageId + event type).
4. Parsing & DTOs
- Outer SNS envelope:
Type,Message,MessageId,SigningCertURL,SubscribeURL, etc. Inner SES event:notificationType,mail.messageId,bounce/complaint/delivery. ParseMessageas JSON; handle missing fields safely.
5. Signature verification
- Fetch cert from
SigningCertURL(HTTPS only). Build canonical string per AWS SNS docs; verify signature. 4xx on failure.
6. Config / AWS
- SES Configuration Set → SNS Topic → HTTPS subscription to this endpoint. Optional config to disable processing (e.g. LocalStack); endpoint still returns 200.
7. LocalStack
- No SNS webhooks in LocalStack. Unknown
messageIdor disabled config → 200, no DB update.
8. Testing
- Unit: signature valid/invalid, parse Bounce/Complaint/Delivery, map to domain, "notification not found". Integration: POST sample SNS payloads (SubscriptionConfirmation + Notification), assert 200 and DB updates where applicable
LocalStack (minimal)
- Start LocalStack; the notification service registers the configured from-address (default
noreply@<domainRoot>) in dev, or runawslocal ses verify-email-identityfor a customnotification.ses.from-email. Setaws.ses.endpointto LocalStack. Inspect sent emails:curl http://localhost:4566/_aws/ses. SNS webhooks not supported; use polling or no-op for webhook endpoint.
References
Updated 10 days ago