Skip to main content

Email Notifications

This document describes all email notifications sent by the donation platform to donors, and how staff members are notified.

Email Infrastructure

Mailer Class

All donor emails are sent by Donations::DonorMailer, which uses the newsletter layout and exposes three methods:

  • thank_you(transaction)
  • charge_failed(event_hash)
  • subscription_canceled(subscription)

The sender address comes from the EMAIL_SENDER environment variable (set on ApplicationMailer as default from).

Email Service Provider

  • SendGrid is used for email delivery
  • Configured via Action Mailer
  • Emails sent asynchronously via Sidekiq (deliver_later, default Active Job queue)

Localization

All emails are localized based on the donor's preferred language, resolved from:

  1. Transaction/notification metadata (primary)
  2. Stripe customer metadata
  3. Browser locale (initial donation)
  4. Application default (fallback)

Subjects are resolved via I18n (donations.donor_mailer.<action>.subject), translated in all eight supported locales. The HTML templates are single files — locale-specific text comes entirely from the translation files, not from per-locale template variants.

URL Configuration

Email links point to the donations subdomain (DONATIONS_HOST / DONATIONS_SUBDOMAIN), e.g. the subscription management link uses the authenticated /authenticate?authToken=... URL described below.

Donor Emails

1. Thank You Email

Sent when: Successful donation (single or recurring charge)

Trigger: Stripe charge.succeeded webhook → Donations::ProcessStripeEventJob creates a Donations::Transaction; an after_commit callback on the transaction delivers the email.

Mailer Method: Donations::DonorMailer.thank_you(transaction)

Email Content:

  • Subject: Localized "Thank you for your donation"
  • Donor's name (first_name + last_name)
  • Donation amount and currency
  • Payment date
  • Transaction ID (for reference)
  • Link to manage subscription (if recurring)
  • Tax deduction information (if applicable)

2. Charge Failed Email

Sent when: Payment attempt fails (recurring subscription)

Trigger: Stripe charge.failed webhook → Donations::ProcessStripeEventJob#process_failed_charge. The email is not sent when the failure is classified as a fraud attempt or when the same payment intent failed more than 3 times in 30 minutes — in that case the payment intent is expired instead (see Error Handling).

Mailer Method: Donations::DonorMailer.charge_failed(event_hash)

Email Content:

  • Subject: "Payment failed for your Aleteia donation"
  • Donor's name
  • Failed amount and currency
  • Failure reason (translated): card declined, insufficient funds, card expired, etc.
  • Link to update payment method
  • Support contact information

3. Subscription Canceled Email

Sent when: Donor cancels a recurring subscription

Trigger: Donations::Subscription#cancel! (from the subscription management page) enqueues Donations::CancelStripeSubscriptionJob, which cancels the subscription in Stripe and then delivers the email.

Mailer Method: Donations::DonorMailer.subscription_canceled(subscription)

Email Content:

  • Subject: "Your Aleteia subscription has been canceled"
  • Donor's name
  • Subscription details (amount, frequency)
  • Cancellation date
  • Invitation to donate again in the future
  • Thank you message

Staff Notifications

There are no staff emails: staff members are notified via Slack.

Every donation event (new donation, new subscription, cancellation, failed charge) creates a Donations::Notification record; an after_commit callback enqueues Donations::SlackNotificationJob, which posts to the channel configured in Setting.donations_slack_channel using the webhook from Setting.donations_slack_webhook_url.

Suspected fraud does not produce an alert notification: the payment intent is expired automatically (Donations::ExpirePaymentIntentJob with reason fraudulent) and a warning is logged. See Error Handling for the detection criteria.

Email Sending Strategy

Asynchronous Delivery

All emails are sent via deliver_later on the default Active Job queue; the webhook processing jobs that trigger them run on the payments queue.

Benefits:

  • Non-blocking (doesn't delay webhook response)
  • Automatic retry on failure
  • Better performance

Retry Logic

If email delivery fails:

  • Sidekiq automatically retries with exponential backoff
  • Max retries: 25 (default)
  • Failures logged to error tracking (Rollbar)

Email Templates

Layout

All donation emails use the newsletter layout. Templates live under app/views/donations/donor_mailer/ (one HTML template per email, shared across locales).

Inline CSS

Email styles are inlined for better email client compatibility by the premailer-rails gem, which processes stylesheets referenced via stylesheet_link_tag automatically at delivery time.

Previewing Emails (in development)

Mailer previews are defined in spec/mailers/previews/donations/donor_preview.rb and cover all three emails, including locale variants and single vs recurring donations.

Access at: http://localhost:3000/rails/mailers/donations/donor

RSpec Tests

Mailer specs live in spec/mailers/donations/donor_mailer_spec.rb and cover headers, content, localization, and the subscription management links.

Emails include authenticated links for donors to manage subscriptions:

Authentication Token:

The token is the donor's signed GlobalID (Donations::Donor#auth_token, generated with to_signed_global_id(expires_in: nil)):

  • Cryptographically signed — cannot be forged or tampered with
  • Identifies the donor uniquely
  • Never expires (the donor can reuse the same link forever)

Link Example:

https://subscriptions.aleteia.org/authenticate?authToken=BAh7CEk...

Clicking the link authenticates the donor and shows the subscription management page.

Email Deliverability

Best Practices Implemented

  1. SPF/DKIM/DMARC: Configured at DNS level via SendGrid
  2. Unsubscribe Links: Not included (transactional emails)
  3. From Address: Uses verified domain ([email protected])
  4. Reply-To: Can be configured for support email
  5. List-Unsubscribe Header: Not set (transactional, not marketing)

Bounce Handling

SendGrid handles bounces and sends webhook events:

  • Hard bounces: Donor marked as invalid
  • Soft bounces: Retry automatically
  • Spam reports: Logged and monitored

Monitoring & Analytics

Email Delivery Metrics

Track via SendGrid:

  • Delivery rate
  • Open rate (if tracking enabled)
  • Click rate
  • Bounce rate
  • Spam complaints

Error Tracking

Email failures are tracked in Rollbar:

  • Mailer exceptions
  • Template rendering errors
  • SendGrid API errors

Next Steps