diff --git a/app/cloud.go b/app/cloud.go index 2f95b73cc3..e4f19ac46b 100644 --- a/app/cloud.go +++ b/app/cloud.go @@ -40,14 +40,40 @@ func (a *App) getSysAdminsEmailRecipients() ([]*model.User, *model.AppError) { return a.GetUsersFromProfiles(userOptions) } +func getCurrentPlanName(a *App) (string, *model.AppError) { + subscription, err := a.Cloud().GetSubscription("") + if err != nil { + return "", model.NewAppError("getCurrentPlanName", "app.cloud.get_subscription.app_error", nil, err.Error(), http.StatusInternalServerError) + } + if subscription == nil { + return "", model.NewAppError("getCurrentPlanName", "app.cloud.get_subscription.app_error", nil, "", http.StatusInternalServerError) + } + + products, err := a.Cloud().GetCloudProducts("", false) + if err != nil { + return "", model.NewAppError("getCurrentPlanName", "app.cloud.get_cloud_products.app_error", nil, err.Error(), http.StatusInternalServerError) + } + if products == nil { + return "", model.NewAppError("getCurrentPlanName", "app.cloud.get_cloud_products.app_error", nil, "", http.StatusInternalServerError) + } + + planName := getCurrentProduct(subscription.ProductID, products).Name + return planName, nil +} + func (a *App) SendPaymentFailedEmail(failedPayment *model.FailedPayment) *model.AppError { sysAdmins, err := a.getSysAdminsEmailRecipients() if err != nil { return err } + planName, err := getCurrentPlanName(a) + if err != nil { + return model.NewAppError("SendPaymentFailedEmail", "app.cloud.get_current_plan_name.app_error", nil, err.Error(), http.StatusInternalServerError) + } + for _, admin := range sysAdmins { - _, err := a.Srv().EmailService.SendPaymentFailedEmail(admin.Email, admin.Locale, failedPayment, *a.Config().ServiceSettings.SiteURL) + _, err := a.Srv().EmailService.SendPaymentFailedEmail(admin.Email, admin.Locale, failedPayment, planName, *a.Config().ServiceSettings.SiteURL) if err != nil { a.Log().Error("Error sending payment failed email", mlog.Err(err)) } @@ -69,6 +95,11 @@ func (a *App) SendDelinquencyEmail(emailToSend model.DelinquencyEmail) *model.Ap if aErr != nil { return aErr } + planName, aErr := getCurrentPlanName(a) + if aErr != nil { + return model.NewAppError("SendDelinquencyEmail", "app.cloud.get_current_plan_name.app_error", nil, aErr.Error(), http.StatusInternalServerError) + } + subscription, err := a.Cloud().GetSubscription("") if err != nil { return model.NewAppError("SendDelinquencyEmail", "app.cloud.get_subscription.app_error", nil, err.Error(), http.StatusInternalServerError) @@ -77,16 +108,6 @@ func (a *App) SendDelinquencyEmail(emailToSend model.DelinquencyEmail) *model.Ap return model.NewAppError("SendDelinquencyEmail", "app.cloud.get_subscription.app_error", nil, "", http.StatusInternalServerError) } - products, err := a.Cloud().GetCloudProducts("", false) - if err != nil { - return model.NewAppError("SendDelinquencyEmail", "app.cloud.get_cloud_products.app_error", nil, err.Error(), http.StatusInternalServerError) - } - if products == nil { - return model.NewAppError("SendDelinquencyEmail", "app.cloud.get_cloud_products.app_error", nil, "", http.StatusInternalServerError) - } - - planName := getCurrentProduct(subscription.ProductID, products).Name - if subscription.DelinquentSince == nil { return model.NewAppError("SendDelinquencyEmail", "app.cloud.get_subscription_delinquency_date.app_error", nil, "", http.StatusInternalServerError) } diff --git a/app/email/email.go b/app/email/email.go index e3928d9b9b..b9057800fa 100644 --- a/app/email/email.go +++ b/app/email/email.go @@ -943,24 +943,27 @@ func (es *Service) SendLicenseUpForRenewalEmail(email, name, locale, siteURL, re return nil } -func (es *Service) SendPaymentFailedEmail(email string, locale string, failedPayment *model.FailedPayment, siteURL string) (bool, error) { +func (es *Service) SendPaymentFailedEmail(email string, locale string, failedPayment *model.FailedPayment, planName, siteURL string) (bool, error) { T := i18n.GetUserTranslations(locale) - subject := T("api.templates.payment_failed.subject") + subject := T("api.templates.payment_failed.subject", map[string]any{"Plan": planName}) data := es.NewEmailTemplateData(locale) data.Props["SiteURL"] = siteURL data.Props["Title"] = T("api.templates.payment_failed.title") - data.Props["Info1"] = T("api.templates.payment_failed.info1", map[string]any{"CardBrand": failedPayment.CardBrand, "LastFour": failedPayment.LastFour}) - data.Props["Info2"] = T("api.templates.payment_failed.info2") - data.Props["Info3"] = T("api.templates.payment_failed.info3") - data.Props["Button"] = T("api.templates.over_limit_fix_now") + data.Props["SubTitle1"] = T("api.templates.payment_failed.info1", map[string]any{"CardBrand": failedPayment.CardBrand, "LastFour": failedPayment.LastFour}) + data.Props["SubTitle2"] = T("api.templates.payment_failed.info2") + data.Props["FailedReason"] = failedPayment.FailureMessage + data.Props["SubTitle3"] = T("api.templates.payment_failed.info3", map[string]any{"Plan": planName}) + data.Props["QuestionTitle"] = T("api.templates.questions_footer.title") + data.Props["QuestionInfo"] = T("api.templates.questions_footer.info") + data.Props["SupportEmail"] = *es.config().SupportSettings.SupportEmail + data.Props["Button"] = T("api.templates.delinquency_45.button") + data.Props["IncludeSecondaryActionButton"] = false data.Props["EmailUs"] = T("api.templates.email_us_anytime_at") data.Props["Footer"] = T("api.templates.copyright") - data.Props["FailedReason"] = failedPayment.FailureMessage - body, err := es.templatesContainer.RenderToString("payment_failed_body", data) if err != nil { return false, err diff --git a/app/email/mocks/ServiceInterface.go b/app/email/mocks/ServiceInterface.go index fc95b9ad47..dbcabb1146 100644 --- a/app/email/mocks/ServiceInterface.go +++ b/app/email/mocks/ServiceInterface.go @@ -463,20 +463,20 @@ func (_m *ServiceInterface) SendPasswordResetEmail(_a0 string, token *model.Toke return r0, r1 } -// SendPaymentFailedEmail provides a mock function with given fields: _a0, locale, failedPayment, siteURL -func (_m *ServiceInterface) SendPaymentFailedEmail(_a0 string, locale string, failedPayment *model.FailedPayment, siteURL string) (bool, error) { - ret := _m.Called(_a0, locale, failedPayment, siteURL) +// SendPaymentFailedEmail provides a mock function with given fields: _a0, locale, failedPayment, planName, siteURL +func (_m *ServiceInterface) SendPaymentFailedEmail(_a0 string, locale string, failedPayment *model.FailedPayment, planName string, siteURL string) (bool, error) { + ret := _m.Called(_a0, locale, failedPayment, planName, siteURL) var r0 bool - if rf, ok := ret.Get(0).(func(string, string, *model.FailedPayment, string) bool); ok { - r0 = rf(_a0, locale, failedPayment, siteURL) + if rf, ok := ret.Get(0).(func(string, string, *model.FailedPayment, string, string) bool); ok { + r0 = rf(_a0, locale, failedPayment, planName, siteURL) } else { r0 = ret.Get(0).(bool) } var r1 error - if rf, ok := ret.Get(1).(func(string, string, *model.FailedPayment, string) error); ok { - r1 = rf(_a0, locale, failedPayment, siteURL) + if rf, ok := ret.Get(1).(func(string, string, *model.FailedPayment, string, string) error); ok { + r1 = rf(_a0, locale, failedPayment, planName, siteURL) } else { r1 = ret.Error(1) } diff --git a/app/email/service.go b/app/email/service.go index 78a7232e52..135991692e 100644 --- a/app/email/service.go +++ b/app/email/service.go @@ -142,7 +142,7 @@ type ServiceInterface interface { SendNotificationMail(to, subject, htmlBody string) error SendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, messageID string, inReplyTo string, references string) error SendLicenseUpForRenewalEmail(email, name, locale, siteURL, renewalLink string, daysToExpiration int) error - SendPaymentFailedEmail(email string, locale string, failedPayment *model.FailedPayment, siteURL string) (bool, error) + SendPaymentFailedEmail(email string, locale string, failedPayment *model.FailedPayment, planName, siteURL string) (bool, error) // Cloud delinquency email sequence SendDelinquencyEmail7(email, locale, siteURL, planName string) error SendDelinquencyEmail14(email, locale, siteURL, planName string) error diff --git a/i18n/en.json b/i18n/en.json index 4e59009f9a..abf4c2280a 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3579,10 +3579,6 @@ "id": "api.templates.mfa_deactivated_body.title", "translation": "Multi-factor authentication was removed" }, - { - "id": "api.templates.over_limit_fix_now", - "translation": "Fix Now" - }, { "id": "api.templates.password_change_body.info", "translation": "Your password has been updated for {{.TeamDisplayName}} on {{ .TeamURL }} by {{.Method}}." @@ -3605,15 +3601,15 @@ }, { "id": "api.templates.payment_failed.info3", - "translation": "To ensure uninterrupted subscription to Mattermost Cloud, please either contact your financial institution to fix the underlying problem or update your payment information. Once payment information is updated, Mattermost will attempt to settle any outstanding balance." + "translation": "To ensure uninterrupted access to Mattermost {{.Plan}}, please either contact your financial institution to fix the underlying problem or update your payment information. Once payment information is updated, Mattermost will attempt to settle any outstanding balance." }, { "id": "api.templates.payment_failed.subject", - "translation": "Action required: Payment failed for Mattermost Cloud" + "translation": "Action required: Payment failed for Mattermost {{.Plan}}" }, { "id": "api.templates.payment_failed.title", - "translation": "Failed Payment" + "translation": "The payment wasn't successful" }, { "id": "api.templates.payment_failed_no_card.button", @@ -4791,6 +4787,10 @@ "id": "app.cloud.get_cloud_products.app_error", "translation": "Couldn't retrieve cloud products" }, + { + "id": "app.cloud.get_current_plan_name.app_error", + "translation": "Unable to get current plan name" + }, { "id": "app.cloud.get_subscription.app_error", "translation": "Couldn't retrieve cloud subscription" diff --git a/templates/cloud_45_day_arrears.html b/templates/cloud_45_day_arrears.html index 97df400064..03d9c87c94 100644 --- a/templates/cloud_45_day_arrears.html +++ b/templates/cloud_45_day_arrears.html @@ -418,7 +418,7 @@ diff --git a/templates/cloud_90_day_arrears.html b/templates/cloud_90_day_arrears.html index 5832fd1538..6da66a6dca 100644 --- a/templates/cloud_90_day_arrears.html +++ b/templates/cloud_90_day_arrears.html @@ -418,7 +418,7 @@
- + {{.Props.Button}}
diff --git a/templates/partials/cloud_title_3subtitles_button.mjml b/templates/partials/cloud_title_3subtitles_button.mjml index 6e41aef92f..23681ab9e3 100644 --- a/templates/partials/cloud_title_3subtitles_button.mjml +++ b/templates/partials/cloud_title_3subtitles_button.mjml @@ -12,7 +12,7 @@ {{.Props.SubTitle3}} - + {{.Props.Button}} {{if .IncludeSecondaryActionButton}} diff --git a/templates/payment_failed_body.html b/templates/payment_failed_body.html index b9d587e5d2..7cff2a096c 100644 --- a/templates/payment_failed_body.html +++ b/templates/payment_failed_body.html @@ -1,95 +1,600 @@ {{define "payment_failed_body"}} - - -
- + {{.Props.Button}}
- - - -
- - - - -
- - - - -
- -
- - - - -
- - - - -
- - - - -
-

- {{ .Props.Title }}

-
-

- {{ .Props.Info1 }}

-

{{.Props.Info2}}
{{.Props.FailedReason}}

-

{{.Props.Info3}}

-

- {{ .Props.Button }} -

-
-
- - - - -
- - - - -
-

Questions?

-

{{ .Props.EmailUs }} feedback@mattermost.com

-
- - - - -
- - - - -
-

- {{.Props.Organization}}
- {{.Props.Footer}} -

-
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+ + + + + + +
+ +
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+
{{.Props.Title}}
+
+
{{.Props.SubTitle1}}
+
+
{{.Props.SubTitle2}}
{{.Props.FailedReason}}
+
+
{{.Props.SubTitle3}}
+
+ + + + +
+ + {{.Props.Button}} + +
+
+ + + + +
+ + {{.Props.SecondaryActionButtonText}} + +
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ +
+
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + +
+ + + + + + + + + +
+
{{.Props.QuestionTitle}}
+
+
{{.Props.QuestionInfo}} + + {{.Props.SupportEmail}} +
+
+
+
+ +
+ + + + + + +
+ + + + + + +
+

+

+ +
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + +
+ + + + + + +
+
{{.Props.Organization}} + {{.Props.FooterV2}} +
+
+
+
+ +
+
+ +
+
+ +
+ + {{end}} diff --git a/templates/payment_failed_body.mjml b/templates/payment_failed_body.mjml new file mode 100644 index 0000000000..0bc6715b72 --- /dev/null +++ b/templates/payment_failed_body.mjml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + {{.Props.Title}} + + + {{.Props.SubTitle1}} + + + {{.Props.SubTitle2}}
{{.Props.FailedReason}} +
+ + {{.Props.SubTitle3}} + + + {{.Props.Button}} + + {{if .IncludeSecondaryActionButton}} + + {{.Props.SecondaryActionButtonText}} + + {{end}} +
+
+ + + +
+
+