wishan / docs/notifications.md

Notifications System

A mobile app for buying asset shares

Last updated: 4/16/2026GitHubWishan

Notifications System

Overview

Wishan includes a built-in notification system that supports two dispatch channels:

DispatcherDescription
emailHTML emails sent via SMTP using a cron job
in_appIn-app notifications stored in the database; optionally pushed via Firebase Cloud Messaging (FCM)

Architecture

notifications domain
├── core/
│   ├── entities.go     # Domain types (NotificationType, NotificationTemplate, Notification, etc.)
│   └── service.go      # Service interface
└── infra/
    ├── service.go      # Implementation (SMTP + Firebase)
    └── templates/
        ├── email.html  # HTML email layout (go:embed)
        └── in_app.txt  # In-app text layout (go:embed)

Clean Architecture

  • core/ – business types and service interface (no external dependencies)
  • infra/ – concrete implementation wiring SMTP, Firebase, and the database

Notification Types

Notification types are defined as a PostgreSQL enum and Go constants:

TypeDescription
draw_winnerSent when a user wins a draw
draw_startedSent when a draw the user joined becomes active
draw_cancelledSent when a draw is cancelled
draw_completedSent to all participants and admins when a draw is completed (winner selected and reveal date set); includes the reveal date
welcomeSent when a user first registers
kyc_approvedSent when a user's KYC is approved
kyc_rejectedSent when a user's KYC is rejected

Database Tables

notification_templates

Stores customizable content for each (type, dispatcher) pair.

ColumnTypeDescription
idbigserialPrimary key
typenotification_typeNotification type enum
dispatchernotification_dispatcheremail or in_app
subjecttext (nullable)Email subject line
titletextNotification title
bodytextNotification body (HTML for email, plain text for in-app)
created_attimestamptz
updated_attimestamptz

Default content for all 12 templates (6 types × 2 dispatchers) is inserted automatically by the database schema.

notifications

Stores individual notification instances sent to users.

ColumnTypeDescription
idbigserialPrimary key
user_idbigintFK to users
typenotification_type
dispatchernotification_dispatcher
titletextRendered title
bodytextRendered body
linktext (nullable)Optional deep-link URL
is_readbooleanWhether the user has read this (in-app only)
sent_attimestamptz (nullable)Set when email is sent; NULL = pending
created_attimestamptz

user_device_tokens

Stores Firebase Cloud Messaging device tokens for push notifications.

ColumnTypeDescription
idbigserialPrimary key
user_idbigintFK to users
tokentextFCM registration token
platformtextios, android, or web

Template Variables

Template bodies support Go template syntax (text/template). The following variables are substituted at send-time:

VariableDescription
{{.UserName}}The recipient's name
{{.DrawName}}The name of the relevant draw/prize

Example body with variables:

Congratulations {{.UserName}}! You have won the draw for {{.DrawName}}.

Storing a Notification

Call StoreNotification from anywhere in the application:

err := notificationsService.StoreNotification(ctx, notifications_core.StoreNotificationParams{
    UserID:     userID,
    Type:       notifications_core.NotificationTypeDrawWinner,
    Dispatcher: notifications_core.DispatcherEmail,
    Link:       &drawLink,
    Vars: map[string]string{
        "UserName": "John Doe",
        "DrawName": "Luxury Car Draw",
    },
})

This will:

  1. Look up the template for (draw_winner, email) from the database
  2. Substitute template variables in the title and body
  3. Store the rendered notification in the notifications table
  4. For in_app dispatchers: trigger a Firebase push notification (if device tokens are registered)
  5. For email dispatchers: the email is queued (pending); the cron job sends it

Email Sending (Cron Job)

The SendPendingEmails function is called by a cron job (default: every 5 minutes) to send all pending email notifications.

  • Looks up user email from users_identities where provider_id = 'email'
  • Renders the notification body into the embedded email.html layout
  • Sends via SMTP using github.com/jordan-wright/email
  • Marks the notification as sent (sent_at = NOW())

SMTP Configuration

Add to config.yaml:

smtp:
  host: "smtp.gmail.com"
  port: 587
  username: "notifications@example.com"
  password: "your-app-password"
  from: "notifications@example.com"
  from_name: "Wishan"
  enabled: true

Set enabled: false (default) to disable email sending without errors.

Cron Schedule

cron:
  send_pending_emails_schedule: "@every 5m"  # Every 5 minutes

Firebase Push Notifications (In-App)

For in-app notifications, the system optionally sends a Firebase Cloud Messaging (FCM) push notification if the user has registered device tokens.

Device Token Registration (Mobile)

POST /device-tokens
Authorization: Bearer <firebase-token>
Content-Type: application/json

{
  "token": "<FCM registration token>",
  "platform": "android"  // "ios", "android", or "web"
}
DELETE /device-tokens
Authorization: Bearer <firebase-token>
Content-Type: application/json

{
  "token": "<FCM registration token>"
}

Admin API Endpoints

MethodPathPermissionDescription
GET/admin/notificationsread:notificationsList all notifications (paginated)
GET/admin/notifications/templatesread:notificationsGet all notification templates
PUT/admin/notifications/templates/:idwrite:notificationsUpdate a notification template

Mobile API Endpoints

MethodPathDescription
GET/notifications?page=1&size=20Get user's in-app notifications
PATCH/notifications/:id/readMark a notification as read
PATCH/notifications/read-allMark all notifications as read
POST/device-tokensRegister FCM device token
DELETE/device-tokensRemove FCM device token

Admin Panel (custom_admin)

The admin panel includes a Notifications page at /admin/notifications where admin users can:

  1. View all notification templates grouped by dispatcher (Email / In-App)
  2. Edit template content (subject, title, body)
  3. Use template variables ({{.UserName}}, {{.DrawName}}) in the content

Navigate to Notifications in the admin sidebar.


Permissions

RolePermissions
AdminRead + Write notifications
EditorRead + Write notifications
ReviewerRead notifications only