Skip to content

feat(backend): deliver SMS claim receipts via Twilio#139

Merged
kilodesodiq-arch merged 3 commits into
ChainForgee:mainfrom
i-sayankh:feat/sms-receipt-twilio
Jun 18, 2026
Merged

feat(backend): deliver SMS claim receipts via Twilio#139
kilodesodiq-arch merged 3 commits into
ChainForgee:mainfrom
i-sayankh:feat/sms-receipt-twilio

Conversation

@i-sayankh

Copy link
Copy Markdown
Contributor

Closes #8

Summary

ClaimsService.sendReceiptViaSMS was a TODO stub that only logged [SMS STUB], so SMS receipts were never sent to recipients. This PR wires claim receipts into the existing async notification harness and plugs in a real Twilio provider.

While investigating, two gaps surfaced — not one:

  1. The claims stub bypassed the harness entirely — it logged instead of enqueuing a job.
  2. The notification processor's send was still a mock ([Mock] Sending...), so no SMS was delivered for any caller — including verification-flow.

Both are now fixed. The existing async machinery (BullMQ queue, NotificationOutbox, 3× exponential-backoff retries, dead-letter queue, metrics) was reused as-is rather than rebuilt.

Scope is SMS only, as agreed — the email receipt stub and email mock are intentionally left untouched.

Changes

  • SMS provider abstraction (src/notifications/providers/)
    • SmsProvider interface (send(to, body)), with a DI token.
    • TwilioSmsProvider — wraps the twilio SDK; validates E.164 numbers; throws on API errors so the existing retry/DLQ path engages.
    • MockSmsProvider — logs and returns a synthetic id; selected automatically when Twilio credentials are absent (mirrors the VERIFICATION_MODE=mock idiom), so dev/test/CI need no credentials and don't flood the DLQ.
    • Module factory selects the provider based on credential presence.
  • Processor (notifications.processor.ts) routes by job type: SMS → smsProvider.send(...), email unchanged. A Twilio failure now throws → retries → DLQ → outbox failed + metric.
  • Claims wiringClaimsService.sendReceiptViaSMS is now async and enqueues via NotificationsService.sendSms per phone number, with per-number isolation so one bad number (or a Redis outage) is logged without aborting the rest or failing receipt generation. ClaimsModule imports NotificationsModule.
  • Config — adds twilio dependency and TWILIO_ACCOUNT_SID / TWILIO_AUTH_TOKEN / TWILIO_FROM_NUMBER to .env.example (empty → mock mode).

Error handling

Failure Behavior
Twilio API error throw → retry 3× exponential backoff → DLQ + outbox failed + metric
Missing Twilio credentials MockSmsProvider: logs + succeeds (no DLQ noise)
Invalid/empty phone number E.164 check; that job fails gracefully, others proceed
Redis/enqueue down (claims) caught + logged + audited; receipt response still returned

Testing

  • TwilioSmsProvider — success path, non-E.164 rejection, API-error propagation.
  • MockSmsProvider — resolves without throwing.
  • Processor — SMS jobs route through the provider, email jobs do not, provider failure propagates to the retry/DLQ path.
  • Claims — sendReceiptViaSMS enqueues per number and survives an enqueue failure while still returning the receipt.

Verification: nest build ✅ · tsc --noEmit ✅ · full unit suite 400 passed, 10 skipped ✅ · ESLint clean on touched files ✅

Notes for reviewers

  • Set TWILIO_* env vars to enable real delivery; without them the logging mock runs.
  • This also fixes verification-flow's SMS path, which shared the same mock.
  • Out of scope: email provider integration; delivery-status webhooks.

Add an SmsProvider abstraction with Twilio and mock implementations,
selected by credential presence (mirrors VERIFICATION_MODE=mock). The
notification processor now delivers SMS jobs through the provider, so a
Twilio API error throws and feeds the existing retry/DLQ/outbox path.
Email delivery remains mocked. Adds TWILIO_* env vars and unit tests.
Wire ClaimsService.sendReceiptViaSMS to NotificationsService.sendSms so
receipts are delivered asynchronously through the existing BullMQ outbox
path instead of being logged. Each phone number is enqueued independently;
enqueue failures are logged so receipt generation still succeeds. Import
NotificationsModule into ClaimsModule and cover the new path with tests.

@kilodesodiq-arch kilodesodiq-arch left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice @i-sayankh — Twilio slots in cleanly behind the existing BullMQ/DLQ infrastructure and your spec coverage (provider + processor + claims) is thorough. Merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

REPO-007 [HIGH] Implement SMS notification service (Twilio/AWS SNS)

2 participants