Skip to main content

Wallet Notifications Implementation Plan

Date: 2025-12-21 Status: Implemented (core) - backend + messaging handlers + member preferences live; web + mobile preferences wired Priority: Medium (Priority 2 enhancement from wallet epic)


1. Overview

Enable configurable wallet notifications for members, including:

  • Low balance threshold alerts
  • Transaction notifications (credit/debit)
  • Auto top-up status notifications
  • Wallet status change notifications

Delivery channels: Email, SMS, Push, WhatsApp (based on tenant/member preferences)


1.1 Implementation Status (Repo-verified)

  • Notification types + wallet payloads: messaging/notifications/src/lib/types/NotificationTypes.ts
  • Handlers wired + tests: messaging/notifications/src/notification.module.ts, messaging/notifications/src/__tests__/wallet-notifications.handler.spec.ts
  • Member preferences model: libs/prisma/scl-data/prisma/schema.prisma (MemberNotificationPreference)
  • Preferences service + portal endpoints: apps/scl/scl-backend/src/app/notification-preference.service.ts, apps/scl/scl-backend/src/app/portal-notification-preferences.controller.ts
  • Outbox polling + enqueue: apps/scl/scl-backend/src/app/wallet-outbox-notification.service.ts, apps/scl/scl-backend/src/app/wallet-notification.producer.ts
  • Mobile preferences UI: apps/scl/scl-mobile/src/app/screens/NotificationSettingsScreen.tsx

Open items

  • Tenant-level defaults model (TenantWalletNotificationConfig) implemented; no admin UI yet
  • Web member-portal preferences page implemented

2. Notification Types to Implement

2.1 New NotificationType Enum Values

Add to messaging/notifications/src/lib/types/NotificationTypes.ts:

// Wallet - Balance
WALLET_LOW_BALANCE = 'wallet_low_balance',

// Wallet - Transactions
WALLET_CREDIT_RECEIVED = 'wallet_credit_received',
WALLET_DEBIT_COMPLETED = 'wallet_debit_completed',

// Wallet - Auto Top-Up
WALLET_AUTO_TOPUP_SCHEDULED = 'wallet_auto_topup_scheduled',
WALLET_AUTO_TOPUP_SUCCEEDED = 'wallet_auto_topup_succeeded',
WALLET_AUTO_TOPUP_FAILED = 'wallet_auto_topup_failed',

// Wallet - Status
WALLET_STATUS_CHANGED = 'wallet_status_changed',

2.2 Notification Triggers

TypeTrigger PointPayload
WALLET_LOW_BALANCEAfter debit when balance < thresholdwalletId, balance, threshold, currency, memberName
WALLET_CREDIT_RECEIVEDAfter credit transaction postedwalletId, amount, reference, newBalance, currency
WALLET_DEBIT_COMPLETEDAfter debit transaction postedwalletId, amount, reference, newBalance, currency
WALLET_AUTO_TOPUP_SCHEDULEDAuto top-up job scheduledwalletId, amount, scheduledDate, currency
WALLET_AUTO_TOPUP_SUCCEEDEDAuto top-up charge succeedswalletId, amount, newBalance, currency
WALLET_AUTO_TOPUP_FAILEDAuto top-up charge failswalletId, amount, reason, failedAttempts, currency
WALLET_STATUS_CHANGEDWallet suspended/reactivated/closedwalletId, previousStatus, newStatus, reason

3. Member Notification Preferences

3.1 Database Schema

Add to libs/prisma/scl-data/prisma/schema.prisma:

model MemberNotificationPreference {
id String @id @default(uuid()) @db.Uuid
tenantId String @db.VarChar(100)
memberId String @db.Uuid

// Channel preferences
emailEnabled Boolean @default(true)
smsEnabled Boolean @default(false)
pushEnabled Boolean @default(true)
whatsappEnabled Boolean @default(false)

// Wallet notification toggles
walletLowBalanceEnabled Boolean @default(true)
walletLowBalanceThreshold Int? // cents, null = use tenant default
walletCreditEnabled Boolean @default(true)
walletDebitEnabled Boolean @default(false) // opt-in (high volume)
walletAutoTopUpEnabled Boolean @default(true)
walletStatusEnabled Boolean @default(true)

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

member Member @relation(fields: [memberId], references: [id])

@@unique([tenantId, memberId])
@@index([tenantId])
@@map("member_notification_preferences")
}

