mirror of
https://github.com/MAGICGrants/btcpayserver-monero-plugin.git
synced 2026-01-08 02:23:51 -05:00
Fix: Payments in mempool not detected
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,5 +3,3 @@
|
||||
.idea
|
||||
Plugins/packed
|
||||
.vs/
|
||||
wallets/cashcow/
|
||||
wallets/merchant/
|
||||
|
||||
@@ -16,7 +16,6 @@ namespace BTCPayServer.Plugins.Monero.Configuration
|
||||
public string WalletDirectory { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string CashCowWalletDirectory { get; set; }
|
||||
public Uri CashCowWalletRpcUri { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,6 @@ namespace BTCPayServer.Plugins.Monero.Controllers
|
||||
_MoneroRpcProvider.Summaries.TryGetValue(cryptoCode, out var summary);
|
||||
_MoneroLikeConfiguration.MoneroLikeConfigurationItems.TryGetValue(cryptoCode,
|
||||
out var configurationItem);
|
||||
var fileAddress = Path.Combine(configurationItem.WalletDirectory, "wallet");
|
||||
var accounts = accountsResponse?.SubaddressAccounts?.Select(account =>
|
||||
new SelectListItem(
|
||||
$"{account.AccountIndex} - {(string.IsNullOrEmpty(account.Label) ? "No label" : account.Label)}",
|
||||
@@ -121,7 +120,6 @@ namespace BTCPayServer.Plugins.Monero.Controllers
|
||||
|
||||
return new MoneroLikePaymentMethodViewModel()
|
||||
{
|
||||
WalletFileFound = System.IO.File.Exists(fileAddress),
|
||||
Enabled =
|
||||
settings != null &&
|
||||
!excludeFilters.Match(PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode)),
|
||||
@@ -193,7 +191,11 @@ namespace BTCPayServer.Plugins.Monero.Controllers
|
||||
ModelState.AddModelError(nameof(viewModel.WalletKeysFile), StringLocalizer["Please select the view-only wallet keys file"]);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (configurationItem.WalletDirectory == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.WalletFile), StringLocalizer["This installation doesn't support wallet import (BTCPAY_XMR_WALLET_DAEMON_WALLETDIR is not set)"]);
|
||||
valid = false;
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
if (_MoneroRpcProvider.Summaries.TryGetValue(cryptoCode, out var summary))
|
||||
@@ -288,6 +290,7 @@ namespace BTCPayServer.Plugins.Monero.Controllers
|
||||
vm.AccountIndex = viewModel.AccountIndex;
|
||||
vm.SettlementConfirmationThresholdChoice = viewModel.SettlementConfirmationThresholdChoice;
|
||||
vm.CustomSettlementConfirmationThreshold = viewModel.CustomSettlementConfirmationThreshold;
|
||||
vm.SupportWalletExport = configurationItem.WalletDirectory is not null;
|
||||
return View("/Views/Monero/GetStoreMoneroLikePaymentMethod.cshtml", vm);
|
||||
}
|
||||
|
||||
@@ -345,6 +348,7 @@ namespace BTCPayServer.Plugins.Monero.Controllers
|
||||
public class MoneroLikePaymentMethodViewModel : IValidatableObject
|
||||
{
|
||||
public MoneroRPCProvider.MoneroLikeSummary Summary { get; set; }
|
||||
public bool SupportWalletExport { get; set; }
|
||||
public string CryptoCode { get; set; }
|
||||
public string NewAccountLabel { get; set; }
|
||||
public long AccountIndex { get; set; }
|
||||
|
||||
@@ -128,17 +128,13 @@ public class MoneroPlugin : BaseBTCPayServerPlugin
|
||||
var walletDaemonWalletDirectory =
|
||||
configuration.GetOrDefault<string>(
|
||||
$"{moneroLikeSpecificBtcPayNetwork.CryptoCode}_wallet_daemon_walletdir", null);
|
||||
// Only for regtest
|
||||
var walletCashCowDaemonWalletDirectory =
|
||||
configuration.GetOrDefault<string>(
|
||||
$"{moneroLikeSpecificBtcPayNetwork.CryptoCode}_cashcow_wallet_daemon_walletdir", null);
|
||||
var daemonUsername =
|
||||
configuration.GetOrDefault<string>(
|
||||
$"{moneroLikeSpecificBtcPayNetwork.CryptoCode}_daemon_username", null);
|
||||
var daemonPassword =
|
||||
configuration.GetOrDefault<string>(
|
||||
$"{moneroLikeSpecificBtcPayNetwork.CryptoCode}_daemon_password", null);
|
||||
if (daemonUri == null || walletDaemonUri == null || walletDaemonWalletDirectory == null)
|
||||
if (daemonUri == null || walletDaemonUri == null)
|
||||
{
|
||||
var logger = serviceProvider.GetRequiredService<ILogger<MoneroPlugin>>();
|
||||
var cryptoCode = moneroLikeSpecificBtcPayNetwork.CryptoCode.ToUpperInvariant();
|
||||
@@ -150,10 +146,6 @@ public class MoneroPlugin : BaseBTCPayServerPlugin
|
||||
{
|
||||
logger.LogWarning($"BTCPAY_{cryptoCode}_WALLET_DAEMON_URI is not configured");
|
||||
}
|
||||
if (walletDaemonWalletDirectory is null)
|
||||
{
|
||||
logger.LogWarning($"BTCPAY_{cryptoCode}_WALLET_DAEMON_WALLETDIR is not configured");
|
||||
}
|
||||
logger.LogWarning($"{cryptoCode} got disabled as it is not fully configured.");
|
||||
}
|
||||
else
|
||||
@@ -165,7 +157,6 @@ public class MoneroPlugin : BaseBTCPayServerPlugin
|
||||
Password = daemonPassword,
|
||||
InternalWalletRpcUri = walletDaemonUri,
|
||||
WalletDirectory = walletDaemonWalletDirectory,
|
||||
CashCowWalletDirectory = walletCashCowDaemonWalletDirectory,
|
||||
CashCowWalletRpcUri = cashCowWalletDaemonUri,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace BTCPayServer.Plugins.Monero.RPC.Models
|
||||
{
|
||||
[JsonProperty("txid")] public string TransactionId { get; set; }
|
||||
|
||||
[JsonProperty("account_index")] public long AccountIndex { get; set; }
|
||||
[JsonProperty("account_index", DefaultValueHandling = DefaultValueHandling.Ignore)] public long? AccountIndex { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class MoneroCheckoutCheatModeExtension : ICheckoutCheatModeExtension
|
||||
public async Task<ICheckoutCheatModeExtension.MineBlockResult> MineBlock(ICheckoutCheatModeExtension.MineBlockContext mineBlockContext)
|
||||
{
|
||||
var cashcow = _rpcProvider.CashCowWalletRpcClients[_network.CryptoCode];
|
||||
var deamon = _rpcProvider.WalletRpcClients[_network.CryptoCode];
|
||||
var deamon = _rpcProvider.DaemonRpcClients[_network.CryptoCode];
|
||||
var address = (await cashcow.SendCommandAsync<GetAddressRequest, GetAddressResponse>("get_address", new()
|
||||
{
|
||||
AccountIndex = 0
|
||||
|
||||
@@ -89,6 +89,7 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||
{
|
||||
await OnNewBlock(moneroEvent.CryptoCode);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(moneroEvent.TransactionHash))
|
||||
{
|
||||
await OnTransactionUpdated(moneroEvent.CryptoCode, moneroEvent.TransactionHash);
|
||||
@@ -132,7 +133,8 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||
var expandedInvoices = invoices.Select(entity => (Invoice: entity,
|
||||
ExistingPayments: GetAllMoneroLikePayments(entity, cryptoCode),
|
||||
Prompt: entity.GetPaymentPrompt(paymentId),
|
||||
PaymentMethodDetails: handler.ParsePaymentPromptDetails(entity.GetPaymentPrompt(paymentId).Details)))
|
||||
PaymentMethodDetails: handler.ParsePaymentPromptDetails(entity.GetPaymentPrompt(paymentId)
|
||||
.Details)))
|
||||
.Select(tuple => (
|
||||
tuple.Invoice,
|
||||
tuple.PaymentMethodDetails,
|
||||
@@ -208,7 +210,8 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||
|
||||
|
||||
return HandlePaymentData(cryptoCode, transfer.Address, transfer.Amount, transfer.SubaddrIndex.Major,
|
||||
transfer.SubaddrIndex.Minor, transfer.Txid, transfer.Confirmations, transfer.Height, transfer.UnlockTime,invoice,
|
||||
transfer.SubaddrIndex.Minor, transfer.Txid, transfer.Confirmations, transfer.Height,
|
||||
transfer.UnlockTime, invoice,
|
||||
updatedPaymentEntities);
|
||||
}));
|
||||
}
|
||||
@@ -228,17 +231,16 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||
private async Task OnNewBlock(string cryptoCode)
|
||||
{
|
||||
await UpdateAnyPendingMoneroLikePayment(cryptoCode);
|
||||
_eventAggregator.Publish(new NewBlockEvent() { PaymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode) });
|
||||
_eventAggregator.Publish(new NewBlockEvent()
|
||||
{ PaymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode) });
|
||||
}
|
||||
|
||||
private async Task OnTransactionUpdated(string cryptoCode, string transactionHash)
|
||||
{
|
||||
var paymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode);
|
||||
var transfer = await _moneroRpcProvider.WalletRpcClients[cryptoCode]
|
||||
.SendCommandAsync<GetTransferByTransactionIdRequest, GetTransferByTransactionIdResponse>(
|
||||
"get_transfer_by_txid",
|
||||
new GetTransferByTransactionIdRequest() { TransactionId = transactionHash });
|
||||
|
||||
var transfer = await GetTransferByTxId(cryptoCode, transactionHash, this.CancellationToken);
|
||||
if (transfer is null)
|
||||
return;
|
||||
var paymentsToUpdate = new List<(PaymentEntity Payment, InvoiceEntity invoice)>();
|
||||
|
||||
//group all destinations of the tx together and loop through the sets
|
||||
@@ -259,7 +261,7 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||
transfer.Transfer.Txid,
|
||||
transfer.Transfer.Confirmations,
|
||||
transfer.Transfer.Height
|
||||
, transfer.Transfer.UnlockTime,invoice, paymentsToUpdate);
|
||||
, transfer.Transfer.UnlockTime, invoice, paymentsToUpdate);
|
||||
}
|
||||
|
||||
if (paymentsToUpdate.Any())
|
||||
@@ -275,6 +277,49 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<GetTransferByTransactionIdResponse> GetTransferByTxId(string cryptoCode,
|
||||
string transactionHash, CancellationToken cancellationToken)
|
||||
{
|
||||
var accounts = await _moneroRpcProvider.WalletRpcClients[cryptoCode].SendCommandAsync<GetAccountsRequest, GetAccountsResponse>("get_accounts", new GetAccountsRequest(), cancellationToken);
|
||||
var accountIndexes = accounts
|
||||
.SubaddressAccounts
|
||||
.Select(a => new long?(a.AccountIndex))
|
||||
.ToList();
|
||||
if (accountIndexes.Count is 0)
|
||||
accountIndexes.Add(null);
|
||||
var req = accountIndexes
|
||||
.Select(i => GetTransferByTxId(cryptoCode, transactionHash, i))
|
||||
.ToArray();
|
||||
foreach (var task in req)
|
||||
{
|
||||
var result = await task;
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<GetTransferByTransactionIdResponse> GetTransferByTxId(string cryptoCode, string transactionHash, long? accountIndex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _moneroRpcProvider.WalletRpcClients[cryptoCode]
|
||||
.SendCommandAsync<GetTransferByTransactionIdRequest, GetTransferByTransactionIdResponse>(
|
||||
"get_transfer_by_txid",
|
||||
new GetTransferByTransactionIdRequest()
|
||||
{
|
||||
TransactionId = transactionHash,
|
||||
AccountIndex = accountIndex
|
||||
});
|
||||
return result;
|
||||
}
|
||||
catch (JsonRpcClient.JsonRpcApiException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandlePaymentData(string cryptoCode, string address, long totalAmount, long subaccountIndex,
|
||||
long subaddressIndex,
|
||||
string txId, long confirmations, long blockHeight, long locktime, InvoiceEntity invoice,
|
||||
@@ -330,16 +375,17 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||
=> ConfirmationsRequired(details, speedPolicy) <= details.ConfirmationCount;
|
||||
|
||||
public static long ConfirmationsRequired(MoneroLikePaymentData details, SpeedPolicy speedPolicy)
|
||||
=> (details, speedPolicy) switch
|
||||
{
|
||||
(_, _) when details.ConfirmationCount < details.LockTime => details.LockTime - details.ConfirmationCount,
|
||||
({ InvoiceSettledConfirmationThreshold: long v }, _) => v,
|
||||
(_, SpeedPolicy.HighSpeed) => 0,
|
||||
(_, SpeedPolicy.MediumSpeed) => 1,
|
||||
(_, SpeedPolicy.LowMediumSpeed) => 2,
|
||||
(_, SpeedPolicy.LowSpeed) => 6,
|
||||
_ => 6,
|
||||
};
|
||||
=> (details, speedPolicy) switch
|
||||
{
|
||||
(_, _) when details.ConfirmationCount < details.LockTime =>
|
||||
details.LockTime - details.ConfirmationCount,
|
||||
({ InvoiceSettledConfirmationThreshold: long v }, _) => v,
|
||||
(_, SpeedPolicy.HighSpeed) => 0,
|
||||
(_, SpeedPolicy.MediumSpeed) => 1,
|
||||
(_, SpeedPolicy.LowMediumSpeed) => 2,
|
||||
(_, SpeedPolicy.LowSpeed) => 6,
|
||||
_ => 6,
|
||||
};
|
||||
|
||||
|
||||
private async Task UpdateAnyPendingMoneroLikePayment(string cryptoCode)
|
||||
@@ -358,4 +404,4 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||
.Where(p => p.PaymentMethodId == PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,13 @@
|
||||
<div class="card">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">Node available: @Model.Summary.DaemonAvailable</li>
|
||||
<li class="list-group-item">Wallet available: @Model.Summary.WalletAvailable (@(Model.WalletFileFound ? "Wallet file present" : "Wallet file not found"))</li>
|
||||
<li class="list-group-item">Last updated: @Model.Summary.UpdatedAt</li>
|
||||
<li class="list-group-item">Synced: @Model.Summary.Synced (@Model.Summary.CurrentHeight / @Model.Summary.TargetHeight)</li>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!Model.WalletFileFound || Model.Summary.WalletHeight == default)
|
||||
@if (Model.SupportWalletExport && Model.Summary?.WalletHeight is null or 0)
|
||||
{
|
||||
<form method="post" asp-action="GetStoreMoneroLikePaymentMethod"
|
||||
asp-route-storeId="@Context.GetRouteValue("storeId")"
|
||||
@@ -63,7 +62,7 @@
|
||||
class="mt-4" enctype="multipart/form-data">
|
||||
|
||||
<input type="hidden" asp-for="CryptoCode"/>
|
||||
@if (!Model.WalletFileFound || Model.Summary.WalletHeight == default)
|
||||
@if (Model.Summary?.WalletHeight is null or 0)
|
||||
{
|
||||
<input type="hidden" asp-for="AccountIndex"/>
|
||||
}
|
||||
|
||||
16
README.md
16
README.md
@@ -8,13 +8,13 @@ This plugin extends BTCPay Server to enable users to receive payments via Monero
|
||||
|
||||
Configure this plugin using the following environment variables:
|
||||
|
||||
| Environment variable | Description | Example |
|
||||
| --- | --- | --- |
|
||||
**BTCPAY_XMR_DAEMON_URI** | **Required**. The URI of the [monerod](https://github.com/monero-project/monero) RPC interface. | http://127.0.0.1:18081 |
|
||||
**BTCPAY_XMR_DAEMON_USERNAME** | **Optional**. The username for authenticating with the daemon. | john |
|
||||
**BTCPAY_XMR_DAEMON_PASSWORD** | **Optional**. The password for authenticating with the daemon. | secret |
|
||||
**BTCPAY_XMR_WALLET_DAEMON_URI** | **Required**. The URI of the [monero-wallet-rpc](https://getmonero.dev/interacting/monero-wallet-rpc.html) RPC interface. | http://127.0.0.1:18082 |
|
||||
**BTCPAY_XMR_WALLET_DAEMON_WALLETDIR** | **Required**. The directory where BTCPay Server saves wallet files uploaded via the UI ([See this blog post for more details](https://sethforprivacy.com/guides/accepting-monero-via-btcpay-server/#configure-the-bitcoin-wallet-of-choice)). | /home/cypherpunk/Monero/wallets/ |
|
||||
| Environment variable | Description | Example |
|
||||
| --- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
|
||||
**BTCPAY_XMR_DAEMON_URI** | **Required**. The URI of the [monerod](https://github.com/monero-project/monero) RPC interface. | http://127.0.0.1:18081 |
|
||||
**BTCPAY_XMR_DAEMON_USERNAME** | **Optional**. The username for authenticating with the daemon. | john |
|
||||
**BTCPAY_XMR_DAEMON_PASSWORD** | **Optional**. The password for authenticating with the daemon. | secret |
|
||||
**BTCPAY_XMR_WALLET_DAEMON_URI** | **Required**. The URI of the [monero-wallet-rpc](https://getmonero.dev/interacting/monero-wallet-rpc.html) RPC interface. | http://127.0.0.1:18082 |
|
||||
**BTCPAY_XMR_WALLET_DAEMON_WALLETDIR** | **Optional**. The directory where BTCPay Server saves wallet files uploaded via the UI ([See this blog post for more details](https://sethforprivacy.com/guides/accepting-monero-via-btcpay-server/#configure-the-bitcoin-wallet-of-choice)). | /home/cypherpunk/Monero/wallets/ |
|
||||
|
||||
BTCPay Server's Docker deployment simplifies the setup by automatically configuring these variables. For further details, refer to this [blog post](https://sethforprivacy.com/guides/accepting-monero-via-btcpay-server).
|
||||
|
||||
@@ -37,8 +37,6 @@ Then create the `appsettings.dev.json` file in `btcpayserver/BTCPayServer`, with
|
||||
"XMR_DAEMON_URI": "http://127.0.0.1:18081",
|
||||
"XMR_WALLET_DAEMON_URI": "http://127.0.0.1:18082",
|
||||
"XMR_CASHCOW_WALLET_DAEMON_URI": "http://127.0.0.1:18092",
|
||||
"XMR_WALLET_DAEMON_WALLETDIR": "C:\\Sources\\btcpayserver-monero-plugin\\wallets\\merchant",
|
||||
"XMR_CASHCOW_WALLET_DAEMON_WALLETDIR": "C:\\Sources\\btcpayserver-monero-plugin\\wallets\\cashcow"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Submodule btcpayserver updated: 995d5a0a19...6b49544470
@@ -86,7 +86,7 @@ services:
|
||||
ports:
|
||||
- "18082:18082"
|
||||
volumes:
|
||||
- "./wallets/merchant:/wallet"
|
||||
- "tests_monero_merchant_wallet:/wallet"
|
||||
depends_on:
|
||||
- monerod
|
||||
|
||||
@@ -98,7 +98,7 @@ services:
|
||||
ports:
|
||||
- "18092:18092"
|
||||
volumes:
|
||||
- "./wallets/cashcow:/wallet"
|
||||
- "tests_monero_cashcow_wallet:/wallet"
|
||||
depends_on:
|
||||
- monerod
|
||||
|
||||
@@ -114,6 +114,8 @@ services:
|
||||
volumes:
|
||||
bitcoin_datadir:
|
||||
monero_data:
|
||||
tests_monero_merchant_wallet:
|
||||
tests_monero_cashcow_wallet:
|
||||
|
||||
networks:
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user