Use PaymentUrlBuilder for ensuring proper formatting of BIP21 addresses (#2723)

This commit is contained in:
Nicolas Dorier 2021-07-30 18:47:02 +09:00 committed by GitHub
parent 4c57405945
commit a9da79cc58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 56 additions and 17 deletions

View File

@ -1,6 +1,7 @@
#if ALTCOINS
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Common;
using NBitcoin;
using NBXplorer;
using NBXplorer.Models;
@ -33,13 +34,15 @@ namespace BTCPayServer
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId));
}
public override string GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
public override PaymentUrlBuilder GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
{
//precision 0: 10 = 0.00000010
//precision 2: 10 = 0.00001000
//precision 8: 10 = 10
var money = cryptoInfoDue is null ? null : new Money(cryptoInfoDue.ToDecimal(MoneyUnit.BTC) / decimal.Parse("1".PadRight(1 + 8 - Divisibility, '0')), MoneyUnit.BTC);
return $"{base.GenerateBIP21(cryptoInfoAddress, money)}{(money is null? "?": "&")}assetid={AssetId}";
var builder = base.GenerateBIP21(cryptoInfoAddress, money);
builder.QueryParams.Add("assetid", AssetId.ToString());
return builder;
}
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BTCPayServer.Common;
using NBitcoin;
using NBXplorer;
using NBXplorer.Models;
@ -121,9 +122,15 @@ namespace BTCPayServer
});
}
public virtual string GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
public virtual PaymentUrlBuilder GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
{
return $"{UriScheme}:{cryptoInfoAddress}{(cryptoInfoDue is null? string.Empty: $"?amount={cryptoInfoDue.ToString(false, true)}")}";
var builder = new PaymentUrlBuilder(UriScheme);
builder.Host = cryptoInfoAddress;
if (cryptoInfoDue != null && cryptoInfoDue != Money.Zero)
{
builder.QueryParams.Add("amount", cryptoInfoDue.ToString(false, true));
}
return builder;
}
public virtual List<TransactionInformation> FilterValidTransactions(List<TransactionInformation> transactionInformationSet)

View File

@ -0,0 +1,30 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BTCPayServer.Common
{
public class PaymentUrlBuilder
{
public PaymentUrlBuilder(string uriScheme)
{
UriScheme = uriScheme;
}
public string UriScheme { get; set; }
public Dictionary<string, string> QueryParams { get; set; } = new Dictionary<string, string>();
public string? Host { get; set; }
public override string ToString()
{
StringBuilder builder = new StringBuilder($"{UriScheme}:{Host}");
if (QueryParams.Count != 0)
{
var parts = QueryParams.Select(q => Uri.EscapeDataString(q.Key) + "=" + System.Web.NBitcoin.HttpUtility.UrlEncode(q.Value))
.ToArray();
builder.Append($"?{string.Join('&', parts)}");
}
return builder.ToString();
}
}
}

View File

@ -127,17 +127,16 @@ namespace BTCPayServer.Controllers.GreenField
return BadRequest();
}
var bip21 = network.GenerateBIP21(kpi.Address.ToString(), null);
var bip21 = network.GenerateBIP21(kpi.Address?.ToString(), null);
var allowedPayjoin = derivationScheme.IsHotWallet && Store.GetStoreBlob().PayJoinEnabled;
if (allowedPayjoin)
{
bip21 +=
$"?{PayjoinClient.BIP21EndpointKey}={Request.GetAbsoluteUri(Url.Action(nameof(PayJoinEndpointController.Submit), "PayJoinEndpoint", new {cryptoCode}))}";
bip21.QueryParams.Add(PayjoinClient.BIP21EndpointKey, Request.GetAbsoluteUri(Url.Action(nameof(PayJoinEndpointController.Submit), "PayJoinEndpoint", new { cryptoCode })));
}
return Ok(new OnChainWalletAddressData()
{
Address = kpi.Address.ToString(),
PaymentLink = bip21,
Address = kpi.Address?.ToString(),
PaymentLink = bip21.ToString(),
KeyPath = kpi.KeyPath
});
}

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client.Models;
using BTCPayServer.Common;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
using BTCPayServer.ModelBinders;
@ -285,7 +286,7 @@ namespace BTCPayServer.Controllers
continue;
}
var blob = payout.GetBlob(_jsonSerializerSettings);
bip21.Add(network.GenerateBIP21(payout.Destination, new Money(blob.CryptoAmount.Value, MoneyUnit.BTC)));
bip21.Add(network.GenerateBIP21(payout.Destination, new Money(blob.CryptoAmount.Value, MoneyUnit.BTC)).ToString());
}
if(bip21.Any())

View File

@ -368,18 +368,17 @@ namespace BTCPayServer.Controllers
return NotFound();
var address = _walletReceiveService.Get(walletId)?.Address;
var allowedPayjoin = paymentMethod.IsHotWallet && CurrentStore.GetStoreBlob().PayJoinEnabled;
var bip21 = address is null ? null : network.GenerateBIP21(address.ToString(), null);
var bip21 = network.GenerateBIP21(address?.ToString(), null);
if (allowedPayjoin)
{
bip21 +=
$"?{PayjoinClient.BIP21EndpointKey}={Request.GetAbsoluteUri(Url.Action(nameof(PayJoinEndpointController.Submit), "PayJoinEndpoint", new {walletId.CryptoCode}))}";
bip21.QueryParams.Add(PayjoinClient.BIP21EndpointKey, Request.GetAbsoluteUri(Url.Action(nameof(PayJoinEndpointController.Submit), "PayJoinEndpoint", new { walletId.CryptoCode })));
}
return View(new WalletReceiveViewModel()
{
CryptoCode = walletId.CryptoCode,
Address = address?.ToString(),
CryptoImage = GetImage(paymentMethod.PaymentId, network),
PaymentLink = bip21
PaymentLink = bip21.ToString()
});
}

View File

@ -79,9 +79,9 @@ namespace BTCPayServer.Payments
if ((paymentMethodDetails as BitcoinLikeOnChainPaymentMethod)?.PayjoinEnabled is true && serverUri != null)
{
bip21 += $"&{PayjoinClient.BIP21EndpointKey}={serverUri.WithTrailingSlash()}{network.CryptoCode}/{PayjoinClient.BIP21EndpointKey}";
bip21.QueryParams.Add(PayjoinClient.BIP21EndpointKey, $"{serverUri.WithTrailingSlash()}{network.CryptoCode}/{PayjoinClient.BIP21EndpointKey}");
}
return bip21;
return bip21.ToString();
}
public override string InvoiceViewPaymentPartialName { get; } = "Bitcoin/ViewBitcoinLikePaymentData";