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
| Type | Trigger Point | Payload |
|---|---|---|
WALLET_LOW_BALANCE | After debit when balance < threshold | walletId, balance, threshold, currency, memberName |
WALLET_CREDIT_RECEIVED | After credit transaction posted | walletId, amount, reference, newBalance, currency |
WALLET_DEBIT_COMPLETED | After debit transaction posted | walletId, amount, reference, newBalance, currency |
WALLET_AUTO_TOPUP_SCHEDULED | Auto top-up job scheduled | walletId, amount, scheduledDate, currency |
WALLET_AUTO_TOPUP_SUCCEEDED | Auto top-up charge succeeds | walletId, amount, newBalance, currency |
WALLET_AUTO_TOPUP_FAILED | Auto top-up charge fails | walletId, amount, reason, failedAttempts, currency |
WALLET_STATUS_CHANGED | Wallet suspended/reactivated/closed | walletId, 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:
- NotificationTypes.ts - Add wallet notification types
- MemberNotificationPreference model - Prisma schema + migration
- TenantWalletNotificationConfig model - Prisma schema + migration
Tasks:
- Add 7 new NotificationType enum values
- Add
MemberNotificationPreferencemodel in schema - Add
TenantWalletNotificationConfigmodel - Generate Prisma client
Phase 2: Notification Handlers (✅ Implemented)
Files to create:
messaging/notifications/src/lib/handlers/WalletLowBalanceHandler.tsmessaging/notifications/src/lib/handlers/WalletCreditReceivedHandler.tsmessaging/notifications/src/lib/handlers/WalletDebitCompletedHandler.tsmessaging/notifications/src/lib/handlers/WalletAutoTopUpScheduledHandler.tsmessaging/notifications/src/lib/handlers/WalletAutoTopUpSucceededHandler.tsmessaging/notifications/src/lib/handlers/WalletAutoTopUpFailedHandler.tsmessaging/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:
-
apps/scl/scl-backend/src/app/admin-wallet.controller.ts- Publish wallet outbox events after transactions
-
apps/scl/events-worker/src/app/wallet-auto-topup.worker.ts- Publish auto top-up outbox events
-
apps/scl/scl-backend/src/app/wallet-outbox-notification.service.ts- Poll outbox and enqueue notifications
-
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:
apps/scl/scl-backend/src/app/notification-preference.service.tsapps/scl/scl-backend/src/app/portal-notification-preferences.controller.tsapps/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.tsxlibs/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 Key | Subject | Variables |
|---|---|---|
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
| File | Purpose |
|---|---|
messaging/notifications/src/lib/handlers/WalletLowBalanceHandler.ts | Low balance handler |
messaging/notifications/src/lib/handlers/WalletCreditReceivedHandler.ts | Credit handler |
messaging/notifications/src/lib/handlers/WalletDebitCompletedHandler.ts | Debit handler |
messaging/notifications/src/lib/handlers/WalletAutoTopUpScheduledHandler.ts | Auto top-up scheduled |
messaging/notifications/src/lib/handlers/WalletAutoTopUpSucceededHandler.ts | Auto top-up success |
messaging/notifications/src/lib/handlers/WalletAutoTopUpFailedHandler.ts | Auto top-up failure |
messaging/notifications/src/lib/handlers/WalletStatusChangedHandler.ts | Status change |
apps/scl/scl-backend/src/app/notification-preference.service.ts | Preference service |
apps/scl/scl-backend/src/app/portal-notification-preferences.controller.ts | Portal endpoints |
apps/scl/scl-backend/src/app/wallet-outbox-notification.service.ts | Outbox polling + notification enqueue |
apps/scl/scl-backend/src/app/wallet-notification.producer.ts | Notification queue producer |
apps/scl/scl-mobile/src/app/screens/NotificationSettingsScreen.tsx | Mobile preference UI |
libs/scl/ui/web-pages/src/lib/member-portal/NotificationPreferencesPage.tsx | Preference UI |
libs/prisma/scl-data/prisma/migrations/ | Schema migration (included in base schema) |
Modified Files
| File | Changes |
|---|---|
messaging/notifications/src/lib/types/NotificationTypes.ts | Add 7 wallet types |
messaging/notifications/src/lib/handlers/index.ts | Export wallet handlers |
messaging/notifications/src/notification.module.ts | Register wallet handlers |
apps/scl/events-worker/src/app/wallet-auto-topup.worker.ts | Emit notifications |
apps/scl/scl-backend/src/app/admin-wallet.controller.ts | Emit transaction notifications |
libs/prisma/scl-data/prisma/schema.prisma | Add preference models |
8. Rollout Plan
- Phase 1: Deploy schema + handlers (notifications disabled by default)
- Phase 2: Enable for beta tenants, monitor delivery
- Phase 3: Add preference UI to member portal
- Phase 4: Enable tenant-wide configuration
9. Success Metrics
| Metric | Target |
|---|---|
| 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