Ticket Tailor: Require full name

This commit is contained in:
Kukks
2023-03-20 12:19:04 +01:00
parent b8b94abf02
commit f11b7823c0
8 changed files with 70 additions and 33 deletions

View File

@@ -9,7 +9,7 @@
<PropertyGroup>
<Product>TicketTailor</Product>
<Description>Allows you to integrate with TicketTailor.com to sell tickets for Bitcoin</Description>
<Version>1.0.8</Version>
<Version>1.0.9</Version>
</PropertyGroup>
<!-- Plugin development properties -->
<PropertyGroup>

View File

@@ -53,6 +53,7 @@ namespace BTCPayServer.Plugins.TicketTailor
public async Task<IActionResult> Purchase(string storeId, TicketTailorViewModel request)
{
var config = await _ticketTailorService.GetTicketTailorForStore(storeId);
(TicketTailorClient.Hold, string error)? hold = null;
try
{
if (config?.ApiKey is not null && config?.EventId is not null)
@@ -109,18 +110,18 @@ namespace BTCPayServer.Plugins.TicketTailor
price += ticketCost * purchaseRequestItem.Quantity;
}
var hold = await client.CreateHold(new TicketTailorClient.CreateHoldRequest()
hold = await client.CreateHold(new TicketTailorClient.CreateHoldRequest()
{
EventId = evt.Id,
Note = "Created by BTCPay Server",
TicketTypeId = request.Items.ToDictionary(item => item.TicketTypeId, item => item.Quantity)
});
if (!string.IsNullOrEmpty(hold.error))
if (!string.IsNullOrEmpty(hold.Value.error))
{
TempData.SetStatusMessageModel(new StatusMessageModel
{
Severity = StatusMessageModel.StatusSeverity.Error,
Html = $"Could not reserve tickets because {hold.error}"
Html = $"Could not reserve tickets because {hold.Value.error}"
});
return RedirectToAction("View", new {storeId});
}
@@ -130,23 +131,31 @@ namespace BTCPayServer.Plugins.TicketTailor
var redirectUrl = Request.GetAbsoluteUri(Url.Action("Receipt",
"TicketTailor", new {storeId, invoiceId = "kukkskukkskukks"}));
redirectUrl = redirectUrl.Replace("kukkskukkskukks", "{InvoiceId}");
if(string.IsNullOrEmpty(request.Name))
request.Name??=string.Empty;
var nameSplit = request.Name.Split(" ", StringSplitOptions.RemoveEmptyEntries);
if (config.RequireFullName && nameSplit.Length < 2)
{
request.Name = "Anonymous lizard";
TempData.SetStatusMessageModel(new StatusMessageModel
{
Severity = StatusMessageModel.StatusSeverity.Error,
Html = "Please enter your full name."
});
return RedirectToAction("View", new {storeId});
}
var nameSplit = request.Name.Split(" ", StringSplitOptions.RemoveEmptyEntries);
if (nameSplit.Length < 2)
request.Name = nameSplit.Length switch
{
request.Name = nameSplit[0] + " Lizard";
}
0 => "Satoshi Nakamoto",
< 2 => $"{nameSplit} Nakamoto",
_ => request.Name
};
var inv = await btcpayClient.CreateInvoice(storeId,
new CreateInvoiceRequest()
{
Amount = price,
Currency = evt.Currency,
Type = InvoiceType.Standard,
AdditionalSearchTerms = new[] {"tickettailor", hold.Item1.Id, evt.Id},
AdditionalSearchTerms = new[] {"tickettailor", hold.Value.Item1.Id, evt.Id},
Checkout =
{
RequiresRefundEmail = true,
@@ -162,7 +171,7 @@ namespace BTCPayServer.Plugins.TicketTailor
btcpayUrl = Request.GetAbsoluteRoot(),
buyerName = request.Name,
buyerEmail = request.Email,
holdId = hold.Item1.Id,
holdId = hold.Value.Item1.Id,
orderId="tickettailor"
})
});
@@ -173,14 +182,24 @@ namespace BTCPayServer.Plugins.TicketTailor
inv = await btcpayClient.GetInvoice(inv.StoreId, inv.Id);
}
if (inv.Status == InvoiceStatus.Settled)
return RedirectToAction("Receipt", new {storeId, invoiceId = inv.Id});
return RedirectToAction("Checkout","UIInvoice", new {invoiceId = inv.Id});
return inv.Status == InvoiceStatus.Settled
? RedirectToAction("Receipt", new {storeId, invoiceId = inv.Id})
: RedirectToAction("Checkout", "UIInvoice", new {invoiceId = inv.Id});
}
}
catch (Exception e)
{
TempData.SetStatusMessageModel(new StatusMessageModel
{
Severity = StatusMessageModel.StatusSeverity.Error,
Html = $"Could not continue with ticket purchase.</br>{e.Message}"
});
if (hold?.Item1 is not null)
{
var client = new TicketTailorClient(_httpClientFactory, config.ApiKey);
await client.DeleteHold(hold?.Item1.Id);
}
}
return RedirectToAction("View", new {storeId});
@@ -280,6 +299,7 @@ namespace BTCPayServer.Plugins.TicketTailor
vm.ShowDescription = TicketTailor.ShowDescription;
vm.BypassAvailabilityCheck = TicketTailor.BypassAvailabilityCheck;
vm.CustomCSS = TicketTailor.CustomCSS;
vm.RequireFullName = TicketTailor.RequireFullName;
vm.SpecificTickets = TicketTailor.SpecificTickets;
}
}
@@ -374,7 +394,8 @@ namespace BTCPayServer.Plugins.TicketTailor
ShowDescription = vm.ShowDescription,
CustomCSS = vm.CustomCSS,
SpecificTickets = vm.SpecificTickets,
BypassAvailabilityCheck = vm.BypassAvailabilityCheck
BypassAvailabilityCheck = vm.BypassAvailabilityCheck,
RequireFullName = vm.RequireFullName
};

