mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 07:34:24 +01:00
Upgrade TicketTailor to be an app
This commit is contained in:
65
Plugins/BTCPayServer.Plugins.TicketTailor/AppMigrate.cs
Normal file
65
Plugins/BTCPayServer.Plugins.TicketTailor/AppMigrate.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Services.Apps;
|
||||||
|
using BTCPayServer.Services.Invoices;
|
||||||
|
using BTCPayServer.Services.Stores;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.TicketTailor;
|
||||||
|
|
||||||
|
public class AppMigrate : IStartupTask
|
||||||
|
{
|
||||||
|
private readonly StoreRepository _storeRepository;
|
||||||
|
private readonly AppService _appService;
|
||||||
|
private readonly ApplicationDbContextFactory _contextFactory;
|
||||||
|
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||||
|
|
||||||
|
public AppMigrate(StoreRepository storeRepository, AppService appService,
|
||||||
|
ApplicationDbContextFactory contextFactory, BTCPayNetworkProvider btcPayNetworkProvider)
|
||||||
|
{
|
||||||
|
_storeRepository = storeRepository;
|
||||||
|
_appService = appService;
|
||||||
|
_contextFactory = contextFactory;
|
||||||
|
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var existingSettings =
|
||||||
|
await _storeRepository.GetSettingsAsync<TicketTailorSettings>("TicketTailorSettings");
|
||||||
|
foreach (var setting in existingSettings)
|
||||||
|
{
|
||||||
|
var app = new AppData()
|
||||||
|
{
|
||||||
|
Created = DateTimeOffset.UtcNow,
|
||||||
|
Name = "Ticket Tailor",
|
||||||
|
AppType = TicketTailorApp.AppType,
|
||||||
|
StoreDataId = setting.Key,
|
||||||
|
TagAllInvoices = false,
|
||||||
|
Archived = false,
|
||||||
|
Settings = JsonConvert.SerializeObject(setting.Value)
|
||||||
|
};
|
||||||
|
await _appService.UpdateOrCreateApp(app);
|
||||||
|
await using var ctx = _contextFactory.CreateContext();
|
||||||
|
var invoices = await ctx.Invoices
|
||||||
|
.Include(data => data.InvoiceSearchData)
|
||||||
|
.Where(data => data.StoreDataId == setting.Key && data.OrderId == "tickettailor").ToListAsync(cancellationToken: cancellationToken);
|
||||||
|
foreach (var invoice in invoices)
|
||||||
|
{
|
||||||
|
var entity = invoice.GetBlob(_btcPayNetworkProvider);
|
||||||
|
entity.Metadata.SetAdditionalData("appId", app.Id);
|
||||||
|
entity.InternalTags.Add(AppService.GetAppInternalTag(app.Id));
|
||||||
|
InvoiceRepository.AddToTextSearch(ctx, invoice, AppService.GetAppSearchTerm(app) );
|
||||||
|
invoice.SetBlob(entity);
|
||||||
|
}
|
||||||
|
await ctx.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
await _storeRepository.UpdateSetting<TicketTailorSettings>(setting.Key, "TicketTailorSettings", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<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>1.0.14</Version>
|
<Version>2.0.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- Plugin development properties -->
|
<!-- Plugin development properties -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ It allows you to sell tickets for your events and accept payments in Bitcoin.
|
|||||||
1. Install the plugin from Plugins=>Add New=> TicketTailor
|
1. Install the plugin from Plugins=>Add New=> TicketTailor
|
||||||
2. Restart BTCPay Server
|
2. Restart BTCPay Server
|
||||||
3. Go to your Ticket Tailor account and add a [new API key](https://app.tickettailor.com/box-office/api#dpop=/box-office/api-key/add) with `Admin` role and "hide personal data from responses" unchecked.
|
3. Go to your Ticket Tailor account and add a [new API key](https://app.tickettailor.com/box-office/api#dpop=/box-office/api-key/add) with `Admin` role and "hide personal data from responses" unchecked.
|
||||||
4. Go back to your BTCPay Server, choose the store to integrate with and click on Ticket Tailor in the navigation.
|
4. Go back to your BTCPay Server, choose the store to integrate with and click on Ticket Tailor in the navigation. This will create a ticket tailor app in your current store.
|
||||||
5. Enter the API Key and save.
|
5. Enter the API Key and save.
|
||||||
6. Now you should be able to select your Ticket tailor events in the dropdown. One selected, click save.
|
6. Now you should be able to select your Ticket tailor events in the dropdown. One selected, click save.
|
||||||
7. You should now have a "ticket purchase" button on your store's page. Clicking it will take you to the btcpayserver event purchase page.
|
7. You should now have a "ticket purchase" button on your store's page. Clicking it will take you to the btcpayserver event purchase page.
|
||||||
|
|||||||
49
Plugins/BTCPayServer.Plugins.TicketTailor/TicketTailorApp.cs
Normal file
49
Plugins/BTCPayServer.Plugins.TicketTailor/TicketTailorApp.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Configuration;
|
||||||
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Services.Apps;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.TicketTailor;
|
||||||
|
|
||||||
|
public class TicketTailorApp : AppBaseType
|
||||||
|
{
|
||||||
|
private readonly LinkGenerator _linkGenerator;
|
||||||
|
private readonly IOptions<BTCPayServerOptions> _options;
|
||||||
|
public const string AppType = "TicketTailor";
|
||||||
|
|
||||||
|
public TicketTailorApp(
|
||||||
|
LinkGenerator linkGenerator,
|
||||||
|
IOptions<BTCPayServerOptions> options)
|
||||||
|
{
|
||||||
|
Description = "Ticket Tailor";
|
||||||
|
Type = AppType;
|
||||||
|
_linkGenerator = linkGenerator;
|
||||||
|
_options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<string> ConfigureLink(AppData app)
|
||||||
|
{
|
||||||
|
return Task.FromResult(_linkGenerator.GetPathByAction(
|
||||||
|
nameof(TicketTailorController.UpdateTicketTailorSettings),
|
||||||
|
"TicketTailor", new {appId = app.Id}, _options.Value.RootPath)!);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<object?> GetInfo(AppData appData)
|
||||||
|
{
|
||||||
|
return Task.FromResult<object?>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task SetDefaultSettings(AppData appData, string defaultCurrency)
|
||||||
|
{
|
||||||
|
appData.SetSettings(new TicketTailorSettings());
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<string> ViewLink(AppData app)
|
||||||
|
{
|
||||||
|
return Task.FromResult(_linkGenerator.GetPathByAction(nameof(TicketTailorController.View),
|
||||||
|
"TicketTailor", new {appId = app.Id}, _options.Value.RootPath)!);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,33 +2,78 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Contracts;
|
using BTCPayServer.Abstractions.Constants;
|
||||||
using BTCPayServer.Abstractions.Extensions;
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
using BTCPayServer.Abstractions.Models;
|
using BTCPayServer.Abstractions.Models;
|
||||||
using BTCPayServer.Client;
|
using BTCPayServer.Client;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
|
using BTCPayServer.Controllers;
|
||||||
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Services.Apps;
|
||||||
|
using BTCPayServer.Services.Invoices;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using AuthenticationSchemes = BTCPayServer.Abstractions.Constants.AuthenticationSchemes;
|
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.TicketTailor
|
namespace BTCPayServer.Plugins.TicketTailor
|
||||||
{
|
{
|
||||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
[Route("plugins/{storeId}/TicketTailor")]
|
|
||||||
public class TicketTailorController : Controller
|
public class TicketTailorController : Controller
|
||||||
{
|
{
|
||||||
[AllowAnonymous]
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
[HttpGet("")]
|
private readonly TicketTailorService _ticketTailorService;
|
||||||
public async Task<IActionResult> View(string storeId)
|
private readonly AppService _appService;
|
||||||
|
private readonly ApplicationDbContextFactory _contextFactory;
|
||||||
|
private readonly InvoiceRepository _invoiceRepository;
|
||||||
|
private readonly UIInvoiceController _uiInvoiceController;
|
||||||
|
|
||||||
|
public TicketTailorController(IHttpClientFactory httpClientFactory,
|
||||||
|
TicketTailorService ticketTailorService,
|
||||||
|
AppService appService,
|
||||||
|
ApplicationDbContextFactory contextFactory,
|
||||||
|
InvoiceRepository invoiceRepository,
|
||||||
|
UIInvoiceController uiInvoiceController )
|
||||||
{
|
{
|
||||||
var config = await _ticketTailorService.GetTicketTailorForStore(storeId);
|
_httpClientFactory = httpClientFactory;
|
||||||
|
_ticketTailorService = ticketTailorService;
|
||||||
|
_appService = appService;
|
||||||
|
_contextFactory = contextFactory;
|
||||||
|
_invoiceRepository = invoiceRepository;
|
||||||
|
_uiInvoiceController = uiInvoiceController;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpGet("plugins/{storeId}/TicketTailor")]
|
||||||
|
public async Task<IActionResult> ViewLegacy(string storeId)
|
||||||
|
{
|
||||||
|
await using var ctx = _contextFactory.CreateContext();
|
||||||
|
var app = await ctx.Apps
|
||||||
|
.Where(data => data.StoreDataId == storeId && data.AppType == TicketTailorApp.AppType)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (app is null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return RedirectToAction(nameof(View), new {storeId, appId = app.Id});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpGet("plugins/TicketTailor/{appId}")]
|
||||||
|
public async Task<IActionResult> View(string appId)
|
||||||
|
{
|
||||||
|
var app = await _appService.GetApp(appId, TicketTailorApp.AppType);
|
||||||
|
if (app is null)
|
||||||
|
return NotFound();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var config = app.GetSettings<TicketTailorSettings>();
|
||||||
if (config?.ApiKey is not null && config?.EventId is not null)
|
if (config?.ApiKey is not null && config?.EventId is not null)
|
||||||
{
|
{
|
||||||
var client = new TicketTailorClient(_httpClientFactory, config.ApiKey);
|
var client = new TicketTailorClient(_httpClientFactory, config.ApiKey);
|
||||||
@@ -49,18 +94,23 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
}
|
}
|
||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpPost("")]
|
[HttpPost("plugins/TicketTailor/{appId}")]
|
||||||
public async Task<IActionResult> Purchase(string storeId, TicketTailorViewModel request, bool preview = false)
|
public async Task<IActionResult> Purchase(string appId, TicketTailorViewModel request, bool preview = false)
|
||||||
{
|
{
|
||||||
var config = await _ticketTailorService.GetTicketTailorForStore(storeId);
|
var app = await _appService.GetApp(appId, TicketTailorApp.AppType, true);
|
||||||
|
if (app is null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
(TicketTailorClient.Hold, string error)? hold = null;
|
(TicketTailorClient.Hold, string error)? hold = null;
|
||||||
|
var config = app.GetSettings<TicketTailorSettings>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (config?.ApiKey is not null && config?.EventId is not null)
|
if (config?.ApiKey is not null && config?.EventId is not null)
|
||||||
{
|
{
|
||||||
var client = new TicketTailorClient(_httpClientFactory, config.ApiKey);
|
var client = new TicketTailorClient(_httpClientFactory, config.ApiKey);
|
||||||
var evt = await client.GetEvent(config.EventId);
|
var evt = await client.GetEvent(config.EventId);
|
||||||
if (evt is null || (!config.BypassAvailabilityCheck && (evt.Unavailable == "true" || evt.TicketsAvailable == "false")))
|
if (evt is null || (!config.BypassAvailabilityCheck &&
|
||||||
|
(evt.Unavailable == "true" || evt.TicketsAvailable == "false")))
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
@@ -70,7 +120,8 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
if (!string.IsNullOrEmpty(request.DiscountCode) && config.AllowDiscountCodes)
|
if (!string.IsNullOrEmpty(request.DiscountCode) && config.AllowDiscountCodes)
|
||||||
{
|
{
|
||||||
discountCode = await client.GetDiscountCode(request.DiscountCode);
|
discountCode = await client.GetDiscountCode(request.DiscountCode);
|
||||||
if (discountCode?.expires?.unix is not null && DateTimeOffset.FromUnixTimeSeconds(discountCode.expires.unix) < DateTimeOffset.Now)
|
if (discountCode?.expires?.unix is not null &&
|
||||||
|
DateTimeOffset.FromUnixTimeSeconds(discountCode.expires.unix) < DateTimeOffset.Now)
|
||||||
{
|
{
|
||||||
discountCode = null;
|
discountCode = null;
|
||||||
}
|
}
|
||||||
@@ -83,14 +134,17 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var ticketType = evt.TicketTypes.FirstOrDefault(type => type.Id == purchaseRequestItem.TicketTypeId);
|
|
||||||
|
var ticketType =
|
||||||
|
evt.TicketTypes.FirstOrDefault(type => type.Id == purchaseRequestItem.TicketTypeId);
|
||||||
|
|
||||||
var specificTicket =
|
var specificTicket =
|
||||||
config.SpecificTickets?.SingleOrDefault(ticket => ticketType?.Id == ticket.TicketTypeId);
|
config.SpecificTickets?.SingleOrDefault(ticket => ticketType?.Id == ticket.TicketTypeId);
|
||||||
if ((config.SpecificTickets?.Any() is true && specificTicket is null) || ticketType is null ||
|
if ((config.SpecificTickets?.Any() is true && specificTicket is null) || ticketType is null ||
|
||||||
(!string.IsNullOrEmpty(ticketType.AccessCode) &&
|
(!string.IsNullOrEmpty(ticketType.AccessCode) &&
|
||||||
!ticketType.AccessCode.Equals(request.AccessCode, StringComparison.InvariantCultureIgnoreCase)) ||
|
!ticketType.AccessCode.Equals(request.AccessCode,
|
||||||
!new []{"on_sale" , "locked"}.Contains(ticketType.Status.ToLowerInvariant())
|
StringComparison.InvariantCultureIgnoreCase)) ||
|
||||||
|
!new[] {"on_sale", "locked"}.Contains(ticketType.Status.ToLowerInvariant())
|
||||||
|| specificTicket?.Hidden is true)
|
|| specificTicket?.Hidden is true)
|
||||||
{
|
{
|
||||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||||
@@ -98,24 +152,24 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||||
Html = "The ticket was not found."
|
Html = "The ticket was not found."
|
||||||
});
|
});
|
||||||
return RedirectToAction("View", new {storeId});
|
return RedirectToAction("View", new {appId});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (purchaseRequestItem.Quantity > ticketType.MaxPerOrder ||
|
if (purchaseRequestItem.Quantity > ticketType.MaxPerOrder ||
|
||||||
purchaseRequestItem.Quantity < ticketType.MinPerOrder )
|
purchaseRequestItem.Quantity < ticketType.MinPerOrder)
|
||||||
{
|
{
|
||||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||||
{
|
{
|
||||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||||
Html = "The amount of tickets was not allowed."
|
Html = "The amount of tickets was not allowed."
|
||||||
});
|
});
|
||||||
return RedirectToAction("View", new {storeId});
|
return RedirectToAction("View", new {appId});
|
||||||
}
|
}
|
||||||
|
|
||||||
var ticketCost = ticketType.Price;
|
var ticketCost = ticketType.Price;
|
||||||
if (specificTicket is not null)
|
if (specificTicket is not null)
|
||||||
{
|
{
|
||||||
ticketCost =specificTicket.Price.GetValueOrDefault(ticketType.Price);
|
ticketCost = specificTicket.Price.GetValueOrDefault(ticketType.Price);
|
||||||
}
|
}
|
||||||
|
|
||||||
var originalTicketCost = ticketCost;
|
var originalTicketCost = ticketCost;
|
||||||
@@ -163,15 +217,14 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||||
Html = $"Could not reserve tickets because {hold.Value.error}"
|
Html = $"Could not reserve tickets because {hold.Value.error}"
|
||||||
});
|
});
|
||||||
return RedirectToAction("View", new {storeId});
|
return RedirectToAction("View", new {appId});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var btcpayClient = await CreateClient(storeId);
|
|
||||||
var redirectUrl = Request.GetAbsoluteUri(Url.Action("Receipt",
|
var redirectUrl = Request.GetAbsoluteUri(Url.Action("Receipt",
|
||||||
"TicketTailor", new {storeId, invoiceId = "kukkskukkskukks"}));
|
"TicketTailor", new {storeId = app.StoreDataId, invoiceId = "kukkskukkskukks"}));
|
||||||
redirectUrl = redirectUrl.Replace("kukkskukkskukks", "{InvoiceId}");
|
redirectUrl = redirectUrl.Replace("kukkskukkskukks", "{InvoiceId}");
|
||||||
request.Name??=string.Empty;
|
request.Name ??= string.Empty;
|
||||||
var nameSplit = request.Name.Split(" ", StringSplitOptions.RemoveEmptyEntries);
|
var nameSplit = request.Name.Split(" ", StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (config.RequireFullName && nameSplit.Length < 2)
|
if (config.RequireFullName && nameSplit.Length < 2)
|
||||||
{
|
{
|
||||||
@@ -180,7 +233,7 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||||
Html = "Please enter your full name."
|
Html = "Please enter your full name."
|
||||||
});
|
});
|
||||||
return RedirectToAction("View", new {storeId});
|
return RedirectToAction("View", new {appId});
|
||||||
}
|
}
|
||||||
|
|
||||||
request.Name = nameSplit.Length switch
|
request.Name = nameSplit.Length switch
|
||||||
@@ -189,13 +242,12 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
< 2 => $"{nameSplit} Nakamoto",
|
< 2 => $"{nameSplit} Nakamoto",
|
||||||
_ => request.Name
|
_ => request.Name
|
||||||
};
|
};
|
||||||
var inv = await btcpayClient.CreateInvoice(storeId,
|
var inv = await _uiInvoiceController.CreateInvoiceCoreRaw( new CreateInvoiceRequest()
|
||||||
new CreateInvoiceRequest()
|
|
||||||
{
|
{
|
||||||
Amount = price,
|
Amount = price,
|
||||||
Currency = evt.Currency,
|
Currency = evt.Currency,
|
||||||
Type = InvoiceType.Standard,
|
Type = InvoiceType.Standard,
|
||||||
AdditionalSearchTerms = new[] {"tickettailor", hold.Value.Item1.Id, evt.Id},
|
AdditionalSearchTerms = new[] {"tickettailor", hold.Value.Item1.Id, evt.Id, AppService.GetAppSearchTerm(app)},
|
||||||
Checkout =
|
Checkout =
|
||||||
{
|
{
|
||||||
RequiresRefundEmail = true,
|
RequiresRefundEmail = true,
|
||||||
@@ -212,20 +264,22 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
buyerName = request.Name,
|
buyerName = request.Name,
|
||||||
buyerEmail = request.Email,
|
buyerEmail = request.Email,
|
||||||
holdId = hold.Value.Item1.Id,
|
holdId = hold.Value.Item1.Id,
|
||||||
orderId="tickettailor",
|
orderId = "tickettailor",
|
||||||
|
appId,
|
||||||
discountCode,
|
discountCode,
|
||||||
discountedAmount
|
discountedAmount
|
||||||
})
|
}),
|
||||||
});
|
|
||||||
|
|
||||||
while (inv.Amount == 0 && inv.Status == InvoiceStatus.New)
|
}, app.StoreData, HttpContext.Request.GetAbsoluteRoot(),new List<string> { AppService.GetAppInternalTag(appId) }, CancellationToken.None);
|
||||||
|
|
||||||
|
while (inv.Price == 0 && inv.Status == InvoiceStatusLegacy.New)
|
||||||
{
|
{
|
||||||
if (inv.Status == InvoiceStatus.New)
|
if (inv.Status == InvoiceStatusLegacy.New)
|
||||||
inv = await btcpayClient.GetInvoice(inv.StoreId, inv.Id);
|
inv = await _invoiceRepository.GetInvoice(inv.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return inv.Status == InvoiceStatus.Settled
|
return inv.Status.ToModernStatus() == InvoiceStatus.Settled
|
||||||
? RedirectToAction("Receipt", new {storeId, invoiceId = inv.Id})
|
? RedirectToAction("Receipt", new {invoiceId = inv.Id})
|
||||||
: RedirectToAction("Checkout", "UIInvoice", new {invoiceId = inv.Id});
|
: RedirectToAction("Checkout", "UIInvoice", new {invoiceId = inv.Id});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,35 +292,45 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
});
|
});
|
||||||
if (hold?.Item1 is not null)
|
if (hold?.Item1 is not null)
|
||||||
{
|
{
|
||||||
|
|
||||||
var client = new TicketTailorClient(_httpClientFactory, config.ApiKey);
|
var client = new TicketTailorClient(_httpClientFactory, config.ApiKey);
|
||||||
await client.DeleteHold(hold?.Item1.Id);
|
await client.DeleteHold(hold?.Item1.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RedirectToAction("View", new {storeId});
|
return RedirectToAction("View", new {appId});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpGet("receipt")]
|
[HttpGet("plugins/{storeId}/TicketTailor/receipt")]
|
||||||
public async Task<IActionResult> Receipt(string storeId, string invoiceId)
|
[HttpGet("plugins/TicketTailor/{invoiceId}/receipt")]
|
||||||
|
public async Task<IActionResult> Receipt(string invoiceId)
|
||||||
{
|
{
|
||||||
var btcpayClient = await CreateClient(storeId);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = new TicketReceiptPage() {InvoiceId = invoiceId};
|
var inv =await _invoiceRepository.GetInvoice(invoiceId);
|
||||||
var invoice = await btcpayClient.GetInvoice(storeId, invoiceId);
|
if (inv is null)
|
||||||
result.Status = invoice.Status;
|
|
||||||
if (invoice.Status == InvoiceStatus.Settled &&
|
|
||||||
invoice.Metadata.TryGetValue("orderId", out var orderId) && orderId.Value<string>() == "tickettailor" &&
|
|
||||||
invoice.Metadata.TryGetValue("ticketIds", out var ticketIds))
|
|
||||||
{
|
{
|
||||||
await SetTicketTailorTicketResult(storeId, result, ticketIds.Values<string>());
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
}else if (invoice.Status == InvoiceStatus.Settled)
|
if (inv.Metadata.OrderId != "tickettailor")
|
||||||
{
|
{
|
||||||
await _ticketTailorService.CheckAndIssueTicket(invoice.Id);
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var appId = AppService.GetAppInternalTags(inv).First();
|
||||||
|
|
||||||
|
var result = new TicketReceiptPage() {InvoiceId = invoiceId};
|
||||||
|
result.Status = inv.Status.ToModernStatus();
|
||||||
|
if (result.Status == InvoiceStatus.Settled &&
|
||||||
|
inv.Metadata.AdditionalData.TryGetValue("ticketIds", out var ticketIds))
|
||||||
|
{
|
||||||
|
await SetTicketTailorTicketResult(appId, result, ticketIds.Values<string>());
|
||||||
|
}
|
||||||
|
else if (inv.Status.ToModernStatus() == InvoiceStatus.Settled)
|
||||||
|
{
|
||||||
|
await _ticketTailorService.CheckAndIssueTicket(inv.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return View(result);
|
return View(result);
|
||||||
@@ -277,9 +341,12 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetTicketTailorTicketResult(string storeId, TicketReceiptPage result, IEnumerable<string> ticketIds)
|
private async Task SetTicketTailorTicketResult(string appId, TicketReceiptPage result,
|
||||||
|
IEnumerable<string> ticketIds)
|
||||||
{
|
{
|
||||||
var settings = await _ticketTailorService.GetTicketTailorForStore(storeId);
|
var app = await _appService.GetApp(appId, TicketTailorApp.AppType);
|
||||||
|
|
||||||
|
var settings = app.GetSettings<TicketTailorSettings>();
|
||||||
var client = new TicketTailorClient(_httpClientFactory, settings.ApiKey);
|
var client = new TicketTailorClient(_httpClientFactory, settings.ApiKey);
|
||||||
var tickets = await Task.WhenAll(ticketIds.Select(s => client.GetTicket(s)));
|
var tickets = await Task.WhenAll(ticketIds.Select(s => client.GetTicket(s)));
|
||||||
var evt = await client.GetEvent(settings.EventId);
|
var evt = await client.GetEvent(settings.EventId);
|
||||||
@@ -288,19 +355,6 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
result.Settings = settings;
|
result.Settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<BTCPayServerClient> CreateClient(string storeId)
|
|
||||||
{
|
|
||||||
return await _btcPayServerClientFactory.Create(null, new[] {storeId}, new DefaultHttpContext()
|
|
||||||
{
|
|
||||||
Request =
|
|
||||||
{
|
|
||||||
Scheme = "https",
|
|
||||||
Host = Request.Host,
|
|
||||||
Path = Request.Path,
|
|
||||||
PathBase = Request.PathBase
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TicketReceiptPage
|
public class TicketReceiptPage
|
||||||
{
|
{
|
||||||
@@ -312,39 +366,28 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
[HttpGet("~/plugins/TicketTailor/{appId}/update")]
|
||||||
private readonly TicketTailorService _ticketTailorService;
|
public async Task<IActionResult> UpdateTicketTailorSettings(string appId)
|
||||||
private readonly IBTCPayServerClientFactory _btcPayServerClientFactory;
|
|
||||||
|
|
||||||
public TicketTailorController(IHttpClientFactory httpClientFactory,
|
|
||||||
TicketTailorService ticketTailorService,
|
|
||||||
IBTCPayServerClientFactory btcPayServerClientFactory)
|
|
||||||
{
|
|
||||||
|
|
||||||
_httpClientFactory = httpClientFactory;
|
|
||||||
_ticketTailorService = ticketTailorService;
|
|
||||||
_btcPayServerClientFactory = btcPayServerClientFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("update")]
|
|
||||||
public async Task<IActionResult> UpdateTicketTailorSettings(string storeId)
|
|
||||||
{
|
{
|
||||||
UpdateTicketTailorSettingsViewModel vm = new();
|
UpdateTicketTailorSettingsViewModel vm = new();
|
||||||
TicketTailorSettings TicketTailor;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TicketTailor = await _ticketTailorService.GetTicketTailorForStore(storeId);
|
var app = await _appService.GetApp(appId, TicketTailorApp.AppType, false, true);
|
||||||
if (TicketTailor is not null)
|
TicketTailorSettings tt = app.GetSettings<TicketTailorSettings>();
|
||||||
|
if (tt is not null)
|
||||||
{
|
{
|
||||||
vm.ApiKey = TicketTailor.ApiKey;
|
vm.ApiKey = tt.ApiKey;
|
||||||
vm.EventId = TicketTailor.EventId;
|
vm.EventId = tt.EventId;
|
||||||
vm.ShowDescription = TicketTailor.ShowDescription;
|
vm.ShowDescription = tt.ShowDescription;
|
||||||
vm.BypassAvailabilityCheck = TicketTailor.BypassAvailabilityCheck;
|
vm.BypassAvailabilityCheck = tt.BypassAvailabilityCheck;
|
||||||
vm.CustomCSS = TicketTailor.CustomCSS;
|
vm.CustomCSS = tt.CustomCSS;
|
||||||
vm.RequireFullName = TicketTailor.RequireFullName;
|
vm.RequireFullName = tt.RequireFullName;
|
||||||
vm.AllowDiscountCodes = TicketTailor.AllowDiscountCodes;
|
vm.AllowDiscountCodes = tt.AllowDiscountCodes;
|
||||||
vm.SpecificTickets = TicketTailor.SpecificTickets;
|
vm.SpecificTickets = tt.SpecificTickets;
|
||||||
}
|
}
|
||||||
|
vm.Archived = app.Archived;
|
||||||
|
vm.AppName = app.Name;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@@ -402,8 +445,8 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpPost("update")]
|
[HttpPost("~/plugins/TicketTailor/{appId}/update")]
|
||||||
public async Task<IActionResult> UpdateTicketTailorSettings(string storeId,
|
public async Task<IActionResult> UpdateTicketTailorSettings(string appId,
|
||||||
UpdateTicketTailorSettingsViewModel vm,
|
UpdateTicketTailorSettingsViewModel vm,
|
||||||
string command)
|
string command)
|
||||||
{
|
{
|
||||||
@@ -429,6 +472,7 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
{
|
{
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelState.Clear();
|
ModelState.Clear();
|
||||||
var settings = new TicketTailorSettings()
|
var settings = new TicketTailorSettings()
|
||||||
{
|
{
|
||||||
@@ -446,15 +490,16 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
switch (command?.ToLowerInvariant())
|
switch (command?.ToLowerInvariant())
|
||||||
{
|
{
|
||||||
case "save":
|
case "save":
|
||||||
await _ticketTailorService.SetTicketTailorForStore(storeId, settings);
|
var app = await _appService.GetApp(appId, TicketTailorApp.AppType, false, true);
|
||||||
|
app.SetSettings(settings);
|
||||||
|
app.Name = vm.AppName;
|
||||||
|
await _appService.UpdateOrCreateApp(app);
|
||||||
TempData["SuccessMessage"] = "TicketTailor settings modified";
|
TempData["SuccessMessage"] = "TicketTailor settings modified";
|
||||||
return RedirectToAction(nameof(UpdateTicketTailorSettings), new {storeId});
|
return RedirectToAction(nameof(UpdateTicketTailorSettings), new {appId});
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using BTCPayServer.Abstractions.Contracts;
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
using BTCPayServer.Abstractions.Models;
|
using BTCPayServer.Abstractions.Models;
|
||||||
using BTCPayServer.Abstractions.Services;
|
using BTCPayServer.Abstractions.Services;
|
||||||
|
using BTCPayServer.Services.Apps;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.TicketTailor
|
namespace BTCPayServer.Plugins.TicketTailor
|
||||||
@@ -9,16 +11,17 @@ namespace BTCPayServer.Plugins.TicketTailor
|
|||||||
{
|
{
|
||||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||||
{
|
{
|
||||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"}
|
||||||
};
|
};
|
||||||
|
|
||||||
public override void Execute(IServiceCollection applicationBuilder)
|
public override void Execute(IServiceCollection applicationBuilder)
|
||||||
{
|
{
|
||||||
|
applicationBuilder.AddStartupTask<AppMigrate>();
|
||||||
applicationBuilder.AddSingleton<TicketTailorService>();
|
applicationBuilder.AddSingleton<TicketTailorService>();
|
||||||
applicationBuilder.AddHostedService(s=>s.GetRequiredService<TicketTailorService>());
|
applicationBuilder.AddHostedService(s => s.GetRequiredService<TicketTailorService>());
|
||||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("TicketTailor/StoreIntegrationTicketTailorOption",
|
|
||||||
"store-integrations-list"));
|
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("TicketTailor/NavExtension", "header-nav"));
|
||||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("TicketTailor/TicketTailorNav",
|
applicationBuilder.AddSingleton<AppBaseType, TicketTailorApp>();
|
||||||
"store-integrations-nav"));
|
|
||||||
base.Execute(applicationBuilder);
|
base.Execute(applicationBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using BTCPayServer.Data;
|
|||||||
using BTCPayServer.Events;
|
using BTCPayServer.Events;
|
||||||
using BTCPayServer.HostedServices;
|
using BTCPayServer.HostedServices;
|
||||||
using BTCPayServer.Logging;
|
using BTCPayServer.Logging;
|
||||||
|
using BTCPayServer.Services.Apps;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Mails;
|
using BTCPayServer.Services.Mails;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@@ -24,63 +25,33 @@ namespace BTCPayServer.Plugins.TicketTailor;
|
|||||||
|
|
||||||
public class TicketTailorService : EventHostedServiceBase
|
public class TicketTailorService : EventHostedServiceBase
|
||||||
{
|
{
|
||||||
private readonly ISettingsRepository _settingsRepository;
|
|
||||||
private readonly IMemoryCache _memoryCache;
|
private readonly IMemoryCache _memoryCache;
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly IStoreRepository _storeRepository;
|
|
||||||
private readonly ILogger<TicketTailorService> _logger;
|
private readonly ILogger<TicketTailorService> _logger;
|
||||||
|
|
||||||
private readonly EmailSenderFactory _emailSenderFactory;
|
private readonly EmailSenderFactory _emailSenderFactory;
|
||||||
|
|
||||||
private readonly LinkGenerator _linkGenerator;
|
private readonly LinkGenerator _linkGenerator;
|
||||||
private readonly InvoiceRepository _invoiceRepository;
|
private readonly InvoiceRepository _invoiceRepository;
|
||||||
|
private readonly AppService _appService;
|
||||||
|
|
||||||
public TicketTailorService(ISettingsRepository settingsRepository, IMemoryCache memoryCache,
|
public TicketTailorService(IMemoryCache memoryCache,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IStoreRepository storeRepository, ILogger<TicketTailorService> logger,
|
ILogger<TicketTailorService> logger,
|
||||||
EmailSenderFactory emailSenderFactory ,
|
EmailSenderFactory emailSenderFactory ,
|
||||||
LinkGenerator linkGenerator,
|
LinkGenerator linkGenerator,
|
||||||
EventAggregator eventAggregator, InvoiceRepository invoiceRepository) : base(eventAggregator, logger)
|
EventAggregator eventAggregator, InvoiceRepository invoiceRepository,
|
||||||
|
AppService appService) : base(eventAggregator, logger)
|
||||||
{
|
{
|
||||||
_settingsRepository = settingsRepository;
|
|
||||||
_memoryCache = memoryCache;
|
_memoryCache = memoryCache;
|
||||||
_httpClientFactory = httpClientFactory;
|
_httpClientFactory = httpClientFactory;
|
||||||
_storeRepository = storeRepository;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_emailSenderFactory = emailSenderFactory;
|
_emailSenderFactory = emailSenderFactory;
|
||||||
_linkGenerator = linkGenerator;
|
_linkGenerator = linkGenerator;
|
||||||
_invoiceRepository = invoiceRepository;
|
_invoiceRepository = invoiceRepository;
|
||||||
|
_appService = appService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<TicketTailorSettings> GetTicketTailorForStore(string storeId)
|
|
||||||
{
|
|
||||||
var k = $"{nameof(TicketTailorSettings)}_{storeId}";
|
|
||||||
return await _memoryCache.GetOrCreateAsync(k, async _ =>
|
|
||||||
{
|
|
||||||
var res = await _storeRepository.GetSettingAsync<TicketTailorSettings>(storeId,
|
|
||||||
nameof(TicketTailorSettings));
|
|
||||||
if (res is not null) return res;
|
|
||||||
res = await _settingsRepository.GetSettingAsync<TicketTailorSettings>(k);
|
|
||||||
|
|
||||||
if (res is not null)
|
|
||||||
{
|
|
||||||
await SetTicketTailorForStore(storeId, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _settingsRepository.UpdateSetting<TicketTailorSettings>(null, k);
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SetTicketTailorForStore(string storeId, TicketTailorSettings TicketTailorSettings)
|
|
||||||
{
|
|
||||||
var k = $"{nameof(TicketTailorSettings)}_{storeId}";
|
|
||||||
await _storeRepository.UpdateSetting(storeId, nameof(TicketTailorSettings), TicketTailorSettings);
|
|
||||||
_memoryCache.Set(k, TicketTailorSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private class IssueTicket
|
private class IssueTicket
|
||||||
{
|
{
|
||||||
public InvoiceEntity Invoice { get; set; }
|
public InvoiceEntity Invoice { get; set; }
|
||||||
@@ -153,7 +124,9 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
{
|
{
|
||||||
|
|
||||||
var invLogs = new InvoiceLogs();
|
var invLogs = new InvoiceLogs();
|
||||||
var settings = await GetTicketTailorForStore(issueTicket.Invoice.StoreId);
|
var appId = AppService.GetAppInternalTags(issueTicket.Invoice).First();
|
||||||
|
var app = await _appService.GetApp(appId, TicketTailorApp.AppType);
|
||||||
|
var settings = app.GetSettings<TicketTailorSettings>();
|
||||||
var invoice = issueTicket.Invoice;
|
var invoice = issueTicket.Invoice;
|
||||||
if (settings?.ApiKey is null)
|
if (settings?.ApiKey is null)
|
||||||
{
|
{
|
||||||
@@ -269,7 +242,7 @@ public class TicketTailorService : EventHostedServiceBase
|
|||||||
var url =
|
var url =
|
||||||
_linkGenerator.GetUriByAction("Receipt",
|
_linkGenerator.GetUriByAction("Receipt",
|
||||||
"TicketTailor",
|
"TicketTailor",
|
||||||
new {issueTicket.Invoice.StoreId, invoiceId = invoice.Id},
|
new {invoiceId = invoice.Id},
|
||||||
uri.Scheme,
|
uri.Scheme,
|
||||||
new HostString(uri.Host),
|
new HostString(uri.Host),
|
||||||
uri.AbsolutePath);
|
uri.AbsolutePath);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ namespace BTCPayServer.Plugins.TicketTailor;
|
|||||||
|
|
||||||
public class UpdateTicketTailorSettingsViewModel
|
public class UpdateTicketTailorSettingsViewModel
|
||||||
{
|
{
|
||||||
|
public string AppName { get; set; }
|
||||||
|
public bool Archived { get; set; }
|
||||||
public string NewSpecificTicket { get; set; }
|
public string NewSpecificTicket { get; set; }
|
||||||
public string ApiKey { get; set; }
|
public string ApiKey { get; set; }
|
||||||
public SelectList Events { get; set; }
|
public SelectList Events { get; set; }
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
@using BTCPayServer.Client
|
||||||
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@using BTCPayServer.Views.Apps
|
||||||
|
@using BTCPayServer.Services.Apps
|
||||||
|
@using BTCPayServer
|
||||||
|
@using BTCPayServer.Abstractions.Extensions
|
||||||
|
@using BTCPayServer.Plugins.TicketTailor
|
||||||
|
@inject AppService AppService;
|
||||||
|
@model BTCPayServer.Components.MainNav.MainNavViewModel
|
||||||
|
@{
|
||||||
|
var store = Context.GetStoreData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (store != null)
|
||||||
|
{
|
||||||
|
var appType = AppService.GetAppType(TicketTailorApp.AppType)!;
|
||||||
|
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
||||||
|
<a asp-area="" asp-controller="UIApps" asp-action="CreateApp" asp-route-storeId="@store.Id" asp-route-appType="@appType.Type" class="nav-link @ViewData.IsActivePage(AppsNavPages.Create, appType.Type)" id="@($"StoreNav-Create{appType.Type}")">
|
||||||
|
<img style="width:14px; margin-right: 10px;" class="icon" src="~/Resources/assets/tt.png" />
|
||||||
|
<span>@appType.Description</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
@foreach (var app in Model.Apps.Where(app => app.AppType == appType.Type))
|
||||||
|
{
|
||||||
|
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
|
||||||
|
<a asp-area="" asp-controller="TicketTailor" asp-action="UpdateTicketTailorSettings" asp-route-appId="@app.Id" class="nav-link @ViewData.IsActivePage(AppsNavPages.Update, app.Id)" id="@($"StoreNav-App-{app.Id}")">
|
||||||
|
<span>@app.AppName</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item nav-item-sub" not-permission="@Policies.CanModifyStoreSettings">
|
||||||
|
<a asp-area="" asp-controller="TicketTailor" asp-action="View" asp-route-appId="@app.Id" class="nav-link">
|
||||||
|
<span>@app.AppName</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
@using BTCPayServer.Client
|
|
||||||
@using BTCPayServer.Plugins.TicketTailor
|
|
||||||
@using Microsoft.AspNetCore.Routing
|
|
||||||
@using BTCPayServer.Abstractions.Contracts
|
|
||||||
@inject IScopeProvider ScopeProvider
|
|
||||||
@inject TicketTailorService TicketTailorService
|
|
||||||
@{
|
|
||||||
var storeId = ScopeProvider.GetCurrentStoreId();
|
|
||||||
|
|
||||||
TicketTailorSettings settings = null;
|
|
||||||
if (!string.IsNullOrEmpty(storeId))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
settings = await TicketTailorService.GetTicketTailorForStore(storeId);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@if (!string.IsNullOrEmpty(storeId))
|
|
||||||
{
|
|
||||||
<li class="list-group-item bg-tile ">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<span class="d-flex flex-wrap flex-fill flex-column flex-sm-row">
|
|
||||||
<strong class="me-3">
|
|
||||||
Ticket Tailor
|
|
||||||
</strong>
|
|
||||||
<span title="" class="d-flex me-3">
|
|
||||||
<span class="text-secondary">Sell tickets on Ticket Tailor using BTCPay Server</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span class="d-flex align-items-center fw-semibold">
|
|
||||||
@if (settings?.ApiKey is not null)
|
|
||||||
{
|
|
||||||
<span class="d-flex align-items-center text-success">
|
|
||||||
<span class="me-2 btcpay-status btcpay-status--enabled"></span>
|
|
||||||
Active
|
|
||||||
</span>
|
|
||||||
<span class="text-light ms-3 me-2">|</span>
|
|
||||||
<a lass="btn btn-link px-1 py-1 fw-semibold" asp-controller="TicketTailor" asp-action="UpdateTicketTailorSettings" asp-route-storeId="@storeId">
|
|
||||||
Modify
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<span class="d-flex align-items-center text-danger">
|
|
||||||
<span class="me-2 btcpay-status btcpay-status--disabled"></span>
|
|
||||||
Disabled
|
|
||||||
</span>
|
|
||||||
<a class="btn btn-primary btn-sm ms-4 px-3 py-1 fw-semibold" asp-controller="TicketTailor" asp-action="UpdateTicketTailorSettings" asp-route-storeId="@storeId">
|
|
||||||
Setup
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
@using BTCPayServer.Plugins.TicketTailor
|
|
||||||
@inject IScopeProvider ScopeProvider
|
|
||||||
@using BTCPayServer.Abstractions.Contracts
|
|
||||||
@{
|
|
||||||
var storeId = ScopeProvider.GetCurrentStoreId();
|
|
||||||
var isActive = !string.IsNullOrEmpty(storeId) && ViewContext.RouteData.Values.TryGetValue("Controller", out var controller) && controller is not null &&
|
|
||||||
nameof(TicketTailorController).StartsWith(controller?.ToString(), StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
|
||||||
@if (!string.IsNullOrEmpty(storeId))
|
|
||||||
{
|
|
||||||
<li class="nav-item">
|
|
||||||
<a asp-area="" asp-controller="TicketTailor" asp-action="UpdateTicketTailorSettings" asp-route-storeId="@storeId" class="nav-link js-scroll-trigger @(isActive? "active": string.Empty)">
|
|
||||||
<img style="width:14px; margin-right: 10px;" class="icon" src="~/Resources/assets/tt.png" />
|
|
||||||
<span>TicketTailor</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
@using BTCPayServer.Client.Models
|
@using BTCPayServer.Client.Models
|
||||||
@model BTCPayServer.Plugins.TicketTailor.TicketTailorController.TicketReceiptPage
|
@model BTCPayServer.Plugins.TicketTailor.TicketTailorController.TicketReceiptPage
|
||||||
@inject ContentSecurityPolicies contentSecurityPolicies
|
|
||||||
@using BTCPayServer.Security
|
|
||||||
@using NBitcoin
|
|
||||||
@{
|
@{
|
||||||
var nonce = RandomUtils.GetUInt256().ToString().Substring(0, 32);
|
|
||||||
contentSecurityPolicies.Add("script-src", $"'nonce-{nonce}'");
|
|
||||||
contentSecurityPolicies.AllowUnsafeHashes();
|
|
||||||
Layout = "_LayoutSimple";
|
Layout = "_LayoutSimple";
|
||||||
var reloadPage = false;
|
var reloadPage = false;
|
||||||
}
|
}
|
||||||
@@ -162,14 +156,14 @@
|
|||||||
|
|
||||||
@if (reloadPage)
|
@if (reloadPage)
|
||||||
{
|
{
|
||||||
<script type="text/javascript" nonce="@nonce">
|
<script type="text/javascript" >
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
|
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
<script type="text/javascript" nonce="@nonce">
|
<script type="text/javascript" >
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
document.getElementById("btnPrint").addEventListener("click", function (){
|
document.getElementById("btnPrint").addEventListener("click", function (){
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
@using BTCPayServer.Plugins.TicketTailor
|
@using BTCPayServer.Plugins.TicketTailor
|
||||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
@using BTCPayServer.Abstractions.Contracts
|
@using BTCPayServer.Abstractions.Extensions
|
||||||
|
@using BTCPayServer.Views.Apps
|
||||||
|
@using Microsoft.AspNetCore.Routing
|
||||||
|
@using BTCPayServer
|
||||||
|
@using BTCPayServer.Abstractions.Models
|
||||||
|
@using BTCPayServer.Services.Apps
|
||||||
@model BTCPayServer.Plugins.TicketTailor.UpdateTicketTailorSettingsViewModel
|
@model BTCPayServer.Plugins.TicketTailor.UpdateTicketTailorSettingsViewModel
|
||||||
@inject IScopeProvider ScopeProvider
|
|
||||||
@{
|
@{
|
||||||
var storeId = ScopeProvider.GetCurrentStoreId();
|
var appId = Context.GetRouteValue("appId").ToString();
|
||||||
ViewData.SetActivePage("TicketTailor", "Update Store TicketTailor Settings", null);
|
var storeId = Context.GetCurrentStoreId();
|
||||||
|
ViewData.SetActivePage(AppsNavPages.Update.ToString(), typeof(AppsNavPages).ToString(), "Update Ticket Tailor app", appId);
|
||||||
Model.SpecificTickets ??= new List<SpecificTicket>();
|
Model.SpecificTickets ??= new List<SpecificTicket>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,9 +22,13 @@
|
|||||||
<h2 class="mb-0">@ViewData["Title"]</h2>
|
<h2 class="mb-0">@ViewData["Title"]</h2>
|
||||||
<div class="d-flex gap-3 mt-3 mt-sm-0">
|
<div class="d-flex gap-3 mt-3 mt-sm-0">
|
||||||
<input type="submit" value="Save" name="command" class="btn btn-primary"/>
|
<input type="submit" value="Save" name="command" class="btn btn-primary"/>
|
||||||
@if (this.ViewContext.ModelState.IsValid && Model.EventId is not null)
|
@if (Model.Archived)
|
||||||
{
|
{
|
||||||
<a class="btn btn-secondary" href=" @Url.Action("View", "TicketTailor", new {storeId})">
|
<button type="button" class="btn btn-outline-secondary" onclick="document.getElementById('btn-archive-toggle').click()">Unarchive</button>
|
||||||
|
}
|
||||||
|
else if (this.ViewContext.ModelState.IsValid && Model.EventId is not null)
|
||||||
|
{
|
||||||
|
<a class="btn btn-secondary" target="_blank" href=" @Url.Action("View", "TicketTailor", new {appId})">
|
||||||
Ticket purchase page
|
Ticket purchase page
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
@@ -32,14 +41,18 @@
|
|||||||
@if (ViewContext.ModelState.IsValid && Model.EventId is not null)
|
@if (ViewContext.ModelState.IsValid && Model.EventId is not null)
|
||||||
{
|
{
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
Please ensure that emails in your store are configured if you wish to send the tickets via email to customers as TicketTailor does not handle it for tickets issued via its API.
|
Please ensure that emails in your store are configured if you wish to send the tickets via email to customers as TicketTailor does not handle it for tickets issued via its API. <a asp-action="StoreEmails" asp-controller="UIStores" asp-route-storeId="@storeId" class="alert-link">Configure here.</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xl-8 col-xxl-constrain">
|
<div class="col-xl-8 col-xxl-constrain">
|
||||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="AppName" class="form-label" data-required>App name</label>
|
||||||
|
<input asp-for="AppName" class="form-control" required/>
|
||||||
|
<span asp-validation-for="AppName" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="ApiKey" class="form-label" data-required>TicketTailor API Key</label>
|
<label asp-for="ApiKey" class="form-label" data-required>TicketTailor API Key</label>
|
||||||
<input asp-for="ApiKey" class="form-control" required/>
|
<input asp-for="ApiKey" class="form-control" required/>
|
||||||
@@ -144,4 +157,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="d-grid d-sm-flex flex-wrap gap-3 mt-3">
|
||||||
|
<a class="btn btn-secondary" asp-action="ListInvoices" asp-controller="UIInvoice" asp-route-storeId="@storeId" asp-route-searchterm="@AppService.GetAppSearchTerm(TicketTailorApp.AppType, appId)">Invoices</a>
|
||||||
|
<form method="post" asp-controller="UIApps" asp-action="ToggleArchive" asp-route-appId="@appId">
|
||||||
|
<button type="submit" class="w-100 btn btn-outline-secondary" id="btn-archive-toggle" >
|
||||||
|
@if (Model.Archived)
|
||||||
|
{
|
||||||
|
<span class="text-nowrap">Unarchive this app</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="text-nowrap" data-bs-toggle="tooltip" title="Archive this app so that it does not appear in the apps list by default">Archive this app</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<a id="DeleteApp" class="btn btn-outline-danger" asp-controller="UIApps" asp-action="DeleteApp" asp-route-appId="@appId" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The app and its settings will be permanently deleted." data-confirm-input="DELETE">Delete this app</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<partial name="_Confirm" model="@(new ConfirmModel("Delete app", "This app will be removed from this store.", "Delete"))" />
|
||||||
|
|
||||||
<partial name="_ValidationScriptsPartial"/>
|
<partial name="_ValidationScriptsPartial"/>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
@model BTCPayServer.Plugins.TicketTailor.TicketTailorViewModel
|
@model BTCPayServer.Plugins.TicketTailor.TicketTailorViewModel
|
||||||
@{
|
@{
|
||||||
var storeId = Context.GetRouteValue("storeId");
|
var appId = Context.GetRouteValue("appId");
|
||||||
Layout = "_LayoutSimple";
|
Layout = "_LayoutSimple";
|
||||||
var available = Model.Settings.BypassAvailabilityCheck || (Model.Event.Unavailable != "true" && Model.Event.TicketsAvailable == "true");
|
var available = Model.Settings.BypassAvailabilityCheck || (Model.Event.Unavailable != "true" && Model.Event.TicketsAvailable == "true");
|
||||||
Model.Settings.SpecificTickets ??= new List<SpecificTicket>();
|
Model.Settings.SpecificTickets ??= new List<SpecificTicket>();
|
||||||
@@ -70,7 +70,7 @@ document.addEventListener("DOMContentLoaded", ()=>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xhttp.open("POST", "@Url.Action("Purchase", new {storeId, preview = true})", true);
|
xhttp.open("POST", "@Url.Action("Purchase", new {appId, preview = true})", true);
|
||||||
xhttp.send(data);
|
xhttp.send(data);
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@@ -92,7 +92,7 @@ document.addEventListener("DOMContentLoaded", ()=>{
|
|||||||
<div class="overflow-hidden col-12 ">@Safe.Raw(Model.Event.Description)</div>
|
<div class="overflow-hidden col-12 ">@Safe.Raw(Model.Event.Description)</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<form method="post" asp-controller="TicketTailor" asp-action="Purchase" asp-antiforgery="false" asp-route-storeId="@storeId">
|
<form method="post" asp-controller="TicketTailor" asp-action="Purchase" asp-antiforgery="false" asp-route-appId="@appId">
|
||||||
<input type="hidden" asp-for="AccessCode" value="@accessCode"/>
|
<input type="hidden" asp-for="AccessCode" value="@accessCode"/>
|
||||||
<div class="row g-2 justify-content-center mb-4" id="ticket-form-container">
|
<div class="row g-2 justify-content-center mb-4" id="ticket-form-container">
|
||||||
<div class="col-sm-6 col-md-4">
|
<div class="col-sm-6 col-md-4">
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
@using BTCPayServer.Abstractions.Extensions
|
@using BTCPayServer.Abstractions.Services
|
||||||
@inject BTCPayServer.Abstractions.Services.Safe Safe
|
@inject Safe Safe
|
||||||
@addTagHelper *, BTCPayServer.Abstractions
|
|
||||||
@addTagHelper *, BTCPayServer.TagHelpers
|
|
||||||
@addTagHelper *, BTCPayServer.Views.TagHelpers
|
|
||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
|
||||||
|
|
||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
@addTagHelper *, BTCPayServer
|
@addTagHelper *, BTCPayServer
|
||||||
|
@addTagHelper *, BTCPayServer.Abstractions
|
||||||
Submodule submodules/btcpayserver updated: 7066a2a577...3f344f2c0c
Reference in New Issue
Block a user