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​