View File

@@ -111,14 +111,23 @@ public class TicketTailorService : EventHostedServiceBase
protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken)
{
if (evt is InvoiceEvent invoiceEvent)
{
if (invoiceEvent.Invoice.Metadata.OrderId != "tickettailor" || !new []{InvoiceStatus.Settled, InvoiceStatus.Expired, InvoiceStatus.Invalid}.Contains(invoiceEvent.Invoice.GetInvoiceState().Status.ToModernStatus()))
switch (evt)
{
case InvoiceEvent invoiceEvent when invoiceEvent.Invoice.Metadata.OrderId != "tickettailor" || !new []{InvoiceStatus.Settled, InvoiceStatus.Expired, InvoiceStatus.Invalid}.Contains(invoiceEvent.Invoice.GetInvoiceState().Status.ToModernStatus()):
return;
}
case InvoiceEvent invoiceEvent:
if(_memoryCache.TryGetValue($"{nameof(TicketTailorService)}_{invoiceEvent.Invoice.Id}_issue_check_from_ui", out _))return;
await _memoryCache.GetOrCreateAsync(
$"{nameof(TicketTailorService)}_{invoiceEvent.Invoice.Id}_issue_check_from_ui", async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2);
return true;
});
evt = new IssueTicket() {Invoice = invoiceEvent.Invoice};
break;
}
if (evt is not IssueTicket issueTicket)

View File

@@ -11,5 +11,6 @@ namespace BTCPayServer.Plugins.TicketTailor
public string CustomCSS { get; set; }
public List<SpecificTicket> SpecificTickets { get; set; }
public bool BypassAvailabilityCheck { get; set; }
public bool RequireFullName { get; set; }
}
}

View File

@@ -15,6 +15,7 @@ public class UpdateTicketTailorSettingsViewModel
public List<SpecificTicket> SpecificTickets { get; set; }
public bool BypassAvailabilityCheck { get; set; }
public bool RequireFullName { get; set; }
}
public class SpecificTicket

View File

@@ -54,6 +54,11 @@
<label asp-for="BypassAvailabilityCheck" class="form-check-label">Bypass availability checks of TicketTailor (such as when in draft mode)</label>
<span asp-validation-for="BypassAvailabilityCheck" class="text-danger"></span>
</div>
<div class="form-group form-check">
<input asp-for="RequireFullName" type="checkbox" class="form-check-input"/>
<label asp-for="RequireFullName" class="form-check-label">Require full name to be provided (if unchecked and not provided, the gap will be filled in with a placeholder)</label>
<span asp-validation-for="RequireFullName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CustomCSS" class="form-label">Additional Custom CSS</label>

View File

@@ -1,15 +1,9 @@
@using Microsoft.AspNetCore.Routing
@using BTCPayServer.Plugins.TicketTailor
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using NBitcoin
@model BTCPayServer.Plugins.TicketTailor.TicketTailorViewModel
@inject BTCPayServer.Security.ContentSecurityPolicies csp;
@{
var nonce = RandomUtils.GetUInt256().ToString().Substring(0, 32);
csp.Add("script-src", $"'nonce-{nonce}'");
csp.AllowUnsafeHashes();
Layout = "_LayoutSimple";
var available = Model.Settings.BypassAvailabilityCheck || (Model.Event.Unavailable != "true" && Model.Event.TicketsAvailable == "true");
Model.Settings.SpecificTickets ??= new List<SpecificTicket>();
@@ -29,7 +23,7 @@ footer {
}
</style>
<script nonce="@nonce">
<script>
document.addEventListener("DOMContentLoaded", ()=>{
const btn = document.querySelector("button[type='submit']");
document.querySelectorAll("input").forEach(value => value.addEventListener("input", (evt)=>{
@@ -77,7 +71,8 @@ document.addEventListener("DOMContentLoaded", ()=>{
<div class="row g-2 justify-content-center mb-4" id="ticket-form-container">
<div class="col-sm-6 col-md-4">
<div class="form-floating">
<input type="text" minlength="3" asp-for="Name" class="form-control">
<input type="text" asp-for="Name" class="form-control" required="@Model.Settings.RequireFullName"
pattern="^(\w\w+)\s(\w+)$" title="Please enter your first and last name, separated with a space.">
<label >Name</label>
</div>
</div>

View File

@@ -1,4 +1,9 @@
@using BTCPayServer.Abstractions.Services
@inject Safe Safe
@using BTCPayServer.Abstractions.Extensions
@inject BTCPayServer.Abstractions.Services.Safe Safe
@addTagHelper *, BTCPayServer.Abstractions
@addTagHelper *, BTCPayServer.TagHelpers
@addTagHelper *, BTCPayServer.Views.TagHelpers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, BTCPayServer