(orgId, userId);
}
+
+ private bool OrgPlanForInvoiceNotifications(Organization org)
+ {
+ switch(org.PlanType)
+ {
+ case Core.Enums.PlanType.FamiliesAnnually:
+ case Core.Enums.PlanType.TeamsAnnually:
+ case Core.Enums.PlanType.EnterpriseAnnually:
+ return true;
+ default:
+ return false;
+ }
+ }
}
}
diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj
index ad08ad5b6a..bfb55a8465 100644
--- a/src/Core/Core.csproj
+++ b/src/Core/Core.csproj
@@ -7,7 +7,10 @@
+
+
+
@@ -18,6 +21,7 @@
+
@@ -26,6 +30,8 @@
+
+
diff --git a/src/Core/MailTemplates/Markdown/InvoiceUpcoming.md b/src/Core/MailTemplates/Markdown/InvoiceUpcoming.md
new file mode 100644
index 0000000000..aa9e671ea5
--- /dev/null
+++ b/src/Core/MailTemplates/Markdown/InvoiceUpcoming.md
@@ -0,0 +1,5 @@
+This is a reminder that your Bitwarden subscription is due for renewal soon. Your payment method on file will be charged for **{{amountDue}}** on **{{dueDate}}**.
+
+To avoid any interruption in service, please ensure that your payment method on file is up to date and can be charged for the above amount. You can manage your subscription and payment method by logging into the web vault at <{{vaultUrl}}>. Once logged in, navigate to the Billing page for your account.
+
+If you have any questions or problems, please feel free to email us at hello@bitwarden.com.
diff --git a/src/Core/MailTemplates/Razor/InvoiceUpcoming.cshtml b/src/Core/MailTemplates/Razor/InvoiceUpcoming.cshtml
new file mode 100644
index 0000000000..7872553dea
--- /dev/null
+++ b/src/Core/MailTemplates/Razor/InvoiceUpcoming.cshtml
@@ -0,0 +1,35 @@
+@model Bit.Core.Models.Mail.InvoiceUpcomingViewModel
+@{
+ Layout = "_BasicMailLayout";
+}
+
+ This is a reminder that your Bitwarden subscription is due for renewal soon.
+ Your payment method on file will be charged for @Model.AmountDue.ToString("C") on
+ @Model.DueDate.ToString("MMM dd, yyyy").
+
+
+ Summary Of Charges
+ @foreach(var item in Model.Items)
+ {
+ @:- @item
+ }
+
+
+
+ To avoid any interruption in service, please ensure that your payment method
+ on file is up to date and can be charged for the above amount. You can manage your
+ subscription and payment method by logging into the web vault at
+ @Model.WebVaultUrl. Once logged in,
+ navigate to the Billing page for your account.
+
+
+@if(Model.MentionInvoices)
+{
+
+ Invoices for your payments can also be downloaded from Billing page for your account.
+
+}
+
+ If you have any questions or problems, please feel free to email us at
+ hello@bitwarden.com.
+
diff --git a/src/Core/MailTemplates/Razor/InvoiceUpcoming.text.cshtml b/src/Core/MailTemplates/Razor/InvoiceUpcoming.text.cshtml
new file mode 100644
index 0000000000..e092f66c11
--- /dev/null
+++ b/src/Core/MailTemplates/Razor/InvoiceUpcoming.text.cshtml
@@ -0,0 +1,27 @@
+@model Bit.Core.Models.Mail.InvoiceUpcomingViewModel
+@{
+ Layout = "_BasicMailLayout.text";
+}
+This is a reminder that your Bitwarden subscription is due for renewal soon.
+Your payment method on file will be charged for @Model.AmountDue.ToString("C") on @Model.DueDate.ToString("MMM dd, yyyy").
+
+Summary Of Charges
+------------------
+@foreach(var item in Model.Items)
+{
+@:- @item
+}
+
+To avoid any interruption in service, please ensure that your payment method
+on file is up to date and can be charged for the above amount. You can manage your
+subscription and payment method by logging into the web vault at <@Model.WebVaultUrl>.
+Once logged in, navigate to the Billing page for your account.
+@if(Model.MentionInvoices)
+{
+@:
+@: Invoices for your payments can also be downloaded from Billing page for your
+@: account.
+}
+
+If you have any questions or problems, please feel free to email us at
+hello@bitwarden.com.
diff --git a/src/Core/Models/Api/Response/AuthTokenResponseModel.cs b/src/Core/Models/Api/Response/AuthTokenResponseModel.cs
deleted file mode 100644
index b94b7d0e1e..0000000000
--- a/src/Core/Models/Api/Response/AuthTokenResponseModel.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-using Bit.Core.Models.Table;
-
-namespace Bit.Core.Models.Api
-{
- public class AuthTokenResponseModel : ResponseModel
- {
- public AuthTokenResponseModel(string token, User user = null)
- : base("authToken")
- {
- Token = token;
- Profile = user == null ? null : new ProfileResponseModel(user, null);
- }
-
- public string Token { get; set; }
- public ProfileResponseModel Profile { get; set; }
- }
-}
diff --git a/src/Core/Models/Mail/InvoiceUpcomingViewModel.cs b/src/Core/Models/Mail/InvoiceUpcomingViewModel.cs
new file mode 100644
index 0000000000..1a12760bf8
--- /dev/null
+++ b/src/Core/Models/Mail/InvoiceUpcomingViewModel.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+
+namespace Bit.Core.Models.Mail
+{
+ public class InvoiceUpcomingViewModel : BaseMailModel
+ {
+ public decimal AmountDue { get; set; }
+ public DateTime DueDate { get; set; }
+ public List Items { get; set; }
+ public bool MentionInvoices { get; set; }
+ }
+}
diff --git a/src/Core/Models/Mail/MailMessage.cs b/src/Core/Models/Mail/MailMessage.cs
index 82e4882543..d94cd04acf 100644
--- a/src/Core/Models/Mail/MailMessage.cs
+++ b/src/Core/Models/Mail/MailMessage.cs
@@ -6,6 +6,7 @@ namespace Bit.Core.Models.Mail
{
public string Subject { get; set; }
public IEnumerable ToEmails { get; set; }
+ public IEnumerable BccEmails { get; set; }
public string HtmlContent { get; set; }
public string TextContent { get; set; }
public IDictionary MetaData { get; set; }
diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs
index c2086082fd..9a2a2aefeb 100644
--- a/src/Core/Services/IMailService.cs
+++ b/src/Core/Services/IMailService.cs
@@ -19,5 +19,6 @@ namespace Bit.Core.Services
Task SendOrganizationAcceptedEmailAsync(string organizationName, string userEmail, IEnumerable adminEmails);
Task SendOrganizationConfirmedEmailAsync(string organizationName, string email);
Task SendPasswordlessSignInAsync(string returnUrl, string token, string email);
+ Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate, List items, bool mentionInvoices);
}
}
diff --git a/src/Core/Services/Implementations/BackupMailService.cs b/src/Core/Services/Implementations/BackupMailService.cs
index b4359c44a3..b5c86ab090 100644
--- a/src/Core/Services/Implementations/BackupMailService.cs
+++ b/src/Core/Services/Implementations/BackupMailService.cs
@@ -179,6 +179,20 @@ namespace Bit.Core.Services
}
}
+ public async Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate,
+ List items, bool mentionInvoices)
+ {
+ try
+ {
+ await _primaryMailService.SendInvoiceUpcomingAsync(email, amount, dueDate, items, mentionInvoices);
+ }
+ catch(Exception e)
+ {
+ LogError(e);
+ await _backupMailService.SendInvoiceUpcomingAsync(email, amount, dueDate, items, mentionInvoices);
+ }
+ }
+
private void LogError(Exception e)
{
_logger.LogError(e, "Error sending mail with primary service, using backup.");
diff --git a/src/Core/Services/Implementations/MarkdownMailService.cs b/src/Core/Services/Implementations/MarkdownMailService.cs
index e8db1a7dc3..7ab98111ee 100644
--- a/src/Core/Services/Implementations/MarkdownMailService.cs
+++ b/src/Core/Services/Implementations/MarkdownMailService.cs
@@ -190,6 +190,22 @@ namespace Bit.Core.Services
await _mailDeliveryService.SendEmailAsync(message);
}
+ public async Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate,
+ List items, bool mentionInvoices)
+ {
+ var model = new Dictionary
+ {
+ ["vaultUrl"] = _globalSettings.BaseServiceUri.VaultWithHash,
+ ["dueDate"] = dueDate.ToString("MMM dd, yyyy"),
+ ["amountDue"] = amount.ToString("C")
+ };
+
+ var message = await CreateMessageAsync("Your Subscription Will Renew Soon", email,
+ "InvoiceUpcoming", model);
+ message.BccEmails = new List { "kyle@bitwarden.com" };
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
private async Task CreateMessageAsync(string subject, string toEmail, string fileName,
Dictionary model)
{
diff --git a/src/Core/Services/Implementations/RazorMailService.cs b/src/Core/Services/Implementations/RazorMailService.cs
index 2cb198c141..2950720e0c 100644
--- a/src/Core/Services/Implementations/RazorMailService.cs
+++ b/src/Core/Services/Implementations/RazorMailService.cs
@@ -223,6 +223,26 @@ namespace Bit.Core.Services
await _mailDeliveryService.SendEmailAsync(message);
}
+ public async Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate,
+ List items, bool mentionInvoices)
+ {
+ var message = CreateDefaultMessage("Your Subscription Will Renew Soon", email);
+ message.BccEmails = new List { "kyle@bitwarden.com" };
+
+ var model = new InvoiceUpcomingViewModel
+ {
+ WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
+ SiteName = _globalSettings.SiteName,
+ AmountDue = amount,
+ DueDate = dueDate,
+ Items = items,
+ MentionInvoices = mentionInvoices
+ };
+ message.HtmlContent = await _engine.CompileRenderAsync("InvoiceUpcoming", model);
+ message.TextContent = await _engine.CompileRenderAsync("InvoiceUpcoming.text", model);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
private MailMessage CreateDefaultMessage(string subject, string toEmail)
{
return CreateDefaultMessage(subject, new List { toEmail });
diff --git a/src/Core/Services/Implementations/SendGridMailDeliveryService.cs b/src/Core/Services/Implementations/SendGridMailDeliveryService.cs
index 9dc077ee58..c1f446c318 100644
--- a/src/Core/Services/Implementations/SendGridMailDeliveryService.cs
+++ b/src/Core/Services/Implementations/SendGridMailDeliveryService.cs
@@ -39,6 +39,10 @@ namespace Bit.Core.Services
sendGridMessage.SetClickTracking(true, false);
sendGridMessage.SetOpenTracking(true, null);
sendGridMessage.AddTos(message.ToEmails.Select(e => new EmailAddress(e)).ToList());
+ if(message.BccEmails?.Any() ?? false)
+ {
+ sendGridMessage.AddBccs(message.BccEmails.Select(e => new EmailAddress(e)).ToList());
+ }
if(message.MetaData?.ContainsKey("SendGridTemplateId") ?? false)
{
diff --git a/src/Core/Services/Implementations/SmtpMailDeliveryService.cs b/src/Core/Services/Implementations/SmtpMailDeliveryService.cs
index 135a6e2058..d03505bc19 100644
--- a/src/Core/Services/Implementations/SmtpMailDeliveryService.cs
+++ b/src/Core/Services/Implementations/SmtpMailDeliveryService.cs
@@ -50,6 +50,14 @@ namespace Bit.Core.Services
smtpMessage.To.Add(new MailAddress(address));
}
+ if(message.BccEmails != null)
+ {
+ foreach(var address in message.BccEmails)
+ {
+ smtpMessage.Bcc.Add(new MailAddress(address));
+ }
+ }
+
if(string.IsNullOrWhiteSpace(message.TextContent))
{
smtpMessage.IsBodyHtml = true;
diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs
index eba42fcea8..fa6ec07ddf 100644
--- a/src/Core/Services/NoopImplementations/NoopMailService.cs
+++ b/src/Core/Services/NoopImplementations/NoopMailService.cs
@@ -66,5 +66,11 @@ namespace Bit.Core.Services
{
return Task.FromResult(0);
}
+
+ public Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate,
+ List items, bool mentionInvoices)
+ {
+ return Task.FromResult(0);
+ }
}
}