mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 07:34:24 +01:00
Support webhooks and emails for ticket tailor
This commit is contained in:
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<LangVersion>10</LangVersion>
|
<LangVersion>12</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Plugin specific properties -->
|
<!-- Plugin specific properties -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Product>TicketTailor</Product>
|
<Product>TicketTailor</Product>
|
||||||
<Description>Allows you to integrate with TicketTailor.com to sell tickets for Bitcoin</Description>
|
<Description>Allows you to integrate with TicketTailor.com to sell tickets for Bitcoin</Description>
|
||||||
<Version>2.0.1</Version>
|
<Version>2.0.2</Version>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- Plugin development properties -->
|
<!-- Plugin development properties -->
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using BTCPayServer.Abstractions.Contracts;
|
|||||||
using BTCPayServer.Abstractions.Extensions;
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
using BTCPayServer.Abstractions.Models;
|
using BTCPayServer.Abstractions.Models;
|
||||||
using BTCPayServer.Abstractions.Services;
|
using BTCPayServer.Abstractions.Services;
|
||||||
|
using BTCPayServer.HostedServices.Webhooks;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
{
|
{
|
||||||
applicationBuilder.AddStartupTask<AppMigrate>();
|
applicationBuilder.AddStartupTask<AppMigrate>();
|
||||||
applicationBuilder.AddSingleton<TicketTailorService>();
|
applicationBuilder.AddSingleton<TicketTailorService>();
|
||||||
|
applicationBuilder.AddSingleton<IWebhookProvider>(o => o.GetRequiredService<TicketTailorService>());
|
||||||
applicationBuilder.AddHostedService(s => s.GetRequiredService<TicketTailorService>());
|
applicationBuilder.AddHostedService(s => s.GetRequiredService<TicketTailorService>());
|
||||||
|
|
||||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("TicketTailor/NavExtension", "header-nav"));
|
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("TicketTailor/NavExtension", "header-nav"));
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Contracts;
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
|
using BTCPayServer.Controllers;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Events;
|
using BTCPayServer.Events;
|
||||||
using BTCPayServer.HostedServices;
|
using BTCPayServer.HostedServices;
|
||||||
|
using BTCPayServer.HostedServices.Webhooks;
|
||||||
using BTCPayServer.Logging;
|
using BTCPayServer.Logging;
|
||||||
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Mails;
|
using BTCPayServer.Services.Mails;
|
||||||
@@ -18,12 +22,15 @@ using Microsoft.AspNetCore.Routing;
|
|||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MimeKit;
|
using MimeKit;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using static System.String;
|
using static System.String;
|
||||||
|
using InvoiceData = BTCPayServer.Data.InvoiceData;
|
||||||
|
using WebhookDeliveryData = BTCPayServer.Data.WebhookDeliveryData;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.TicketTailor;
|
namespace BTCPayServer.Plugins.TicketTailor;
|
||||||
|
|
||||||
public class TicketTailorService : EventHostedServiceBase
|
public class TicketTailorService : EventHostedServiceBase, IWebhookProvider
|
||||||
{
|
{
|
||||||
private readonly IMemoryCache _memoryCache;
|
private readonly IMemoryCache _memoryCache;
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
@@ -34,14 +41,18 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
private readonly LinkGenerator _linkGenerator;
|
private readonly LinkGenerator _linkGenerator;
|
||||||
private readonly InvoiceRepository _invoiceRepository;
|
private readonly InvoiceRepository _invoiceRepository;
|
||||||
private readonly AppService _appService;
|
private readonly AppService _appService;
|
||||||
|
private readonly WebhookSender _webhookSender;
|
||||||
|
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
|
||||||
|
private IWebhookProvider _webhookProviderImplementation;
|
||||||
|
|
||||||
public TicketTailorService(IMemoryCache memoryCache,
|
public TicketTailorService(IMemoryCache memoryCache,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
ILogger<TicketTailorService> logger,
|
ILogger<TicketTailorService> logger,
|
||||||
EmailSenderFactory emailSenderFactory ,
|
EmailSenderFactory emailSenderFactory,
|
||||||
LinkGenerator linkGenerator,
|
LinkGenerator linkGenerator,
|
||||||
EventAggregator eventAggregator, InvoiceRepository invoiceRepository,
|
EventAggregator eventAggregator, InvoiceRepository invoiceRepository,
|
||||||
AppService appService) : base(eventAggregator, logger)
|
AppService appService,
|
||||||
|
WebhookSender webhookSender,BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings) : base(eventAggregator, logger)
|
||||||
{
|
{
|
||||||
_memoryCache = memoryCache;
|
_memoryCache = memoryCache;
|
||||||
_httpClientFactory = httpClientFactory;
|
_httpClientFactory = httpClientFactory;
|
||||||
@@ -50,6 +61,8 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
_linkGenerator = linkGenerator;
|
_linkGenerator = linkGenerator;
|
||||||
_invoiceRepository = invoiceRepository;
|
_invoiceRepository = invoiceRepository;
|
||||||
_appService = appService;
|
_appService = appService;
|
||||||
|
_webhookSender = webhookSender;
|
||||||
|
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class IssueTicket
|
private class IssueTicket
|
||||||
@@ -84,11 +97,17 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
{
|
{
|
||||||
switch (evt)
|
switch (evt)
|
||||||
{
|
{
|
||||||
case InvoiceEvent invoiceEvent when invoiceEvent.Invoice.Metadata.OrderId != "tickettailor" || !new []{InvoiceStatus.Settled, InvoiceStatus.Expired, InvoiceStatus.Invalid}.Contains(invoiceEvent.Invoice.GetInvoiceState().Status.ToModernStatus()):
|
case InvoiceEvent invoiceEvent when invoiceEvent.Invoice.Metadata.OrderId != "tickettailor" ||
|
||||||
|
!new[]
|
||||||
|
{
|
||||||
|
InvoiceStatus.Settled, InvoiceStatus.Expired, InvoiceStatus.Invalid
|
||||||
|
}.Contains(invoiceEvent.Invoice.GetInvoiceState().Status
|
||||||
|
.ToModernStatus()):
|
||||||
return;
|
return;
|
||||||
case InvoiceEvent invoiceEvent:
|
case InvoiceEvent invoiceEvent:
|
||||||
|
|
||||||
if(_memoryCache.TryGetValue($"{nameof(TicketTailorService)}_{invoiceEvent.Invoice.Id}_issue_check_from_ui", out _))return;
|
if (_memoryCache.TryGetValue(
|
||||||
|
$"{nameof(TicketTailorService)}_{invoiceEvent.Invoice.Id}_issue_check_from_ui", out _)) return;
|
||||||
|
|
||||||
await _memoryCache.GetOrCreateAsync(
|
await _memoryCache.GetOrCreateAsync(
|
||||||
$"{nameof(TicketTailorService)}_{invoiceEvent.Invoice.Id}_issue_check_from_ui", async entry =>
|
$"{nameof(TicketTailorService)}_{invoiceEvent.Invoice.Id}_issue_check_from_ui", async entry =>
|
||||||
@@ -104,9 +123,11 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
if (evt is not IssueTicket issueTicket)
|
if (evt is not IssueTicket issueTicket)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
async Task HandleIssueTicketError(string e, InvoiceEntity invoiceEntity, InvoiceLogs invoiceLogs, bool setInvalid = true)
|
async Task HandleIssueTicketError(string e, InvoiceEntity invoiceEntity, InvoiceLogs invoiceLogs,
|
||||||
|
bool setInvalid = true)
|
||||||
{
|
{
|
||||||
invoiceLogs.Write( $"Ticket could not be created. You should refund customer.{Environment.NewLine}{e}", InvoiceEventData.EventSeverity.Error);
|
invoiceLogs.Write($"Ticket could not be created. You should refund customer.{Environment.NewLine}{e}",
|
||||||
|
InvoiceEventData.EventSeverity.Error);
|
||||||
await _invoiceRepository.AddInvoiceLogs(invoiceEntity.Id, invoiceLogs);
|
await _invoiceRepository.AddInvoiceLogs(invoiceEntity.Id, invoiceLogs);
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -131,9 +152,11 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
if (settings?.ApiKey is null)
|
if (settings?.ApiKey is null)
|
||||||
{
|
{
|
||||||
await HandleIssueTicketError(
|
await HandleIssueTicketError(
|
||||||
"The ticket tailor integration is misconfigured and BTCPay Server cannot connect to Ticket Tailor.", invoice, invLogs, false);
|
"The ticket tailor integration is misconfigured and BTCPay Server cannot connect to Ticket Tailor.",
|
||||||
|
invoice, invLogs, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new[] {InvoiceStatus.Invalid, InvoiceStatus.Expired}.Contains(invoice.Status.ToModernStatus()))
|
if (new[] {InvoiceStatus.Invalid, InvoiceStatus.Expired}.Contains(invoice.Status.ToModernStatus()))
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -147,7 +170,8 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
{
|
{
|
||||||
invoice.Metadata.AdditionalData.Remove("holdId");
|
invoice.Metadata.AdditionalData.Remove("holdId");
|
||||||
invoice.Metadata.AdditionalData.Add("holdId_deleted", holdIdx);
|
invoice.Metadata.AdditionalData.Add("holdId_deleted", holdIdx);
|
||||||
await _invoiceRepository.UpdateInvoiceMetadata(invoice.Id, invoice.StoreId, invoice.Metadata.ToJObject());
|
await _invoiceRepository.UpdateInvoiceMetadata(invoice.Id, invoice.StoreId,
|
||||||
|
invoice.Metadata.ToJObject());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +193,9 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
jHoldId.Value<string>() is not { } holdId)
|
jHoldId.Value<string>() is not { } holdId)
|
||||||
{
|
{
|
||||||
|
|
||||||
await HandleIssueTicketError( "There was no hold associated with this invoice. Maybe this invoice was marked as invalid before?", invoice, invLogs);
|
await HandleIssueTicketError(
|
||||||
|
"There was no hold associated with this invoice. Maybe this invoice was marked as invalid before?",
|
||||||
|
invoice, invLogs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,14 +217,16 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
var hold = await client.GetHold(holdId);
|
var hold = await client.GetHold(holdId);
|
||||||
if (hold is null)
|
if (hold is null)
|
||||||
{
|
{
|
||||||
await HandleIssueTicketError( "The hold created for this invoice was not found", invoice, invLogs);
|
await HandleIssueTicketError("The hold created for this invoice was not found", invoice, invLogs);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var holdOriginalAmount = hold?.TotalOnHold;
|
var holdOriginalAmount = hold?.TotalOnHold;
|
||||||
|
|
||||||
invLogs.Write( $"Issuing {holdOriginalAmount} tickets for hold {holdId}", InvoiceEventData.EventSeverity.Info);
|
invLogs.Write($"Issuing {holdOriginalAmount} tickets for hold {holdId}",
|
||||||
|
InvoiceEventData.EventSeverity.Info);
|
||||||
while (hold?.TotalOnHold > 0)
|
while (hold?.TotalOnHold > 0)
|
||||||
{
|
{
|
||||||
foreach (var tt in hold.Quantities.Where(quantity => quantity.Quantity > 0))
|
foreach (var tt in hold.Quantities.Where(quantity => quantity.Quantity > 0))
|
||||||
@@ -216,13 +244,15 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
if (ticketResult.error is null)
|
if (ticketResult.error is null)
|
||||||
{
|
{
|
||||||
tickets.Add(ticketResult.Item1);
|
tickets.Add(ticketResult.Item1);
|
||||||
invLogs.Write($"Issued ticket {ticketResult.Item1.Id} {ticketResult.Item1.Reference}", InvoiceEventData.EventSeverity.Info);
|
invLogs.Write($"Issued ticket {ticketResult.Item1.Id} {ticketResult.Item1.Reference}",
|
||||||
|
InvoiceEventData.EventSeverity.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
errors.Add(ticketResult.error);
|
errors.Add(ticketResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
hold = await client.GetHold(holdId);
|
hold = await client.GetHold(holdId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,30 +262,33 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
new JArray(tickets.Select(issuedTicket => issuedTicket.Id));
|
new JArray(tickets.Select(issuedTicket => issuedTicket.Id));
|
||||||
if (tickets.Count != holdOriginalAmount)
|
if (tickets.Count != holdOriginalAmount)
|
||||||
{
|
{
|
||||||
await HandleIssueTicketError( $"Not all the held tickets were issued because: {Join(",", errors)}", invoice, invLogs);
|
await HandleIssueTicketError($"Not all the held tickets were issued because: {Join(",", errors)}",
|
||||||
|
invoice, invLogs);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _invoiceRepository.UpdateInvoiceMetadata(invoice.Id, invoice.StoreId, invoice.Metadata.ToJObject());
|
|
||||||
|
await _invoiceRepository.UpdateInvoiceMetadata(invoice.Id, invoice.StoreId,
|
||||||
|
invoice.Metadata.ToJObject());
|
||||||
await _invoiceRepository.AddInvoiceLogs(invoice.Id, invLogs);
|
await _invoiceRepository.AddInvoiceLogs(invoice.Id, invLogs);
|
||||||
|
|
||||||
if (settings.SendEmail)
|
|
||||||
{
|
|
||||||
|
|
||||||
var uri = new Uri(btcpayUrl);
|
var uri = new Uri(btcpayUrl);
|
||||||
var url =
|
var receiptUrl =
|
||||||
_linkGenerator.GetUriByAction("Receipt",
|
_linkGenerator.GetUriByAction("Receipt",
|
||||||
"TicketTailor",
|
"TicketTailor",
|
||||||
new {invoiceId = invoice.Id},
|
new {invoiceId = invoice.Id},
|
||||||
uri.Scheme,
|
uri.Scheme,
|
||||||
new HostString(uri.Host),
|
new HostString(uri.Host),
|
||||||
uri.AbsolutePath);
|
uri.AbsolutePath);
|
||||||
|
if (settings.SendEmail)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sender = await _emailSenderFactory.GetEmailSender(issueTicket.Invoice.StoreId);
|
var sender = await _emailSenderFactory.GetEmailSender(issueTicket.Invoice.StoreId);
|
||||||
sender.SendEmail(MailboxAddress.Parse(email), "Your ticket is available now.",
|
sender.SendEmail(MailboxAddress.Parse(email), "Your ticket is available now.",
|
||||||
$"Your payment has been settled and the event ticket has been issued successfully. Please go to <a href='{url}'>{url}</a>");
|
$"Your payment has been settled and the event ticket has been issued successfully. Please go to <a href='{receiptUrl}'>{receiptUrl}</a>");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -263,6 +296,35 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
TicketTailorWebhookDeliveryRequest CreateDeliveryRequest(WebhookData? webhook)
|
||||||
|
{
|
||||||
|
var webhookEvent = new WebhookTicketTailorEvent(TicketTailorTicketIssued, invoice.StoreId)
|
||||||
|
{
|
||||||
|
AppId = appId,
|
||||||
|
Tickets = tickets.Select(t => t.Id).ToArray(),
|
||||||
|
InvoiceId = invoice.Id
|
||||||
|
};
|
||||||
|
var delivery = webhook is null? null: WebhookExtensions.NewWebhookDelivery(webhook.Id);
|
||||||
|
if (delivery is not null)
|
||||||
|
{
|
||||||
|
webhookEvent.DeliveryId = delivery.Id;
|
||||||
|
webhookEvent.OriginalDeliveryId = delivery.Id;
|
||||||
|
webhookEvent.Timestamp = delivery.Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TicketTailorWebhookDeliveryRequest(receiptUrl, invoice, webhook?.Id,
|
||||||
|
webhookEvent,
|
||||||
|
delivery,
|
||||||
|
webhook?.GetBlob(),_btcPayNetworkJsonSerializerSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
var webhooks = await _webhookSender.GetWebhooks(app.StoreDataId, TicketTailorTicketIssued);
|
||||||
|
foreach (var webhook in webhooks)
|
||||||
|
{
|
||||||
|
_webhookSender.EnqueueDelivery(CreateDeliveryRequest( webhook));
|
||||||
|
}
|
||||||
|
|
||||||
|
EventAggregator.Publish(CreateDeliveryRequest( null));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -274,4 +336,92 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
_logger.LogError(ex, "Failed to issue ticket");
|
_logger.LogError(ex, "Failed to issue ticket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public const string TicketTailorTicketIssued = "TicketTailorTicketIssued";
|
||||||
|
|
||||||
|
public Dictionary<string, string> GetSupportedWebhookTypes()
|
||||||
|
{
|
||||||
|
return new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{TicketTailorTicketIssued, "A ticket has been issued through ticket tailor"},
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebhookEvent CreateTestEvent(string type, params object[] args)
|
||||||
|
{
|
||||||
|
var storeId = args[0].ToString();
|
||||||
|
return new WebhookTicketTailorEvent(type, storeId)
|
||||||
|
{
|
||||||
|
AppId = "__test__" + Guid.NewGuid() + "__test__",
|
||||||
|
Tickets = new[] {"__test__" + Guid.NewGuid() + "__test__"}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WebhookTicketTailorEvent : StoreWebhookEvent
|
||||||
|
{
|
||||||
|
public WebhookTicketTailorEvent(string evtType, string storeId)
|
||||||
|
{
|
||||||
|
if (!evtType.StartsWith("tickettailor", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
throw new ArgumentException("Invalid event type", nameof(evtType));
|
||||||
|
Type = evtType;
|
||||||
|
StoreId = storeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[JsonProperty(Order = 2)] public string AppId { get; set; }
|
||||||
|
[JsonProperty(Order = 3)] public string[] Tickets { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(Order = 4)] public string InvoiceId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TicketTailorWebhookDeliveryRequest(
|
||||||
|
string receiptUrl,
|
||||||
|
InvoiceEntity invoice,
|
||||||
|
string? webhookId,
|
||||||
|
WebhookEvent webhookEvent,
|
||||||
|
WebhookDeliveryData? delivery,
|
||||||
|
WebhookBlob? webhookBlob,
|
||||||
|
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings)
|
||||||
|
: WebhookSender.WebhookDeliveryRequest(webhookId!, webhookEvent, delivery!, webhookBlob!)
|
||||||
|
{
|
||||||
|
public InvoiceEntity Invoice { get; } = invoice;
|
||||||
|
|
||||||
|
public override Task<SendEmailRequest?> Interpolate(SendEmailRequest req,
|
||||||
|
UIStoresController.StoreEmailRule storeEmailRule)
|
||||||
|
{
|
||||||
|
if (storeEmailRule.CustomerEmail &&
|
||||||
|
MailboxAddressValidator.TryParse(Invoice.Metadata.BuyerEmail, out var bmb))
|
||||||
|
{
|
||||||
|
req.Email ??= string.Empty;
|
||||||
|
req.Email += $",{bmb}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
req.Subject = Interpolate(req.Subject);
|
||||||
|
req.Body = Interpolate(req.Body);
|
||||||
|
return Task.FromResult(req)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string Interpolate(string str)
|
||||||
|
{
|
||||||
|
var res = str.Replace("{Invoice.Id}", Invoice.Id)
|
||||||
|
.Replace("{Invoice.StoreId}", Invoice.StoreId)
|
||||||
|
.Replace("{Invoice.Price}", Invoice.Price.ToString(CultureInfo.InvariantCulture))
|
||||||
|
.Replace("{Invoice.Currency}", Invoice.Currency)
|
||||||
|
.Replace("{Invoice.Status}", Invoice.Status.ToString())
|
||||||
|
.Replace("{Invoice.AdditionalStatus}", Invoice.ExceptionStatus.ToString())
|
||||||
|
.Replace("{Invoice.OrderId}", Invoice.Metadata.OrderId);
|
||||||
|
|
||||||
|
|
||||||
|
res = InterpolateJsonField(str, "Invoice.Metadata", Invoice.Metadata.ToJObject());
|
||||||
|
|
||||||
|
res = res.Replace("{TicketTailor.ReceiptUrl}", receiptUrl);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user