mirror of
https://github.com/bitwarden/server.git
synced 2025-02-02 23:41:21 +01:00
subscription renewal reminder emails
This commit is contained in:
parent
4e6e215d35
commit
053096c1a1
@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<RootNamespace>Bit.Billing</RootNamespace>
|
<RootNamespace>Bit.Billing</RootNamespace>
|
||||||
<UserSecretsId>bitwarden-Billing</UserSecretsId>
|
<UserSecretsId>bitwarden-Billing</UserSecretsId>
|
||||||
|
<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.Services;
|
using Bit.Core.Models.Table;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@ -17,18 +19,24 @@ namespace Bit.Billing.Controllers
|
|||||||
private readonly BillingSettings _billingSettings;
|
private readonly BillingSettings _billingSettings;
|
||||||
private readonly IHostingEnvironment _hostingEnvironment;
|
private readonly IHostingEnvironment _hostingEnvironment;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly IOrganizationService _organizationService;
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
private readonly IMailService _mailService;
|
||||||
|
|
||||||
public StripeController(
|
public StripeController(
|
||||||
IOptions<BillingSettings> billingSettings,
|
IOptions<BillingSettings> billingSettings,
|
||||||
IHostingEnvironment hostingEnvironment,
|
IHostingEnvironment hostingEnvironment,
|
||||||
IOrganizationService organizationService,
|
IOrganizationService organizationService,
|
||||||
IUserService userService)
|
IOrganizationRepository organizationRepository,
|
||||||
|
IUserService userService,
|
||||||
|
IMailService mailService)
|
||||||
{
|
{
|
||||||
_billingSettings = billingSettings?.Value;
|
_billingSettings = billingSettings?.Value;
|
||||||
_hostingEnvironment = hostingEnvironment;
|
_hostingEnvironment = hostingEnvironment;
|
||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
|
_mailService = mailService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("webhook")]
|
[HttpPost("webhook")]
|
||||||
@ -105,7 +113,7 @@ namespace Bit.Billing.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(false /* Disabled for now */ && invUpcoming)
|
else if(invUpcoming)
|
||||||
{
|
{
|
||||||
StripeInvoice invoice = Mapper<StripeInvoice>.MapFromJson(
|
StripeInvoice invoice = Mapper<StripeInvoice>.MapFromJson(
|
||||||
parsedEvent.Data.Object.ToString());
|
parsedEvent.Data.Object.ToString());
|
||||||
@ -114,7 +122,6 @@ namespace Bit.Billing.Controllers
|
|||||||
throw new Exception("Invoice is null.");
|
throw new Exception("Invoice is null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: maybe invoice subscription expandable is already here any we don't need to call API?
|
|
||||||
var subscriptionService = new StripeSubscriptionService();
|
var subscriptionService = new StripeSubscriptionService();
|
||||||
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
||||||
if(subscription == null)
|
if(subscription == null)
|
||||||
@ -122,21 +129,32 @@ namespace Bit.Billing.Controllers
|
|||||||
throw new Exception("Invoice subscription is null.");
|
throw new Exception("Invoice subscription is null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string email = null;
|
||||||
var ids = GetIdsFromMetaData(subscription.Metadata);
|
var ids = GetIdsFromMetaData(subscription.Metadata);
|
||||||
|
|
||||||
// To include in email:
|
|
||||||
// invoice.AmountDue;
|
|
||||||
// invoice.DueDate;
|
|
||||||
|
|
||||||
// org
|
// org
|
||||||
if(ids.Item1.HasValue)
|
if(ids.Item1.HasValue)
|
||||||
{
|
{
|
||||||
// TODO: email billing contact
|
var org = await _organizationRepository.GetByIdAsync(ids.Item1.Value);
|
||||||
|
if(org != null && OrgPlanForInvoiceNotifications(org))
|
||||||
|
{
|
||||||
|
email = org.BillingEmail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// user
|
// user
|
||||||
else if(ids.Item2.HasValue)
|
else if(ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
// TODO: email user
|
var user = await _userService.GetUserByIdAsync(ids.Item2.Value);
|
||||||
|
if(user.Premium)
|
||||||
|
{
|
||||||
|
email = user.Email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(email) && invoice.NextPaymentAttempt.HasValue)
|
||||||
|
{
|
||||||
|
var items = invoice.StripeInvoiceLineItems.Select(i => i.Description).ToList();
|
||||||
|
await _mailService.SendInvoiceUpcomingAsync(email, invoice.AmountDue / 100M,
|
||||||
|
invoice.NextPaymentAttempt.Value, items, ids.Item1.HasValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,5 +199,18 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
return new Tuple<Guid?, Guid?>(orgId, userId);
|
return new Tuple<Guid?, Guid?>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Remove="MailTemplates\Markdown\InvoiceUpcoming.md" />
|
||||||
<None Remove="MailTemplates\Markdown\PasswordlessSignIn.md" />
|
<None Remove="MailTemplates\Markdown\PasswordlessSignIn.md" />
|
||||||
|
<None Remove="MailTemplates\Razor\InvoiceUpcoming.cshtml" />
|
||||||
|
<None Remove="MailTemplates\Razor\InvoiceUpcoming.text.cshtml" />
|
||||||
<None Remove="MailTemplates\Razor\PasswordlessSignIn.cshtml" />
|
<None Remove="MailTemplates\Razor\PasswordlessSignIn.cshtml" />
|
||||||
<None Remove="MailTemplates\Razor\PasswordlessSignIn.text.cshtml" />
|
<None Remove="MailTemplates\Razor\PasswordlessSignIn.text.cshtml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -18,6 +21,7 @@
|
|||||||
<EmbeddedResource Include="MailTemplates\Markdown\ChangeEmailAlreadyExists.md" />
|
<EmbeddedResource Include="MailTemplates\Markdown\ChangeEmailAlreadyExists.md" />
|
||||||
<EmbeddedResource Include="MailTemplates\Markdown\MasterPasswordHint.md" />
|
<EmbeddedResource Include="MailTemplates\Markdown\MasterPasswordHint.md" />
|
||||||
<EmbeddedResource Include="MailTemplates\Markdown\NoMasterPasswordHint.md" />
|
<EmbeddedResource Include="MailTemplates\Markdown\NoMasterPasswordHint.md" />
|
||||||
|
<EmbeddedResource Include="MailTemplates\Markdown\InvoiceUpcoming.md" />
|
||||||
<EmbeddedResource Include="MailTemplates\Markdown\OrganizationUserAccepted.md" />
|
<EmbeddedResource Include="MailTemplates\Markdown\OrganizationUserAccepted.md" />
|
||||||
<EmbeddedResource Include="MailTemplates\Markdown\OrganizationUserConfirmed.md" />
|
<EmbeddedResource Include="MailTemplates\Markdown\OrganizationUserConfirmed.md" />
|
||||||
<EmbeddedResource Include="MailTemplates\Markdown\PasswordlessSignIn.md" />
|
<EmbeddedResource Include="MailTemplates\Markdown\PasswordlessSignIn.md" />
|
||||||
@ -26,6 +30,8 @@
|
|||||||
<EmbeddedResource Include="MailTemplates\Markdown\VerifyDelete.md" />
|
<EmbeddedResource Include="MailTemplates\Markdown\VerifyDelete.md" />
|
||||||
<EmbeddedResource Include="MailTemplates\Markdown\VerifyEmail.md" />
|
<EmbeddedResource Include="MailTemplates\Markdown\VerifyEmail.md" />
|
||||||
<EmbeddedResource Include="MailTemplates\Markdown\Welcome.md" />
|
<EmbeddedResource Include="MailTemplates\Markdown\Welcome.md" />
|
||||||
|
<EmbeddedResource Include="MailTemplates\Razor\InvoiceUpcoming.cshtml" />
|
||||||
|
<EmbeddedResource Include="MailTemplates\Razor\InvoiceUpcoming.text.cshtml" />
|
||||||
<EmbeddedResource Include="MailTemplates\Razor\PasswordlessSignIn.text.cshtml" />
|
<EmbeddedResource Include="MailTemplates\Razor\PasswordlessSignIn.text.cshtml" />
|
||||||
<EmbeddedResource Include="MailTemplates\Razor\PasswordlessSignIn.cshtml" />
|
<EmbeddedResource Include="MailTemplates\Razor\PasswordlessSignIn.cshtml" />
|
||||||
<EmbeddedResource Include="MailTemplates\Razor\VerifyDelete.cshtml" />
|
<EmbeddedResource Include="MailTemplates\Razor\VerifyDelete.cshtml" />
|
||||||
|
5
src/Core/MailTemplates/Markdown/InvoiceUpcoming.md
Normal file
5
src/Core/MailTemplates/Markdown/InvoiceUpcoming.md
Normal file
@ -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.
|
35
src/Core/MailTemplates/Razor/InvoiceUpcoming.cshtml
Normal file
35
src/Core/MailTemplates/Razor/InvoiceUpcoming.cshtml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
@model Bit.Core.Models.Mail.InvoiceUpcomingViewModel
|
||||||
|
@{
|
||||||
|
Layout = "_BasicMailLayout";
|
||||||
|
}
|
||||||
|
<p>
|
||||||
|
This is a reminder that your Bitwarden subscription is due for renewal soon.
|
||||||
|
Your payment method on file will be charged for <b>@Model.AmountDue.ToString("C")</b> on
|
||||||
|
<b>@Model.DueDate.ToString("MMM dd, yyyy")</b>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b><u>Summary Of Charges</u></b><br />
|
||||||
|
@foreach(var item in Model.Items)
|
||||||
|
{
|
||||||
|
@:- @item<br />
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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
|
||||||
|
<a href="@Model.WebVaultUrl" target="_blank">@Model.WebVaultUrl</a>. Once logged in,
|
||||||
|
navigate to the Billing page for your account.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@if(Model.MentionInvoices)
|
||||||
|
{
|
||||||
|
<p>
|
||||||
|
Invoices for your payments can also be downloaded from Billing page for your account.
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
<p>
|
||||||
|
If you have any questions or problems, please feel free to email us at
|
||||||
|
hello@bitwarden.com.
|
||||||
|
</p>
|
27
src/Core/MailTemplates/Razor/InvoiceUpcoming.text.cshtml
Normal file
27
src/Core/MailTemplates/Razor/InvoiceUpcoming.text.cshtml
Normal file
@ -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.
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
13
src/Core/Models/Mail/InvoiceUpcomingViewModel.cs
Normal file
13
src/Core/Models/Mail/InvoiceUpcomingViewModel.cs
Normal file
@ -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<string> Items { get; set; }
|
||||||
|
public bool MentionInvoices { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ namespace Bit.Core.Models.Mail
|
|||||||
{
|
{
|
||||||
public string Subject { get; set; }
|
public string Subject { get; set; }
|
||||||
public IEnumerable<string> ToEmails { get; set; }
|
public IEnumerable<string> ToEmails { get; set; }
|
||||||
|
public IEnumerable<string> BccEmails { get; set; }
|
||||||
public string HtmlContent { get; set; }
|
public string HtmlContent { get; set; }
|
||||||
public string TextContent { get; set; }
|
public string TextContent { get; set; }
|
||||||
public IDictionary<string, object> MetaData { get; set; }
|
public IDictionary<string, object> MetaData { get; set; }
|
||||||
|
@ -19,5 +19,6 @@ namespace Bit.Core.Services
|
|||||||
Task SendOrganizationAcceptedEmailAsync(string organizationName, string userEmail, IEnumerable<string> adminEmails);
|
Task SendOrganizationAcceptedEmailAsync(string organizationName, string userEmail, IEnumerable<string> adminEmails);
|
||||||
Task SendOrganizationConfirmedEmailAsync(string organizationName, string email);
|
Task SendOrganizationConfirmedEmailAsync(string organizationName, string email);
|
||||||
Task SendPasswordlessSignInAsync(string returnUrl, string token, string email);
|
Task SendPasswordlessSignInAsync(string returnUrl, string token, string email);
|
||||||
|
Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate, List<string> items, bool mentionInvoices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,6 +179,20 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate,
|
||||||
|
List<string> 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)
|
private void LogError(Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Error sending mail with primary service, using backup.");
|
_logger.LogError(e, "Error sending mail with primary service, using backup.");
|
||||||
|
@ -190,6 +190,22 @@ namespace Bit.Core.Services
|
|||||||
await _mailDeliveryService.SendEmailAsync(message);
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate,
|
||||||
|
List<string> items, bool mentionInvoices)
|
||||||
|
{
|
||||||
|
var model = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["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<string> { "kyle@bitwarden.com" };
|
||||||
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<MailMessage> CreateMessageAsync(string subject, string toEmail, string fileName,
|
private async Task<MailMessage> CreateMessageAsync(string subject, string toEmail, string fileName,
|
||||||
Dictionary<string, string> model)
|
Dictionary<string, string> model)
|
||||||
{
|
{
|
||||||
|
@ -223,6 +223,26 @@ namespace Bit.Core.Services
|
|||||||
await _mailDeliveryService.SendEmailAsync(message);
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate,
|
||||||
|
List<string> items, bool mentionInvoices)
|
||||||
|
{
|
||||||
|
var message = CreateDefaultMessage("Your Subscription Will Renew Soon", email);
|
||||||
|
message.BccEmails = new List<string> { "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)
|
private MailMessage CreateDefaultMessage(string subject, string toEmail)
|
||||||
{
|
{
|
||||||
return CreateDefaultMessage(subject, new List<string> { toEmail });
|
return CreateDefaultMessage(subject, new List<string> { toEmail });
|
||||||
|
@ -39,6 +39,10 @@ namespace Bit.Core.Services
|
|||||||
sendGridMessage.SetClickTracking(true, false);
|
sendGridMessage.SetClickTracking(true, false);
|
||||||
sendGridMessage.SetOpenTracking(true, null);
|
sendGridMessage.SetOpenTracking(true, null);
|
||||||
sendGridMessage.AddTos(message.ToEmails.Select(e => new EmailAddress(e)).ToList());
|
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)
|
if(message.MetaData?.ContainsKey("SendGridTemplateId") ?? false)
|
||||||
{
|
{
|
||||||
|
@ -50,6 +50,14 @@ namespace Bit.Core.Services
|
|||||||
smtpMessage.To.Add(new MailAddress(address));
|
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))
|
if(string.IsNullOrWhiteSpace(message.TextContent))
|
||||||
{
|
{
|
||||||
smtpMessage.IsBodyHtml = true;
|
smtpMessage.IsBodyHtml = true;
|
||||||
|
@ -66,5 +66,11 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate,
|
||||||
|
List<string> items, bool mentionInvoices)
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user