mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
Add ability to move a payout from InProgress to AwaitingPayment (#6564)
This commit is contained in:
@@ -113,7 +113,6 @@ namespace BTCPayServer.Tests
|
|||||||
s.Driver.FindElement(By.Id("Title")).SendKeys("Pay123");
|
s.Driver.FindElement(By.Id("Title")).SendKeys("Pay123");
|
||||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("700");
|
s.Driver.FindElement(By.Id("Amount")).SendKeys("700");
|
||||||
new SelectElement(s.Driver.FindElement(By.Id("FormId"))).SelectByValue("Email");
|
new SelectElement(s.Driver.FindElement(By.Id("FormId"))).SelectByValue("Email");
|
||||||
s.Driver.TakeScreenshot().SaveAsFile("C:\\Users\\NicolasDorier\\Downloads\\chromedriver-win64\\1.png");
|
|
||||||
s.ClickPagePrimary();
|
s.ClickPagePrimary();
|
||||||
|
|
||||||
s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click();
|
s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click();
|
||||||
@@ -2175,6 +2174,55 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Contains("PP1 Edited", s.Driver.PageSource);
|
Assert.Contains("PP1 Edited", s.Driver.PageSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Selenium", "Selenium")]
|
||||||
|
public async Task CanUseAwaitProgressForInProgressPayout()
|
||||||
|
{
|
||||||
|
using var s = CreateSeleniumTester();
|
||||||
|
await s.StartAsync();
|
||||||
|
s.RegisterNewUser(true);
|
||||||
|
s.CreateNewStore();
|
||||||
|
s.GenerateWallet(isHotWallet: true);
|
||||||
|
await s.FundStoreWallet(denomination: 50.0m);
|
||||||
|
|
||||||
|
s.GoToStore(s.StoreId, StoreNavPages.PayoutProcessors);
|
||||||
|
s.Driver.FindElement(By.Id("Configure-BTC-CHAIN")).Click();
|
||||||
|
s.Driver.SetCheckbox(By.Id("ProcessNewPayoutsInstantly"), true);
|
||||||
|
s.ClickPagePrimary();
|
||||||
|
|
||||||
|
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||||
|
s.ClickPagePrimary();
|
||||||
|
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
|
||||||
|
s.Driver.FindElement(By.Id("Amount")).Clear();
|
||||||
|
s.Driver.FindElement(By.Id("Amount")).SendKeys("99.0");
|
||||||
|
s.Driver.SetCheckbox(By.Id("AutoApproveClaims"), true);
|
||||||
|
s.ClickPagePrimary();
|
||||||
|
|
||||||
|
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||||
|
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||||
|
|
||||||
|
var address = await s.Server.ExplorerNode.GetNewAddressAsync();
|
||||||
|
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString() + Keys.Enter);
|
||||||
|
s.GoToStore(s.StoreId, StoreNavPages.Payouts);
|
||||||
|
s.Driver.FindElement(By.Id("InProgress-view")).Click();
|
||||||
|
|
||||||
|
// Waiting for the payment processor to process the payment
|
||||||
|
int i = 0;
|
||||||
|
while (!s.Driver.PageSource.Contains("mass-action-select-all"))
|
||||||
|
{
|
||||||
|
s.Driver.Navigate().Refresh();
|
||||||
|
i++;
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
if (i > 10)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
s.Driver.FindElement(By.ClassName("mass-action-select-all")).Click();
|
||||||
|
|
||||||
|
s.Driver.FindElement(By.Id("InProgress-mark-awaiting-payment")).Click();
|
||||||
|
s.Driver.FindElement(By.Id("AwaitingPayment-view")).Click();
|
||||||
|
Assert.Contains("PP1", s.Driver.PageSource);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Selenium", "Selenium")]
|
[Trait("Selenium", "Selenium")]
|
||||||
[Trait("Lightning", "Lightning")]
|
[Trait("Lightning", "Lightning")]
|
||||||
|
|||||||
@@ -439,7 +439,28 @@ namespace BTCPayServer.Controllers
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "mark-awaiting-payment":
|
||||||
|
await using (var context = _dbContextFactory.CreateContext())
|
||||||
|
{
|
||||||
|
var payouts = (await PullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
|
||||||
|
{
|
||||||
|
States = new[] { PayoutState.InProgress },
|
||||||
|
Stores = new[] { storeId },
|
||||||
|
PayoutIds = payoutIds
|
||||||
|
}, context));
|
||||||
|
foreach (var payout in payouts)
|
||||||
|
{
|
||||||
|
payout.State = PayoutState.AwaitingPayment;
|
||||||
|
payout.Proof = null;
|
||||||
|
}
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||||
|
{
|
||||||
|
Message = "Payout payments have been marked as awaiting payment",
|
||||||
|
Severity = StatusMessageModel.StatusSeverity.Success
|
||||||
|
});
|
||||||
|
break;
|
||||||
case "cancel":
|
case "cancel":
|
||||||
await _pullPaymentService.Cancel(
|
await _pullPaymentService.Cancel(
|
||||||
new PullPaymentHostedService.CancelRequest(payoutIds, new[] { storeId }));
|
new PullPaymentHostedService.CancelRequest(payoutIds, new[] { storeId }));
|
||||||
|
|||||||
@@ -339,61 +339,22 @@ public class BitcoinLikePayoutHandler : IPayoutHandler, IHasNetwork
|
|||||||
{
|
{
|
||||||
var proof = ParseProof(payout) as PayoutTransactionOnChainBlob;
|
var proof = ParseProof(payout) as PayoutTransactionOnChainBlob;
|
||||||
var payoutBlob = payout.GetBlob(this._jsonSerializerSettings);
|
var payoutBlob = payout.GetBlob(this._jsonSerializerSettings);
|
||||||
if (proof is null || proof.Accounted is false)
|
if (proof?.Accounted is not true)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
foreach (var txid in proof.Candidates.ToList())
|
foreach (var txid in proof.Candidates.ToList())
|
||||||
{
|
{
|
||||||
var explorer = _explorerClientProvider.GetExplorerClient(Network.CryptoCode);
|
var explorer = _explorerClientProvider.GetExplorerClient(Network.CryptoCode);
|
||||||
var tx = await explorer.GetTransactionAsync(txid);
|
var tx = await explorer.GetTransactionAsync(txid);
|
||||||
if (tx is null)
|
if (tx is null)
|
||||||
{
|
continue;
|
||||||
proof.Candidates.Remove(txid);
|
if (tx.Confirmations >= payoutBlob.MinimumConfirmation)
|
||||||
}
|
|
||||||
else if (tx.Confirmations >= payoutBlob.MinimumConfirmation)
|
|
||||||
{
|
{
|
||||||
payout.State = PayoutState.Completed;
|
payout.State = PayoutState.Completed;
|
||||||
proof.TransactionId = tx.TransactionHash;
|
proof.TransactionId = tx.TransactionHash;
|
||||||
updatedPayouts.Add(payout);
|
updatedPayouts.Add(payout);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
var rebroadcasted = await explorer.BroadcastAsync(tx.Transaction);
|
|
||||||
if (rebroadcasted.RPCCode == RPCErrorCode.RPC_TRANSACTION_ERROR ||
|
|
||||||
rebroadcasted.RPCCode == RPCErrorCode.RPC_TRANSACTION_REJECTED)
|
|
||||||
{
|
|
||||||
proof.Candidates.Remove(txid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
payout.State = PayoutState.InProgress;
|
|
||||||
proof.TransactionId = tx.TransactionHash;
|
|
||||||
updatedPayouts.Add(payout);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proof.TransactionId is not null && !proof.Candidates.Contains(proof.TransactionId))
|
|
||||||
{
|
|
||||||
proof.TransactionId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proof.Candidates.Count == 0)
|
|
||||||
{
|
|
||||||
if (payout.State != PayoutState.AwaitingPayment)
|
|
||||||
{
|
|
||||||
updatedPayouts.Add(payout);
|
|
||||||
}
|
|
||||||
payout.State = PayoutState.AwaitingPayment;
|
|
||||||
}
|
|
||||||
else if (proof.TransactionId is null)
|
|
||||||
{
|
|
||||||
proof.TransactionId = proof.Candidates.First();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payout.State == PayoutState.Completed)
|
if (payout.State == PayoutState.Completed)
|
||||||
proof.Candidates = null;
|
proof.Candidates = null;
|
||||||
SetProofBlob(payout, proof);
|
SetProofBlob(payout, proof);
|
||||||
@@ -402,7 +363,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler, IHasNetwork
|
|||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
foreach (PayoutData payoutData in updatedPayouts)
|
foreach (PayoutData payoutData in updatedPayouts)
|
||||||
{
|
{
|
||||||
_eventAggregator.Publish(new PayoutEvent(PayoutEvent.PayoutEventType.Updated,payoutData));
|
_eventAggregator.Publish(new PayoutEvent(PayoutEvent.PayoutEventType.Updated, payoutData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -39,11 +39,11 @@
|
|||||||
<td class="actions-col" permission="@Policies.CanModifyStoreSettings">
|
<td class="actions-col" permission="@Policies.CanModifyStoreSettings">
|
||||||
@if (conf.Value is null)
|
@if (conf.Value is null)
|
||||||
{
|
{
|
||||||
<a href="@processorsView.Factory.ConfigureLink(storeId, conf.Key, Context.Request)" text-translate="true">Configure</a>
|
<a id="Configure-@conf.Key" href="@processorsView.Factory.ConfigureLink(storeId, conf.Key, Context.Request)" text-translate="true">Configure</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<a href="@processorsView.Factory.ConfigureLink(storeId, conf.Key, Context.Request)" text-translate="true">Modify</a>
|
<a id="Configure-@conf.Key" href="@processorsView.Factory.ConfigureLink(storeId, conf.Key, Context.Request)" text-translate="true">Modify</a>
|
||||||
|
|
||||||
@if (await processorsView.Factory.CanRemove())
|
@if (await processorsView.Factory.CanRemove())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,6 +33,9 @@
|
|||||||
stateActions.Add(("cancel", StringLocalizer["Cancel"]));
|
stateActions.Add(("cancel", StringLocalizer["Cancel"]));
|
||||||
stateActions.Add(("mark-paid", StringLocalizer["Mark as already paid"]));
|
stateActions.Add(("mark-paid", StringLocalizer["Mark as already paid"]));
|
||||||
break;
|
break;
|
||||||
|
case PayoutState.InProgress:
|
||||||
|
stateActions.Add(("mark-awaiting-payment", StringLocalizer["Mark as awaiting payment"]));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user