Send notifications when subscription tier changes #59

Closed
opened 2026-01-19 15:54:14 +00:00 by egullickson · 1 comment
Owner

Summary

When a user's subscription tier changes (via admin override, user upgrade/downgrade, or grace period auto-downgrade), they should receive both an email notification and an in-app notification.

Current Behavior

Tier changes occur without notifying the user.

Expected Behavior

  1. Email notification sent when tier changes with details:

    • Previous tier
    • New tier
    • Reason (admin override, user action, grace period expiration)
  2. In-app notification displayed in notification bell with same details

Implementation Notes

The notification infrastructure already exists (backend/src/features/notifications/). This requires:

  1. Extend TemplateKey type to include 'subscription_tier_change'
  2. Create migration for new email template
  3. Add sendTierChangeNotification() to NotificationsService
  4. Create in-app notification on tier change
  5. Integrate into:
    • SubscriptionsService.adminOverrideTier()
    • SubscriptionsService.upgradeSubscription()
    • SubscriptionsService.downgradeSubscription()
    • grace-period.job.ts

Acceptance Criteria

  • Email notification sent when tier changes (admin, user action, or auto-downgrade)
  • In-app notification displayed when tier changes
  • Mobile + desktop responsive validation
## Summary When a user's subscription tier changes (via admin override, user upgrade/downgrade, or grace period auto-downgrade), they should receive both an email notification and an in-app notification. ## Related - Deferred from #58 ## Current Behavior Tier changes occur without notifying the user. ## Expected Behavior 1. Email notification sent when tier changes with details: - Previous tier - New tier - Reason (admin override, user action, grace period expiration) 2. In-app notification displayed in notification bell with same details ## Implementation Notes The notification infrastructure already exists (`backend/src/features/notifications/`). This requires: 1. Extend `TemplateKey` type to include `'subscription_tier_change'` 2. Create migration for new email template 3. Add `sendTierChangeNotification()` to NotificationsService 4. Create in-app notification on tier change 5. Integrate into: - `SubscriptionsService.adminOverrideTier()` - `SubscriptionsService.upgradeSubscription()` - `SubscriptionsService.downgradeSubscription()` - `grace-period.job.ts` ## Acceptance Criteria - [ ] Email notification sent when tier changes (admin, user action, or auto-downgrade) - [ ] In-app notification displayed when tier changes - [ ] Mobile + desktop responsive validation
egullickson added the
status
backlog
type
feature
labels 2026-01-19 15:54:23 +00:00
egullickson added
status
in-progress
and removed
status
backlog
labels 2026-02-01 01:44:02 +00:00
Author
Owner

Milestone: Implementation Complete

Phase: Execution | Agent: Developer | Status: PASS

Completed Tasks

  1. Extended TemplateKey type - Added 'subscription_tier_change' to the TemplateKey union type and Zod schema in notifications.types.ts

  2. Created migration for email template - Added 007_subscription_tier_change_template.sql with:

    • Extended CHECK constraint for template_key
    • Email template with subject, plain text body, and responsive HTML
    • Variables: userName, changeType, previousTier, newTier, reason, isDowngrade, isUpgrade, vehicleLimit
  3. Added sendTierChangeNotification() to NotificationsService - New method that:

    • Sends both email and in-app notifications
    • Handles tier comparison (upgrade vs downgrade)
    • Maps reason codes to user-friendly messages
    • Logs failures without throwing (notifications are non-critical)
  4. Integrated into SubscriptionsService - Added notification calls to:

    • upgradeSubscription() - reason: user_upgrade
    • downgradeSubscription() - reason: user_downgrade
    • adminOverrideTier() - reason: admin_override
  5. Integrated into grace-period.job.ts - Sends notification on auto-downgrade:

    • reason: grace_period_expiration
    • Joins with user_profiles to get email/name
    • Non-blocking (logs errors but doesn't fail the job)

Verification

  • Type-check: PASS
  • Lint: PASS (no new errors)
  • Tests: PASS (relevant tests passing)

Files Changed

  • backend/src/features/notifications/domain/notifications.types.ts
  • backend/src/features/notifications/domain/notifications.service.ts
  • backend/src/features/notifications/migrations/007_subscription_tier_change_template.sql
  • backend/src/features/subscriptions/domain/subscriptions.service.ts
  • backend/src/features/subscriptions/jobs/grace-period.job.ts

Verdict: PASS | Next: Open PR for review

## Milestone: Implementation Complete **Phase**: Execution | **Agent**: Developer | **Status**: PASS ### Completed Tasks 1. **Extended TemplateKey type** - Added `'subscription_tier_change'` to the TemplateKey union type and Zod schema in `notifications.types.ts` 2. **Created migration for email template** - Added `007_subscription_tier_change_template.sql` with: - Extended CHECK constraint for template_key - Email template with subject, plain text body, and responsive HTML - Variables: userName, changeType, previousTier, newTier, reason, isDowngrade, isUpgrade, vehicleLimit 3. **Added sendTierChangeNotification() to NotificationsService** - New method that: - Sends both email and in-app notifications - Handles tier comparison (upgrade vs downgrade) - Maps reason codes to user-friendly messages - Logs failures without throwing (notifications are non-critical) 4. **Integrated into SubscriptionsService** - Added notification calls to: - `upgradeSubscription()` - reason: `user_upgrade` - `downgradeSubscription()` - reason: `user_downgrade` - `adminOverrideTier()` - reason: `admin_override` 5. **Integrated into grace-period.job.ts** - Sends notification on auto-downgrade: - reason: `grace_period_expiration` - Joins with user_profiles to get email/name - Non-blocking (logs errors but doesn't fail the job) ### Verification - Type-check: PASS - Lint: PASS (no new errors) - Tests: PASS (relevant tests passing) ### Files Changed - `backend/src/features/notifications/domain/notifications.types.ts` - `backend/src/features/notifications/domain/notifications.service.ts` - `backend/src/features/notifications/migrations/007_subscription_tier_change_template.sql` - `backend/src/features/subscriptions/domain/subscriptions.service.ts` - `backend/src/features/subscriptions/jobs/grace-period.job.ts` *Verdict*: PASS | *Next*: Open PR for review
egullickson added
status
review
and removed
status
in-progress
labels 2026-02-01 01:51:13 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: egullickson/motovaultpro#59