3.2 Tenant-Level Defaults

Add to TenantConfig or create WalletNotificationConfig:

model TenantWalletNotificationConfig {
id String @id @default(uuid()) @db.Uuid
tenantId String @unique @db.VarChar(100)

// Default low balance threshold (cents)
defaultLowBalanceThreshold Int @default(1000) // 10.00 default

// Which notifications are available for members to enable
lowBalanceAvailable Boolean @default(true)
creditAvailable Boolean @default(true)
debitAvailable Boolean @default(true)
autoTopUpAvailable Boolean @default(true)
statusAvailable Boolean @default(true)

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@map("tenant_wallet_notification_configs")
}

Status: Implemented (schema + service); add admin UI/ops flow as needed.


4. Implementation Phases

Phase 1: Core Infrastructure (✅ Implemented)

Files to create/modify:

  1. NotificationTypes.ts - Add wallet notification types
  2. MemberNotificationPreference model - Prisma schema + migration
  3. TenantWalletNotificationConfig model - Prisma schema + migration

Tasks:

  • Add 7 new NotificationType enum values
  • Add MemberNotificationPreference model in schema
  • Add TenantWalletNotificationConfig model
  • Generate Prisma client

Phase 2: Notification Handlers (✅ Implemented)

Files to create:

  1. messaging/notifications/src/lib/handlers/WalletLowBalanceHandler.ts
  2. messaging/notifications/src/lib/handlers/WalletCreditReceivedHandler.ts
  3. messaging/notifications/src/lib/handlers/WalletDebitCompletedHandler.ts
  4. messaging/notifications/src/lib/handlers/WalletAutoTopUpScheduledHandler.ts
  5. messaging/notifications/src/lib/handlers/WalletAutoTopUpSucceededHandler.ts
  6. messaging/notifications/src/lib/handlers/WalletAutoTopUpFailedHandler.ts
  7. messaging/notifications/src/lib/handlers/WalletStatusChangedHandler.ts

Handler pattern:

@Injectable()
export class WalletLowBalanceHandler implements INotificationHandler {
public readonly type = NotificationType.WALLET_LOW_BALANCE;

constructor(
@Inject(EMAIL_SENDER) private readonly email: EmailSender,
@Inject(SMS_SENDER) private readonly sms: SmsSender,
private readonly emitter: NotificationEmitter,
) {}

async handleNotification(payload: WalletLowBalancePayload): Promise<void> {
if (isTemplateRegistryEnabled()) {
return this.handleTemplateMode(payload);
}
return this.handleDirectMode(payload);
}

// Template mode: queue to BullMQ with template key
// Direct mode: send immediately with inline content
}

Phase 3: Event Integration (✅ Implemented via outbox poller)

Files to modify:

  1. apps/scl/scl-backend/src/app/admin-wallet.controller.ts

    • Publish wallet outbox events after transactions
  2. apps/scl/events-worker/src/app/wallet-auto-topup.worker.ts

    • Publish auto top-up outbox events
  3. apps/scl/scl-backend/src/app/wallet-outbox-notification.service.ts

    • Poll outbox and enqueue notifications
  4. libs/prisma/wallet-data/src/lib/wallet-outbox.repository.ts

    • Outbox publish/claim/ack helpers Integration points:
  • Transaction posted → check balance → emit LOW_BALANCE if below threshold
  • Credit posted → emit CREDIT_RECEIVED
  • Debit posted → emit DEBIT_COMPLETED
  • Auto top-up success → emit AUTO_TOPUP_SUCCEEDED
  • Auto top-up failure → emit AUTO_TOPUP_FAILED
  • Status change → emit STATUS_CHANGED

Phase 4: Preference Service (✅ Implemented)

Files to create:

  1. apps/scl/scl-backend/src/app/notification-preference.service.ts
  2. apps/scl/scl-backend/src/app/portal-notification-preferences.controller.ts
  3. apps/scl/scl-backend/src/app/__tests__/portal-notification-preferences.controller.spec.ts

Endpoints:

GET  /portal/notification-preferences
POST /portal/notification-preferences

Service methods:

interface NotificationPreferencesService {
getPreferences(tenantId: string, memberId: string): Promise<Preferences>;
updatePreferences(tenantId: string, memberId: string, update: PreferencesUpdate): Promise<Preferences>;
shouldNotify(tenantId: string, memberId: string, type: NotificationType): Promise<NotifyConfig>;
}

