Skip to content

feat: Referral System with Email/SMS/WhatsApp Invites #261

@islandbitcoin

Description

@islandbitcoin

Overview

Implement a referral/invite system allowing users to invite friends via Email (SendGrid), SMS (Twilio), or WhatsApp (Twilio).

PR: #259
Branch: feat/invite
Status: Ready for review

Feature Description

User Flow

  1. Authenticated user calls createInvite with contact (email/phone) + method
  2. System validates contact format, checks rate limits, generates secure token
  3. Notification sent via SendGrid (email) or Twilio (SMS/WhatsApp) with Firebase Dynamic Link
  4. New user (within 24h of account creation) calls redeemInvite with token
  5. System validates token, checks contact match, marks invite as accepted

Key Components

Public API:

  • createInvite mutation - Create and send invite
  • redeemInvite mutation - Redeem invite as new user
  • invitePreview query - Preview invite details (unauthenticated)

Admin API:

  • inviteById query - View invite details with inviter/redeemer info
  • invitesList query - List/filter invites with pagination
  • Admin functions: revoke, extend, reset rate limits

Rate Limiting (Redis-based):

  • 10 invites/day per user
  • 3 invites/day per target contact
  • 24-hour invite expiration

Security:

  • Tokens hashed with SHA-256 (plaintext never stored)
  • Firebase Dynamic Links for mobile deep linking

Files Changed (37 files, +2117/-111)

Core:

  • src/app/invite/ - Creation, redemption, rate limiting, queries
  • src/domain/invite/ - Domain types and validation
  • src/services/mongoose/models/invite.ts - MongoDB schema
  • src/services/notification/index.ts - Twilio/SendGrid service
  • src/services/notifications/invite.ts - Invite notifications

GraphQL:

  • src/graphql/public/root/mutation/create-invite.ts
  • src/graphql/public/root/mutation/redeem-invite.ts
  • src/graphql/public/root/query/invite-preview.ts
  • src/graphql/admin/root/query/invite-*.ts

Known Issues / Follow-up Work

🔴 HIGH PRIORITY

  • Dead Code / Duplicate Logic

    • Location: src/app/invite/redeem-invite.ts vs GraphQL mutation
    • Issue: Two implementations of redemption logic; app layer appears unused
    • Action: Remove src/app/invite/redeem-invite.ts or refactor mutation to use it
  • Token Fragment Logged

    • Location: src/graphql/public/root/mutation/redeem-invite.ts line 156
    • Issue: First 8 chars of token logged in error cases
    • Risk: Reduces entropy for brute-force if logs compromised
    • Action: Log invite ID or token hash instead

🟡 MEDIUM PRIORITY

  • Email Validation Deferred (Documented in code)

  • Type Casting Hack

    • Location: src/app/invite/rate-limits.ts line 21
    • Issue: contact as IpAddress - incorrect type cast
    • Fix: Create proper branded type for rate limit keys
  • WhatsApp Template Incomplete

    • Location: src/services/notification/index.ts lines 176-189
    • Issue: TWILIO_WHATSAPP_TEMPLATE_SID env var not in schema
    • Impact: Template messages may fail in production
  • No Transaction Boundary

    • Location: redeem-invite.ts lines 128-132
    • Issue: Status update not in transaction; concurrent requests could cause issues
  • Status Transition Edge Case

    • Location: src/app/admin/invite.ts line 61
    • Issue: Extending expired invite resets to PENDING (was SENT)
    • Impact: Loses notification history

🟢 LOW PRIORITY

  • Undocumented GraphQL complexity (120)
  • Hardcoded fallback URLs (https://getflash.io)
  • Invite preview returns full contact to anyone with token

Testing Status

  • TypeScript compiles for feature code
  • Integration tests (blocked by shared test infrastructure issues)

Dependencies

  • Twilio: SMS and WhatsApp delivery
  • SendGrid: Email delivery
  • Firebase Dynamic Links: Mobile deep linking (optional)
  • Depends on: feat/email-registration #212 (feat/email-registration) for email invite validation

Environment Variables Required

# Existing (Twilio)
TWILIO_ACCOUNT_SID
TWILIO_AUTH_TOKEN

# New
TWILIO_FROM              # SMS sender number
TWILIO_WHATSAPP_FROM     # WhatsApp sender number

# Optional
FIREBASE_DYNAMIC_LINK_DOMAIN
APP_INSTALL_URL
ANDROID_PACKAGE_NAME
IOS_BUNDLE_ID
TWILIO_WHATSAPP_TEMPLATE_SID  # For approved templates

Related Issues/PRs

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions