[MM-30984] Missing payment email on billing day if no CC is present (#16442)

* Adding email and scaffolding for payment failure in case where customer has not added payment method

* Adding email template

* Remove unused boolean

* Fix error

* Add Email Us verbiage

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Nick Misasi
2020-12-03 16:02:43 -05:00
коммит произвёл GitHub
родитель 9e968a9ef1
Коммит fa5a033f66
8 изменённых файлов: 180 добавлений и 2 удалений

Просмотреть файл

@@ -353,7 +353,11 @@ func handleCWSWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = nErr
return
}
case model.EventTypeFailedPaymentNoCard:
if nErr := c.App.SendNoCardPaymentFailedEmail(); nErr != nil {
c.Err = nErr
return
}
}
ReturnStatusOK(w)

Просмотреть файл

@@ -265,6 +265,8 @@ type AppIface interface {
SearchAllChannels(term string, opts model.ChannelSearchOpts) (*model.ChannelListWithTeamData, int64, *model.AppError)
// SearchAllTeams returns a team list and the total count of the results
SearchAllTeams(searchOpts *model.TeamSearch) ([]*model.Team, int64, *model.AppError)
// SendNoCardPaymentFailedEmail
SendNoCardPaymentFailedEmail() *model.AppError
// ServePluginPublicRequest serves public plugin files
// at the URL http(s)://$SITE_URL/plugins/$PLUGIN_ID/public/{anything}
ServePluginPublicRequest(w http.ResponseWriter, r *http.Request)

Просмотреть файл

@@ -75,3 +75,19 @@ func (a *App) SendPaymentFailedEmail(failedPayment *model.FailedPayment) *model.
}
return nil
}
// SendNoCardPaymentFailedEmail
func (a *App) SendNoCardPaymentFailedEmail() *model.AppError {
sysAdmins, err := a.getSysAdminsEmailRecipients()
if err != nil {
return err
}
for _, admin := range sysAdmins {
err := a.Srv().EmailService.SendNoCardPaymentFailedEmail(admin.Email, admin.Locale, *a.Config().ServiceSettings.SiteURL)
if err != nil {
a.Log().Error("Error sending payment failed email", mlog.Err(err))
}
}
return nil
}

Просмотреть файл

@@ -779,6 +779,7 @@ func (es *EmailService) SendPaymentFailedEmail(email string, locale string, fail
bodyPage.Props["Info2"] = T("api.templates.payment_failed.info2")
bodyPage.Props["Info3"] = T("api.templates.payment_failed.info3")
bodyPage.Props["Button"] = T("api.templates.over_limit_fix_now")
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
bodyPage.Props["FailedReason"] = failedPayment.FailureMessage
@@ -788,3 +789,23 @@ func (es *EmailService) SendPaymentFailedEmail(email string, locale string, fail
return true, nil
}
func (es *EmailService) SendNoCardPaymentFailedEmail(email string, locale string, siteURL string) *model.AppError {
T := utils.GetUserTranslations(locale)
subject := T("api.templates.payment_failed_no_card.subject")
bodyPage := es.newEmailTemplate("payment_failed_no_card_body", locale)
bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.payment_failed_no_card.title")
bodyPage.Props["Info1"] = T("api.templates.payment_failed_no_card.info1")
bodyPage.Props["Info3"] = T("api.templates.payment_failed_no_card.info3")
bodyPage.Props["Button"] = T("api.templates.payment_failed_no_card.button")
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return model.NewAppError("SendPaymentFailedEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Message, http.StatusInternalServerError)
}
return nil
}

Просмотреть файл

@@ -13110,6 +13110,28 @@ func (a *OpenTracingAppLayer) SendEphemeralPost(userId string, post *model.Post)
return resultVar0
}
func (a *OpenTracingAppLayer) SendNoCardPaymentFailedEmail() *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendNoCardPaymentFailedEmail")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.SendNoCardPaymentFailedEmail()
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) SendNotifications(post *model.Post, team *model.Team, channel *model.Channel, sender *model.User, parentPostList *model.PostList, setOnline bool) ([]string, error) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendNotifications")

Просмотреть файл