Phase 5: Frontend UI (✅ Implemented)

Implemented (mobile):

  • apps/scl/scl-mobile/src/app/screens/NotificationSettingsScreen.tsx

Implemented (web member portal):

  • libs/scl/ui/web-pages/src/lib/member-portal/NotificationPreferencesPage.tsx
  • libs/scl/ui/web-pages/src/lib/member-portal/__tests__/NotificationPreferencesPage.spec.tsx

UI components:

  • Channel toggles (Email, SMS, Push, WhatsApp)
  • Wallet notification toggles with descriptions
  • Low balance threshold input (with currency)
  • Save/reset buttons

5. Template Requirements

5.1 Email Templates

Template KeySubjectVariables
wallet_low_balance"Low balance alert: {currency} {balance}"memberName, balance, threshold, currency, topUpUrl
wallet_credit_received"Funds received: {currency} {amount}"memberName, amount, reference, newBalance, currency
wallet_debit_completed"Transaction complete: {currency} {amount}"memberName, amount, reference, newBalance, currency
wallet_auto_topup_succeeded"Auto top-up complete: {currency} {amount}"memberName, amount, newBalance, currency
wallet_auto_topup_failed"Auto top-up failed"memberName, amount, reason, manageUrl
wallet_status_changed"Wallet status update"memberName, previousStatus, newStatus, reason

5.2 SMS Templates

Shorter versions for SMS channel (max 160 chars).

5.3 Push Templates

Title + body format for mobile push notifications.


6. Testing Strategy

6.1 Unit Tests

  • Handler tests (mock email/sms senders)
  • Preference service tests
  • Controller endpoint tests

6.2 Integration Tests

  • Full notification flow: transaction → handler → queue → delivery
  • Preference check before sending

6.3 E2E Tests

  • Member enables low balance alerts → receives notification

7. File Summary

New Files

FilePurpose
messaging/notifications/src/lib/handlers/WalletLowBalanceHandler.tsLow balance handler
messaging/notifications/src/lib/handlers/WalletCreditReceivedHandler.tsCredit handler
messaging/notifications/src/lib/handlers/WalletDebitCompletedHandler.tsDebit handler
messaging/notifications/src/lib/handlers/WalletAutoTopUpScheduledHandler.tsAuto top-up scheduled
messaging/notifications/src/lib/handlers/WalletAutoTopUpSucceededHandler.tsAuto top-up success
messaging/notifications/src/lib/handlers/WalletAutoTopUpFailedHandler.tsAuto top-up failure
messaging/notifications/src/lib/handlers/WalletStatusChangedHandler.tsStatus change
apps/scl/scl-backend/src/app/notification-preference.service.tsPreference service
apps/scl/scl-backend/src/app/portal-notification-preferences.controller.tsPortal endpoints
apps/scl/scl-backend/src/app/wallet-outbox-notification.service.tsOutbox polling + notification enqueue
apps/scl/scl-backend/src/app/wallet-notification.producer.tsNotification queue producer
apps/scl/scl-mobile/src/app/screens/NotificationSettingsScreen.tsxMobile preference UI
libs/scl/ui/web-pages/src/lib/member-portal/NotificationPreferencesPage.tsxPreference UI
libs/prisma/scl-data/prisma/migrations/Schema migration (included in base schema)

Modified Files

FileChanges
messaging/notifications/src/lib/types/NotificationTypes.tsAdd 7 wallet types
messaging/notifications/src/lib/handlers/index.tsExport wallet handlers
messaging/notifications/src/notification.module.tsRegister wallet handlers
apps/scl/events-worker/src/app/wallet-auto-topup.worker.tsEmit notifications
apps/scl/scl-backend/src/app/admin-wallet.controller.tsEmit transaction notifications
libs/prisma/scl-data/prisma/schema.prismaAdd preference models

8. Rollout Plan

  1. Phase 1: Deploy schema + handlers (notifications disabled by default)
  2. Phase 2: Enable for beta tenants, monitor delivery
  3. Phase 3: Add preference UI to member portal
  4. Phase 4: Enable tenant-wide configuration

9. Success Metrics

MetricTarget
Notification delivery rate> 99%
Average delivery latency< 30s
Member opt-in rate (low balance)> 50%
Auto top-up failure notification CTR> 20%

Last updated: 2025-12-29