@@ -61,6 +61,142 @@ func SendRegistrationEmail(
6161 return nil
6262}
6363
64+ type ThankYouEmailData struct {
65+ Name string
66+ HomepageUrl string
67+ ManageSubscriptionUrl string
68+ RenewalDate string
69+ Amount string
70+ }
71+
72+ func SendThankYouEmail (
73+ toAddress string ,
74+ toName string ,
75+ renewalDate * time.Time ,
76+ amount string ,
77+ perf * perf.RequestPerf ,
78+ ) error {
79+ defer perf .StartBlock ("EMAIL" , "Thank you email" ).End ()
80+
81+ renewalDateStr := ""
82+ if renewalDate != nil {
83+ renewalDateStr = renewalDate .Format ("January 2, 2006" )
84+ }
85+
86+ b1 := perf .StartBlock ("EMAIL" , "Rendering template" )
87+ defer b1 .End ()
88+ contents , err := renderTemplate ("email_thank_you.html" , ThankYouEmailData {
89+ Name : toName ,
90+ HomepageUrl : hmnurl .BuildHomepage (),
91+ ManageSubscriptionUrl : hmnurl .BuildSubscriptionManage (),
92+ RenewalDate : renewalDateStr ,
93+ Amount : amount ,
94+ })
95+ if err != nil {
96+ return err
97+ }
98+ b1 .End ()
99+
100+ b2 := perf .StartBlock ("EMAIL" , "Sending email" )
101+ defer b2 .End ()
102+ err = sendMail (toAddress , toName , "[Handmade Software Foundation] Thank you!" , contents )
103+ if err != nil {
104+ return oops .New (err , "Failed to send email" )
105+ }
106+ b2 .End ()
107+
108+ return nil
109+ }
110+
111+ type SubscriptionCancelledEmailData struct {
112+ Name string
113+ HomepageUrl string
114+ ExpirationDate string
115+ }
116+
117+ func SendSubscriptionCancelledEmail (
118+ toAddress string ,
119+ toName string ,
120+ expirationDate * time.Time ,
121+ perf * perf.RequestPerf ,
122+ ) error {
123+ defer perf .StartBlock ("EMAIL" , "Subscription cancelled email" ).End ()
124+
125+ expirationDateStr := ""
126+ if expirationDate != nil && ! expirationDate .IsZero () {
127+ expirationDateStr = expirationDate .Format ("January 2, 2006" )
128+ }
129+
130+ b1 := perf .StartBlock ("EMAIL" , "Rendering template" )
131+ defer b1 .End ()
132+ contents , err := renderTemplate ("email_subscription_cancelled.html" , SubscriptionCancelledEmailData {
133+ Name : toName ,
134+ HomepageUrl : hmnurl .BuildHomepage (),
135+ ExpirationDate : expirationDateStr ,
136+ })
137+ if err != nil {
138+ return err
139+ }
140+ b1 .End ()
141+
142+ b2 := perf .StartBlock ("EMAIL" , "Sending email" )
143+ defer b2 .End ()
144+ err = sendMail (toAddress , toName , "[Handmade Software Foundation] Membership cancelled" , contents )
145+ if err != nil {
146+ return oops .New (err , "Failed to send email" )
147+ }
148+ b2 .End ()
149+
150+ return nil
151+ }
152+
153+ type PaymentFailedEmailData struct {
154+ Name string
155+ HomepageUrl string
156+ ManageSubscriptionUrl string
157+ Amount string
158+ NextAttemptDate string
159+ }
160+
161+ func SendPaymentFailedEmail (
162+ toAddress string ,
163+ toName string ,
164+ amount string ,
165+ nextAttemptDate * time.Time ,
166+ perf * perf.RequestPerf ,
167+ ) error {
168+ defer perf .StartBlock ("EMAIL" , "Payment failed email" ).End ()
169+
170+ nextAttemptDateStr := ""
171+ if nextAttemptDate != nil && ! nextAttemptDate .IsZero () {
172+ nextAttemptDateStr = nextAttemptDate .Format ("January 2, 2006" )
173+ }
174+
175+ b1 := perf .StartBlock ("EMAIL" , "Rendering template" )
176+ defer b1 .End ()
177+ contents , err := renderTemplate ("email_payment_failed.html" , PaymentFailedEmailData {
178+ Name : toName ,
179+ HomepageUrl : hmnurl .BuildHomepage (),
180+ ManageSubscriptionUrl : hmnurl .BuildSubscriptionManage (),
181+ Amount : amount ,
182+ NextAttemptDate : nextAttemptDateStr ,
183+ })
184+ if err != nil {
185+ return err
186+ }
187+ b1 .End ()
188+
189+ b2 := perf .StartBlock ("EMAIL" , "Sending email" )
190+ defer b2 .End ()
191+ err = sendMail (toAddress , toName , "[Handmade Software Foundation] Payment failed" , contents )
192+ if err != nil {
193+ return oops .New (err , "Failed to send email" )
194+ }
195+ b2 .End ()
196+
197+ return nil
198+ }
199+
64200type ExistingAccountEmailData struct {
65201 Name string
66202 Username string
@@ -219,6 +355,7 @@ func renderTemplate(name string, data any) ([]byte, error) {
219355 }
220356 contentString := buffer .Bytes ()
221357 contentString = bytes .ReplaceAll (contentString , []byte ("\n " ), []byte ("\r \n " ))
358+
222359 return contentString , nil
223360}
224361
@@ -236,9 +373,16 @@ func sendMail(toAddress, toName, subject string, contentHTML []byte) error {
236373 subject ,
237374 processedHTML ,
238375 )
376+
377+ // Support for passwordless authentication for Mailpit testing
378+ var auth smtp.Auth
379+ if config .Config .Email .MailerPassword != "" {
380+ auth = smtp .PlainAuth ("" , config .Config .Email .MailerUsername , config .Config .Email .MailerPassword , config .Config .Email .ServerAddress )
381+ }
382+
239383 return smtp .SendMail (
240384 fmt .Sprintf ("%s:%d" , config .Config .Email .ServerAddress , config .Config .Email .ServerPort ),
241- smtp . PlainAuth ( "" , config . Config . Email . MailerUsername , config . Config . Email . MailerPassword , config . Config . Email . ServerAddress ) ,
385+ auth ,
242386 config .Config .Email .FromAddress ,
243387 []string {toAddress },
244388 contents ,
0 commit comments