@@ -2882,6 +2882,26 @@
"id": "api.templates.payment_failed.title",
"translation": "Failed Payment"
},
{
"id": "api.templates.payment_failed_no_card.button",
"translation": "Pay now"
},
{
"id": "api.templates.payment_failed_no_card.info1",
"translation": "Your Mattermost Cloud invoice for the most recent billing period has been processed. However, we don't have your payment details on file."
},
{
"id": "api.templates.payment_failed_no_card.info3",
"translation": "To review your invoice and add a payment method, select Pay now."
},
{
"id": "api.templates.payment_failed_no_card.subject",
"translation": "Payment is due for your Mattermost Cloud subscription"
},
{
"id": "api.templates.payment_failed_no_card.title",
"translation": "Your Mattermost Cloud Invoice is due"
},
{
"id": "api.templates.post_body.button",
"translation": "Go To Post"

Просмотреть файл

@@ -4,7 +4,8 @@
package model
const (
EventTypeFailedPayment = "failed-payment"
EventTypeFailedPayment = "failed-payment"
EventTypeFailedPaymentNoCard = "failed-payment-no-card"
)
// Product model represents a product on the cloud system.

92
templates/payment_failed_no_card_body.html Обычный файл
Просмотреть файл

@@ -0,0 +1,92 @@
{{define "payment_failed_no_card_body"}}
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
style="margin-top: 20px; line-height: 1.7; color: #555;font-family: Arial; font-style: normal; font-weight: bold;">
<tr>
<td>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
<tr>
<td style="border: 1px solid #ddd;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td style="padding: 20px 20px 10px; text-align:left;">
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" style="opacity: 0.5"
width="130px" alt="">
</td>
</tr>
</table>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
style="border-collapse: collapse; max-width: 443px">
<tr style="width: 443px">
<td>
<table border="0" cellpadding="0" cellspacing="0"
style="padding: 20px 0 0; text-align: center; margin: 0 auto">
<tr>
<td style="padding: 0 0 64px;">
<table align="center" border="0" cellpadding="0" cellspacing="0"
width="371px">
<tr>
<td>
<h2
style="margin-bottom: 0; text-align: center; color: #3D3C40; font-weight: bold; margin-top: 10px; line-height: 32px; font-size: 28px; font-family: Arial">
{{ .Props.Title }}</h2>
</td>
</tr>
</table>
<p
style="font-weight: normal; color: #3D3C40; font-size: 16px; line-height: 24px; font-family: Arial">
{{ .Props.Info1 }}</p>
<p style="margin-top: 24px; margin-bottom: 24px; font-weight: normal; font-size: 16px; line-height: 24px; color: #3D3C40; font-family: Arial">{{.Props.Info3}}</p>
<p style="margin: 31px 0 31px">
<a href="{{.Props.SiteURL}}/admin_console/billing/subscription"
target="_blank"
style="font-weight: normal; border-radius: 4px; background: #166DE0; color: #fff;outline: none; min-width: 200px; padding: 11px 24px 11px 24px; font-size: 16px; font-family: Arial; cursor: pointer; -webkit-appearance: none;text-decoration: none; margin-top: 16px;">{{ .Props.Button }}</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
<table align="center" border="0" cellpadding="0" cellspcing="0" width="612px">
<tr>
<td style="border-bottom: 1px solid #E5E5E5"></td>
</tr>
</table>
<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%"
style="max-width: 443px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF; border-collapse: collapse;">
<tr>
<td style="font-family: Arial; padding: 20px 20px 24px 48px; text-align:left;">
<h3 style="margin-bottom: 0;">Questions?</h3>
<p style="font-weight: normal; margin-top: 0;">{{ .Props.EmailUs }} <a
href="mailto:feedback@mattermost.com">feedback@mattermost.com</a></p>
</td>
</tr>
</table>
<table align="center" border="0" cellpadding="0" cellspcing="0" width="612px">
<tr>
<td style="border-bottom: 1px solid #E5E5E5"></td>
</tr>
</table>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
style="font-family: Arial; line-height: 16px; font-size: 12px; background: #FFF;">
<tr>
<td
style="font-weight: normal; text-align: center;color: #AAA;font-size: 11px;padding-bottom: 10px;padding: 0;line-height: 16px;padding-bottom: 48px;padding-top: 4px;">
<p>
{{.Props.Organization}}<br>
{{.Props.Footer}}
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
{{end}}