[MM-48472]: Update the Upgrade Confirmation Email for yearly subscriptions (#21766)
Этот коммит содержится в:
@@ -136,9 +136,16 @@ func changeSubscription(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
product, err := c.App.Cloud().GetCloudProduct(c.AppContext.Session().UserId, subscriptionChange.ProductID)
|
||||||
|
if err != nil || product == nil {
|
||||||
|
c.Logger.Error("Error finding the new cloud product", mlog.Err(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
isYearly := product.IsYearly()
|
||||||
|
|
||||||
// Log failures for purchase confirmation email, but don't show an error to the user so as not to confuse them
|
// Log failures for purchase confirmation email, but don't show an error to the user so as not to confuse them
|
||||||
// At this point, the upgrade is complete.
|
// At this point, the upgrade is complete.
|
||||||
if appErr := c.App.SendUpgradeConfirmationEmail(); appErr != nil {
|
if appErr := c.App.SendUpgradeConfirmationEmail(isYearly); appErr != nil {
|
||||||
c.Logger.Error("Error sending purchase confirmation email", mlog.Err(appErr))
|
c.Logger.Error("Error sending purchase confirmation email", mlog.Err(appErr))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,7 +643,26 @@ func handleCWSWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
case model.EventTypeSendUpgradeConfirmationEmail:
|
case model.EventTypeSendUpgradeConfirmationEmail:
|
||||||
if nErr := c.App.SendUpgradeConfirmationEmail(); nErr != nil {
|
|
||||||
|
// isYearly determines whether to send the yearly or monthly Upgrade email
|
||||||
|
isYearly := false
|
||||||
|
if event.Subscription != nil && event.CloudWorkspaceOwner != nil {
|
||||||
|
user, appErr := c.App.GetUserByUsername(event.CloudWorkspaceOwner.UserName)
|
||||||
|
if appErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.handleCWSWebhook", appErr.Id, nil, appErr.Error(), appErr.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current cloud product to determine whether it's a monthly or yearly product
|
||||||
|
product, err := c.App.Cloud().GetCloudProduct(user.Id, event.Subscription.ProductID)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.handleCWSWebhook", "api.cloud.request_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isYearly = product.IsYearly()
|
||||||
|
}
|
||||||
|
|
||||||
|
if nErr := c.App.SendUpgradeConfirmationEmail(isYearly); nErr != nil {
|
||||||
c.Err = nErr
|
c.Err = nErr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1038,7 +1038,7 @@ type AppIface interface {
|
|||||||
SendPasswordReset(email string, siteURL string) (bool, *model.AppError)
|
SendPasswordReset(email string, siteURL string) (bool, *model.AppError)
|
||||||
SendPaymentFailedEmail(failedPayment *model.FailedPayment) *model.AppError
|
SendPaymentFailedEmail(failedPayment *model.FailedPayment) *model.AppError
|
||||||
SendTestPushNotification(deviceID string) string
|
SendTestPushNotification(deviceID string) string
|
||||||
SendUpgradeConfirmationEmail() *model.AppError
|
SendUpgradeConfirmationEmail(isYearly bool) *model.AppError
|
||||||
ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string)
|
ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string)
|
||||||
SessionHasPermissionTo(session model.Session, permission *model.Permission) bool
|
SessionHasPermissionTo(session model.Session, permission *model.Permission) bool
|
||||||
SessionHasPermissionToAny(session model.Session, permissions []*model.Permission) bool
|
SessionHasPermissionToAny(session model.Session, permissions []*model.Permission) bool
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ func getNextBillingDateString() string {
|
|||||||
return fmt.Sprintf("%s %d, %d", t.Month(), t.Day(), t.Year())
|
return fmt.Sprintf("%s %d, %d", t.Month(), t.Day(), t.Year())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SendUpgradeConfirmationEmail() *model.AppError {
|
func (a *App) SendUpgradeConfirmationEmail(isYearly bool) *model.AppError {
|
||||||
sysAdmins, e := a.getSysAdminsEmailRecipients()
|
sysAdmins, e := a.getSysAdminsEmailRecipients()
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e
|
return e
|
||||||
@@ -200,7 +200,7 @@ func (a *App) SendUpgradeConfirmationEmail() *model.AppError {
|
|||||||
name = admin.Username
|
name = admin.Username
|
||||||
}
|
}
|
||||||
|
|
||||||
err := a.Srv().EmailService.SendCloudUpgradeConfirmationEmail(admin.Email, name, billingDate, admin.Locale, *a.Config().ServiceSettings.SiteURL, subscription.GetWorkSpaceNameFromDNS())
|
err := a.Srv().EmailService.SendCloudUpgradeConfirmationEmail(admin.Email, name, billingDate, admin.Locale, *a.Config().ServiceSettings.SiteURL, subscription.GetWorkSpaceNameFromDNS(), isYearly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Log().Error("Error sending trial ended email to", mlog.String("email", admin.Email), mlog.Err(err))
|
a.Log().Error("Error sending trial ended email to", mlog.String("email", admin.Email), mlog.Err(err))
|
||||||
countNotOks++
|
countNotOks++
|
||||||
|
|||||||
@@ -233,13 +233,13 @@ func (es *Service) SendWelcomeEmail(userID string, email string, verified bool,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *Service) SendCloudUpgradeConfirmationEmail(userEmail, name, date, locale, siteURL, workspaceName string) error {
|
func (es *Service) SendCloudUpgradeConfirmationEmail(userEmail, name, date, locale, siteURL, workspaceName string, isYearly bool) error {
|
||||||
T := i18n.GetUserTranslations(locale)
|
T := i18n.GetUserTranslations(locale)
|
||||||
subject := T("api.templates.cloud_upgrade_confirmation.subject")
|
subject := T("api.templates.cloud_upgrade_confirmation.subject")
|
||||||
|
|
||||||
data := es.NewEmailTemplateData(locale)
|
data := es.NewEmailTemplateData(locale)
|
||||||
data.Props["Title"] = T("api.templates.cloud_upgrade_confirmation.title")
|
data.Props["Title"] = T("api.templates.cloud_upgrade_confirmation.title")
|
||||||
data.Props["SubTitle"] = T("api.templates.cloud_upgrade_confirmation.subtitle", map[string]any{"WorkspaceName": workspaceName, "Date": date})
|
data.Props["SubTitle"] = T("api.templates.cloud_upgrade_confirmation_monthly.subtitle", map[string]any{"WorkspaceName": workspaceName, "Date": date})
|
||||||
data.Props["SiteURL"] = siteURL
|
data.Props["SiteURL"] = siteURL
|
||||||
data.Props["ButtonURL"] = siteURL
|
data.Props["ButtonURL"] = siteURL
|
||||||
data.Props["Button"] = T("api.templates.cloud_welcome_email.button")
|
data.Props["Button"] = T("api.templates.cloud_welcome_email.button")
|
||||||
@@ -247,6 +247,12 @@ func (es *Service) SendCloudUpgradeConfirmationEmail(userEmail, name, date, loca
|
|||||||
data.Props["QuestionInfo"] = T("api.templates.questions_footer.info")
|
data.Props["QuestionInfo"] = T("api.templates.questions_footer.info")
|
||||||
data.Props["SupportEmail"] = *es.config().SupportSettings.SupportEmail
|
data.Props["SupportEmail"] = *es.config().SupportSettings.SupportEmail
|
||||||
|
|
||||||
|
if isYearly {
|
||||||
|
data.Props["SubTitle"] = T("api.templates.cloud_upgrade_confirmation_yearly.subtitle", map[string]any{"WorkspaceName": workspaceName})
|
||||||
|
data.Props["ButtonURL"] = siteURL + "/admin_console/billing/billing_history"
|
||||||
|
data.Props["Button"] = T("api.templates.cloud_welcome_email.yearly_plan_button")
|
||||||
|
}
|
||||||
|
|
||||||
body, err := es.templatesContainer.RenderToString("cloud_upgrade_confirmation", data)
|
body, err := es.templatesContainer.RenderToString("cloud_upgrade_confirmation", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ func TestSendCloudUpgradedEmail(t *testing.T) {
|
|||||||
emailTo := "testclouduser@example.com"
|
emailTo := "testclouduser@example.com"
|
||||||
emailToUsername := strings.Split(emailTo, "@")[0]
|
emailToUsername := strings.Split(emailTo, "@")[0]
|
||||||
|
|
||||||
t.Run("SendCloudUpgradedEmail", func(t *testing.T) {
|
t.Run("SendCloudMonthlyUpgradedEmail", func(t *testing.T) {
|
||||||
verifyMailbox := func(t *testing.T) {
|
verifyMailbox := func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
@@ -278,10 +278,44 @@ func TestSendCloudUpgradedEmail(t *testing.T) {
|
|||||||
require.NoError(t, err, "Could not get message from mailbox")
|
require.NoError(t, err, "Could not get message from mailbox")
|
||||||
require.Contains(t, resultsEmail.Body.Text, "You are now upgraded!", "Wrong received message %s", resultsEmail.Body.Text)
|
require.Contains(t, resultsEmail.Body.Text, "You are now upgraded!", "Wrong received message %s", resultsEmail.Body.Text)
|
||||||
require.Contains(t, resultsEmail.Body.Text, "SomeName workspace has now been upgraded", "Wrong received message %s", resultsEmail.Body.Text)
|
require.Contains(t, resultsEmail.Body.Text, "SomeName workspace has now been upgraded", "Wrong received message %s", resultsEmail.Body.Text)
|
||||||
|
require.Contains(t, resultsEmail.Body.Text, "You'll be billed from", "Wrong received message %s", resultsEmail.Body.Text)
|
||||||
|
require.Contains(t, resultsEmail.Body.Text, "Open Mattermost", "Wrong received message %s", resultsEmail.Body.Text)
|
||||||
}
|
}
|
||||||
mail.DeleteMailBox(emailTo)
|
mail.DeleteMailBox(emailTo)
|
||||||
|
|
||||||
err := th.service.SendCloudUpgradeConfirmationEmail(emailTo, emailToUsername, "June 23, 2200", th.BasicUser.Locale, "https://example.com", "SomeName")
|
// Send Update to Monthly Plan email
|
||||||
|
err := th.service.SendCloudUpgradeConfirmationEmail(emailTo, emailToUsername, "June 23, 2200", th.BasicUser.Locale, "https://example.com", "SomeName", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
verifyMailbox(t)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SendCloudYearlyUpgradedEmail", func(t *testing.T) {
|
||||||
|
verifyMailbox := func(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var resultsMailbox mail.JSONMessageHeaderInbucket
|
||||||
|
err2 := mail.RetryInbucket(5, func() error {
|
||||||
|
var err error
|
||||||
|
resultsMailbox, err = mail.GetMailBox(emailTo)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err2 != nil {
|
||||||
|
t.Skipf("No email was received, maybe due load on the server: %v", err2)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Len(t, resultsMailbox, 1)
|
||||||
|
require.Contains(t, resultsMailbox[0].To[0], emailTo, "Wrong To: recipient")
|
||||||
|
resultsEmail, err := mail.GetMessageFromMailbox(emailTo, resultsMailbox[0].ID)
|
||||||
|
require.NoError(t, err, "Could not get message from mailbox")
|
||||||
|
require.Contains(t, resultsEmail.Body.Text, "You are now upgraded!", "Wrong received message %s", resultsEmail.Body.Text)
|
||||||
|
require.Contains(t, resultsEmail.Body.Text, "SomeName workspace has now been upgraded", "Wrong received message %s", resultsEmail.Body.Text)
|
||||||
|
require.Contains(t, resultsEmail.Body.Text, "View your invoice", "Wrong received message %s", resultsEmail.Body.Text)
|
||||||
|
}
|
||||||
|
mail.DeleteMailBox(emailTo)
|
||||||
|
|
||||||
|
// Send Update to Monthly Plan email
|
||||||
|
err := th.service.SendCloudUpgradeConfirmationEmail(emailTo, emailToUsername, "June 23, 2200", th.BasicUser.Locale, "https://example.com", "SomeName", true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
verifyMailbox(t)
|
verifyMailbox(t)
|
||||||
|
|||||||
@@ -125,13 +125,13 @@ func (_m *ServiceInterface) SendChangeUsernameEmail(newUsername string, _a1 stri
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendCloudUpgradeConfirmationEmail provides a mock function with given fields: userEmail, name, trialEndDate, locale, siteURL, workspaceName
|
// SendCloudUpgradeConfirmationEmail provides a mock function with given fields: userEmail, name, trialEndDate, locale, siteURL, workspaceName, isYearly
|
||||||
func (_m *ServiceInterface) SendCloudUpgradeConfirmationEmail(userEmail string, name string, trialEndDate string, locale string, siteURL string, workspaceName string) error {
|
func (_m *ServiceInterface) SendCloudUpgradeConfirmationEmail(userEmail string, name string, trialEndDate string, locale string, siteURL string, workspaceName string, isYearly bool) error {
|
||||||
ret := _m.Called(userEmail, name, trialEndDate, locale, siteURL, workspaceName)
|
ret := _m.Called(userEmail, name, trialEndDate, locale, siteURL, workspaceName, isYearly)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
if rf, ok := ret.Get(0).(func(string, string, string, string, string, string) error); ok {
|
if rf, ok := ret.Get(0).(func(string, string, string, string, string, string, bool) error); ok {
|
||||||
r0 = rf(userEmail, name, trialEndDate, locale, siteURL, workspaceName)
|
r0 = rf(userEmail, name, trialEndDate, locale, siteURL, workspaceName, isYearly)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
r0 = ret.Error(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ type ServiceInterface interface {
|
|||||||
SendVerifyEmail(userEmail, locale, siteURL, token, redirect string) error
|
SendVerifyEmail(userEmail, locale, siteURL, token, redirect string) error
|
||||||
SendSignInChangeEmail(email, method, locale, siteURL string) error
|
SendSignInChangeEmail(email, method, locale, siteURL string) error
|
||||||
SendWelcomeEmail(userID string, email string, verified bool, disableWelcomeEmail bool, locale, siteURL, redirect string) error
|
SendWelcomeEmail(userID string, email string, verified bool, disableWelcomeEmail bool, locale, siteURL, redirect string) error
|
||||||
SendCloudUpgradeConfirmationEmail(userEmail, name, trialEndDate, locale, siteURL, workspaceName string) error
|
SendCloudUpgradeConfirmationEmail(userEmail, name, trialEndDate, locale, siteURL, workspaceName string, isYearly bool) error
|
||||||
SendCloudWelcomeEmail(userEmail, locale, teamInviteID, workSpaceName, dns, siteURL string) error
|
SendCloudWelcomeEmail(userEmail, locale, teamInviteID, workSpaceName, dns, siteURL string) error
|
||||||
SendPasswordChangeEmail(email, method, locale, siteURL string) error
|
SendPasswordChangeEmail(email, method, locale, siteURL string) error
|
||||||
SendUserAccessTokenAddedEmail(email, locale, siteURL string) error
|
SendUserAccessTokenAddedEmail(email, locale, siteURL string) error
|
||||||
|
|||||||
@@ -15546,7 +15546,7 @@ func (a *OpenTracingAppLayer) SendTestPushNotification(deviceID string) string {
|
|||||||
return resultVar0
|
return resultVar0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) SendUpgradeConfirmationEmail() *model.AppError {
|
func (a *OpenTracingAppLayer) SendUpgradeConfirmationEmail(isYearly bool) *model.AppError {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendUpgradeConfirmationEmail")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendUpgradeConfirmationEmail")
|
||||||
|
|
||||||
@@ -15558,7 +15558,7 @@ func (a *OpenTracingAppLayer) SendUpgradeConfirmationEmail() *model.AppError {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
resultVar0 := a.app.SendUpgradeConfirmationEmail()
|
resultVar0 := a.app.SendUpgradeConfirmationEmail(isYearly)
|
||||||
|
|
||||||
if resultVar0 != nil {
|
if resultVar0 != nil {
|
||||||
span.LogFields(spanlog.Error(resultVar0))
|
span.LogFields(spanlog.Error(resultVar0))
|
||||||
|
|||||||
14
i18n/en.json
14
i18n/en.json
@@ -3164,12 +3164,16 @@
|
|||||||
"translation": "Mattermost Upgrade Confirmation"
|
"translation": "Mattermost Upgrade Confirmation"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.templates.cloud_upgrade_confirmation.subtitle",
|
"id": "api.templates.cloud_upgrade_confirmation.title",
|
||||||
|
"translation": "You are now upgraded!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "api.templates.cloud_upgrade_confirmation_monthly.subtitle",
|
||||||
"translation": "Your {{.WorkspaceName}} workspace has now been upgraded. You'll be billed from {{.Date}}"
|
"translation": "Your {{.WorkspaceName}} workspace has now been upgraded. You'll be billed from {{.Date}}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.templates.cloud_upgrade_confirmation.title",
|
"id": "api.templates.cloud_upgrade_confirmation_yearly.subtitle",
|
||||||
"translation": "You are now upgraded!"
|
"translation": "Your {{.WorkspaceName}} workspace has now been upgraded."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.templates.cloud_welcome_email.add_apps_info",
|
"id": "api.templates.cloud_welcome_email.add_apps_info",
|
||||||
@@ -3239,6 +3243,10 @@
|
|||||||
"id": "api.templates.cloud_welcome_email.title",
|
"id": "api.templates.cloud_welcome_email.title",
|
||||||
"translation": "Your workspace is ready to go!"
|
"translation": "Your workspace is ready to go!"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "api.templates.cloud_welcome_email.yearly_plan_button",
|
||||||
|
"translation": "View your invoice"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "api.templates.copyright",
|
"id": "api.templates.copyright",
|
||||||
"translation": "© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301"
|
"translation": "© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301"
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user