//! Purchase, subscription, and Fan+ programme email templates. use crate::email::{Audience, Delivery, EmailClass, EmailClient, OperationalKind}; use crate::error::Result; impl EmailClient { /// Send a purchase confirmation email pub async fn send_purchase_confirmation( &self, to_email: &str, to_name: Option<&str>, item_title: &str, price: &str, ) -> Result<()> { let subject = "Your purchase is confirmed"; let body = format!( r"Hi{name}, Your purchase of {item} ({price}) is confirmed. You can access your purchase from your library at any time. - Makenotwork", name = crate::email::greeting(to_name), item = item_title, price = price ); self.dispatch( EmailClass::Operational(OperationalKind::PurchaseReceipt), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } /// Send a guest purchase confirmation email with download and claim links. pub async fn send_guest_purchase_confirmation( &self, to_email: &str, item_title: &str, price: &str, download_url: &str, claim_url: &str, ) -> Result<()> { let subject = "Your purchase is confirmed"; let body = format!( r"Hi, Your purchase of {item_title} ({price}) is confirmed. Download your purchase: {download_url} This link works without an account. You can download at any time. Want to keep all your purchases in one place? Create a Makenotwork account with this email address and your purchase will appear in your library automatically. Or use this link to claim it to an existing account: {claim_url} - Makenotwork", ); self.dispatch( EmailClass::Operational(OperationalKind::PurchaseReceipt), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } /// Send a subscription started email pub async fn send_subscription_started( &self, to_email: &str, to_name: Option<&str>, tier_name: &str, project_title: &str, price: &str, ) -> Result<()> { let subject = &format!("You're subscribed to {project_title}"); let body = format!( r"Hi{name}, You're now subscribed to {project} ({tier} - {price}/mo). You have access to all content included in this tier. Your subscription will renew automatically each month. - Makenotwork", name = crate::email::greeting(to_name), project = project_title, tier = tier_name, price = price ); self.dispatch( EmailClass::Operational(OperationalKind::SubscriptionBilling), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } /// Send a subscription cancelled email pub async fn send_subscription_cancelled( &self, to_email: &str, to_name: Option<&str>, tier_name: &str, project_title: &str, ) -> Result<()> { let subject = "Your subscription has been cancelled"; let body = format!( r"Hi{name}, Your subscription to {project} ({tier}) has been cancelled. You will retain access until the end of your current billing period. - Makenotwork", name = crate::email::greeting(to_name), project = project_title, tier = tier_name ); self.dispatch( EmailClass::Operational(OperationalKind::SubscriptionBilling), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } /// Send a subscription renewed email pub async fn send_subscription_renewed( &self, to_email: &str, to_name: Option<&str>, tier_name: &str, price: &str, ) -> Result<()> { let subject = "Your subscription has been renewed"; let body = format!( r"Hi{name}, Your subscription ({tier} - {price}/mo) has been renewed for another month. - Makenotwork", name = crate::email::greeting(to_name), tier = tier_name, price = price ); self.dispatch( EmailClass::Operational(OperationalKind::SubscriptionBilling), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } /// Send a Fan+ welcome email after subscription starts. pub async fn send_fan_plus_welcome(&self, to_email: &str, to_name: Option<&str>) -> Result<()> { let subject = "Welcome to Fan+"; let body = format!( r"Hi{name}, You're now a Fan+ member. Each month you'll receive a $5 credit code you can use toward any purchase on the platform. Your first credit will arrive with your next billing cycle. Thank you for supporting Makenotwork. - Makenotwork", name = crate::email::greeting(to_name), ); self.dispatch( EmailClass::Operational(OperationalKind::FanPlus), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } /// Send a Fan+ monthly credit code email. pub async fn send_fan_plus_credit( &self, to_email: &str, to_name: Option<&str>, code: &str, expiry: Option<&chrono::DateTime>, ) -> Result<()> { let subject = "Your Fan+ monthly credit"; let expiry_line = expiry .map(|d| format!("\n\nThis code expires on {}.", d.format("%B %-d, %Y"))) .unwrap_or_default(); let body = format!( r"Hi{name}, Your $5 Fan+ credit for this month is ready. Code: {code} Enter this code at checkout to apply it toward any purchase on the platform.{expiry} - Makenotwork", name = crate::email::greeting(to_name), code = code, expiry = expiry_line, ); self.dispatch( EmailClass::Operational(OperationalKind::FanPlus), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } /// Notify a creator that their backgrounded content export is ready, with a /// time-limited download link. The export ZIP is built off the request path, /// so the link arrives by email rather than inline in the dashboard. pub async fn send_content_export_ready( &self, to_email: &str, to_name: Option<&str>, download_url: &str, ) -> Result<()> { let subject = "Your content export is ready"; let body = format!( r"Hi{name}, Your content export is ready to download: {url} This link expires in one hour. If it expires before you download, start a new export from your dashboard. - Makenotwork", name = crate::email::greeting(to_name), url = download_url, ); self.dispatch( EmailClass::Operational(OperationalKind::ContentExport), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } /// Notify a creator that their content export could not be completed, so a /// silently-failed background job doesn't leave them waiting indefinitely. pub async fn send_content_export_failed( &self, to_email: &str, to_name: Option<&str>, reason: Option<&str>, ) -> Result<()> { let subject = "Your content export could not be completed"; let reason_line = reason.map(|r| format!("\n\n{r}")).unwrap_or_default(); let body = format!( r"Hi{name}, We were unable to finish preparing your content export. No changes were made to your content.{reason} Please try again from your dashboard; if it keeps failing, contact info@makenot.work. - Makenotwork", name = crate::email::greeting(to_name), reason = reason_line, ); self.dispatch( EmailClass::Operational(OperationalKind::ContentExport), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } /// Send a Fan+ cancellation email. pub async fn send_fan_plus_cancelled( &self, to_email: &str, to_name: Option<&str>, period_end: Option<&chrono::DateTime>, ) -> Result<()> { let subject = "Your Fan+ membership has been cancelled"; let access_line = period_end.map_or_else( || "Your Fan+ benefits have ended.".to_string(), |d| { format!( "You will retain your Fan+ benefits until {}.", d.format("%B %-d, %Y") ) }, ); let body = format!( r"Hi{name}, Your Fan+ membership has been cancelled. {access} Any unused credit codes remain valid until their expiry date. - Makenotwork", name = crate::email::greeting(to_name), access = access_line, ); self.dispatch( EmailClass::Operational(OperationalKind::FanPlus), Audience::Address(to_email), subject, &body, Delivery::default(), ) .await } }