mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2026-01-04 06:34:29 +01:00
Pluginize Webhooks and support Payouts (#5421)
Co-authored-by: d11n <mail@dennisreimann.de>
This commit is contained in:
@@ -146,9 +146,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
return PaymentRequestNotFound();
|
||||
}
|
||||
|
||||
var updatedPr = pr.First();
|
||||
updatedPr.Archived = true;
|
||||
await _paymentRequestRepository.CreateOrUpdatePaymentRequest(updatedPr);
|
||||
await _paymentRequestRepository.ArchivePaymentRequest(pr.First().Id);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.HostedServices.Webhooks;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Google.Apis.Auth.OAuth2;
|
||||
|
||||
@@ -12,6 +12,7 @@ using BTCPayServer.Services;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.HostedServices.Webhooks;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Form;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
@@ -11,7 +10,6 @@ using BTCPayServer.Data;
|
||||
using BTCPayServer.Filters;
|
||||
using BTCPayServer.Forms;
|
||||
using BTCPayServer.Forms.Models;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.PaymentRequestViewModels;
|
||||
using BTCPayServer.PaymentRequest;
|
||||
using BTCPayServer.Services;
|
||||
@@ -22,7 +20,6 @@ using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PaymentRequestData = BTCPayServer.Data.PaymentRequestData;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
@@ -115,7 +112,8 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var prInvoices = payReqId is null ? null : (await _PaymentRequestService.GetPaymentRequest(payReqId, GetUserId())).Invoices;
|
||||
var vm = new UpdatePaymentRequestViewModel(paymentRequest)
|
||||
{
|
||||
@@ -123,7 +121,9 @@ namespace BTCPayServer.Controllers
|
||||
AmountAndCurrencyEditable = payReqId is null || !prInvoices.Any()
|
||||
};
|
||||
|
||||
vm.Currency ??= store.GetStoreBlob().DefaultCurrency;
|
||||
vm.Currency ??= storeBlob.DefaultCurrency;
|
||||
vm.HasEmailRules = storeBlob.EmailRules?.Any(rule =>
|
||||
rule.Trigger.Contains("PaymentRequest", StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
return View(nameof(EditPaymentRequest), vm);
|
||||
}
|
||||
@@ -162,10 +162,12 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
viewModel.HasEmailRules = storeBlob.EmailRules?.Any(rule =>
|
||||
rule.Trigger.Contains("PaymentRequest", StringComparison.InvariantCultureIgnoreCase));
|
||||
return View(nameof(EditPaymentRequest), viewModel);
|
||||
}
|
||||
|
||||
|
||||
blob.Title = viewModel.Title;
|
||||
blob.Email = viewModel.Email;
|
||||
blob.Description = viewModel.Description;
|
||||
@@ -413,13 +415,11 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<IActionResult> TogglePaymentRequestArchival(string payReqId)
|
||||
{
|
||||
var store = GetCurrentStore();
|
||||
var result = await EditPaymentRequest(store.Id, payReqId);
|
||||
if (result is ViewResult viewResult)
|
||||
|
||||
var result = await _PaymentRequestRepository.ArchivePaymentRequest(payReqId, true);
|
||||
if(result is not null)
|
||||
{
|
||||
var model = (UpdatePaymentRequestViewModel)viewResult.Model;
|
||||
model.Archived = !model.Archived;
|
||||
await EditPaymentRequest(payReqId, model);
|
||||
TempData[WellKnownTempData.SuccessMessage] = model.Archived
|
||||
TempData[WellKnownTempData.SuccessMessage] = result.Value
|
||||
? "The payment request has been archived and will no longer appear in the payment request list by default again."
|
||||
: "The payment request has been unarchived and will appear in the payment request list by default.";
|
||||
return RedirectToAction("GetPaymentRequests", new { storeId = store.Id });
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
@@ -19,20 +20,25 @@ namespace BTCPayServer.Controllers
|
||||
public partial class UIStoresController
|
||||
{
|
||||
[HttpGet("{storeId}/emails")]
|
||||
public IActionResult StoreEmails(string storeId)
|
||||
public async Task<IActionResult> StoreEmails(string storeId)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
|
||||
var blob = store.GetStoreBlob();
|
||||
var data = blob.EmailSettings;
|
||||
if (data?.IsComplete() is not true)
|
||||
var storeSetupComplete = blob.EmailSettings?.IsComplete() is true;
|
||||
if (!storeSetupComplete && !TempData.HasStatusMessage())
|
||||
{
|
||||
var emailSender = await _emailSenderFactory.GetEmailSender(store.Id) as StoreEmailSender;
|
||||
var hasServerFallback = await IsSetupComplete(emailSender?.FallbackSender);
|
||||
var message = hasServerFallback
|
||||
? "Emails will be sent with the email settings of the server"
|
||||
: "You need to configure email settings before this feature works";
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
Html = $"You need to configure email settings before this feature works. <a class='alert-link' href='{Url.Action("StoreEmailSettings", new { storeId })}'>Configure now</a>."
|
||||
Severity = hasServerFallback ? StatusMessageModel.StatusSeverity.Info : StatusMessageModel.StatusSeverity.Warning,
|
||||
Html = $"{message}. <a class='alert-link' href='{Url.Action("StoreEmailSettings", new { storeId })}'>Configure store email settings</a>."
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,17 +50,17 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<IActionResult> StoreEmails(string storeId, StoreEmailRuleViewModel vm, string command)
|
||||
{
|
||||
vm.Rules ??= new List<StoreEmailRule>();
|
||||
int index = 0;
|
||||
int commandIndex = 0;
|
||||
var indSep = command.IndexOf(":", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (indSep > 0)
|
||||
{
|
||||
var item = command[(indSep + 1)..];
|
||||
index = int.Parse(item, CultureInfo.InvariantCulture);
|
||||
commandIndex = int.Parse(item, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (command.StartsWith("remove", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
vm.Rules.RemoveAt(index);
|
||||
vm.Rules.RemoveAt(commandIndex);
|
||||
}
|
||||
else if (command == "add")
|
||||
{
|
||||
@@ -63,7 +69,7 @@ namespace BTCPayServer.Controllers
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
for (var i = 0; index < vm.Rules.Count; index++)
|
||||
for (var i = 0; i < vm.Rules.Count; i++)
|
||||
{
|
||||
var rule = vm.Rules[i];
|
||||
if (!rule.CustomerEmail && string.IsNullOrEmpty(rule.To))
|
||||
@@ -79,41 +85,50 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
|
||||
string message = "";
|
||||
|
||||
// update rules
|
||||
var blob = store.GetStoreBlob();
|
||||
blob.EmailRules = vm.Rules;
|
||||
if (store.SetStoreBlob(blob))
|
||||
{
|
||||
await _Repo.UpdateStore(store);
|
||||
message += "Store email rules saved. ";
|
||||
}
|
||||
|
||||
if (command.StartsWith("test", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var rule = vm.Rules[index];
|
||||
try
|
||||
{
|
||||
var emailSettings = blob.EmailSettings;
|
||||
using var client = await emailSettings.CreateSmtpClient();
|
||||
var message = emailSettings.CreateMailMessage(MailboxAddress.Parse(rule.To), "(test) " + rule.Subject, rule.Body, true);
|
||||
await client.SendAsync(message);
|
||||
await client.DisconnectAsync(true);
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Rule email saved and sent to {rule.To}. Please verify you received it.";
|
||||
|
||||
blob.EmailRules = vm.Rules;
|
||||
store.SetStoreBlob(blob);
|
||||
await _Repo.UpdateStore(store);
|
||||
var rule = vm.Rules[commandIndex];
|
||||
var emailSender = await _emailSenderFactory.GetEmailSender(store.Id);
|
||||
if (await IsSetupComplete(emailSender))
|
||||
{
|
||||
emailSender.SendEmail(MailboxAddress.Parse(rule.To), $"({store.StoreName} test) {rule.Subject}", rule.Body);
|
||||
message += $"Test email sent to {rule.To} — please verify you received it.";
|
||||
}
|
||||
else
|
||||
{
|
||||
message += "Complete the email setup to send test emails.";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "Error: " + ex.Message;
|
||||
TempData[WellKnownTempData.ErrorMessage] = message + "Error sending test email: " + ex.Message;
|
||||
return RedirectToAction("StoreEmails", new { storeId });
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
// UPDATE
|
||||
blob.EmailRules = vm.Rules;
|
||||
store.SetStoreBlob(blob);
|
||||
await _Repo.UpdateStore(store);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Store email rules saved"
|
||||
Message = message
|
||||
});
|
||||
}
|
||||
|
||||
return RedirectToAction("StoreEmails", new { storeId });
|
||||
}
|
||||
|
||||
@@ -125,7 +140,7 @@ namespace BTCPayServer.Controllers
|
||||
public class StoreEmailRule
|
||||
{
|
||||
[Required]
|
||||
public WebhookEventType Trigger { get; set; }
|
||||
public string Trigger { get; set; }
|
||||
|
||||
public bool CustomerEmail { get; set; }
|
||||
|
||||
@@ -209,5 +224,10 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction(nameof(StoreEmailSettings), new { storeId });
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> IsSetupComplete(IEmailSender emailSender)
|
||||
{
|
||||
return emailSender is not null && (await emailSender.GetEmailSettings())?.IsComplete() == true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using BTCPayServer.Client;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.HostedServices.Webhooks;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
@@ -22,6 +23,7 @@ using BTCPayServer.Security.Bitpay;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Mails;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
@@ -68,7 +70,8 @@ namespace BTCPayServer.Controllers
|
||||
IOptions<LightningNetworkOptions> lightningNetworkOptions,
|
||||
IOptions<ExternalServicesOptions> externalServiceOptions,
|
||||
IHtmlHelper html,
|
||||
LightningClientFactoryService lightningClientFactoryService)
|
||||
LightningClientFactoryService lightningClientFactoryService,
|
||||
EmailSenderFactory emailSenderFactory)
|
||||
{
|
||||
_RateFactory = rateFactory;
|
||||
_Repo = repo;
|
||||
@@ -93,6 +96,7 @@ namespace BTCPayServer.Controllers
|
||||
_BTCPayEnv = btcpayEnv;
|
||||
_externalServiceOptions = externalServiceOptions;
|
||||
_lightningClientFactoryService = lightningClientFactoryService;
|
||||
_emailSenderFactory = emailSenderFactory;
|
||||
Html = html;
|
||||
}
|
||||
|
||||
@@ -116,6 +120,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly EventAggregator _EventAggregator;
|
||||
private readonly IOptions<ExternalServicesOptions> _externalServiceOptions;
|
||||
private readonly LightningClientFactoryService _lightningClientFactoryService;
|
||||
private readonly EmailSenderFactory _emailSenderFactory;
|
||||
|
||||
public string? GeneratedPairingCode { get; set; }
|
||||
public WebhookSender WebhookNotificationManager { get; }
|
||||
|
||||
Reference in New Issue
Block a user