Add the concept of RateDivisibility (#6278)

This commit is contained in:
Nicolas Dorier 2024-10-04 23:34:31 +09:00 committed by GitHub
parent 64ba8248d2
commit b246beab3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 18 additions and 13 deletions

View file

@ -875,13 +875,7 @@ namespace BTCPayServer.Controllers
return extension?.Image ?? ""; return extension?.Image ?? "";
} }
var cd = this._CurrencyNameTable.GetCurrencyData(prompt.Currency, false); string ShowMoney(decimal value) => MoneyExtensions.ShowMoney(value, prompt.RateDivisibility ?? prompt.Divisibility);
// Show the "Common divisibility" rather than the payment method disibility.
// For example, BTC has commonly 8 digits, but on lightning it has 11. In this case, pick 8.
if (cd?.Divisibility is not int divisibility)
divisibility = prompt.Divisibility;
string ShowMoney(decimal value) => MoneyExtensions.ShowMoney(value, divisibility);
var model = new PaymentModel var model = new PaymentModel
{ {
Activated = prompt.Activated, Activated = prompt.Activated,

View file

@ -53,6 +53,7 @@ namespace BTCPayServer.Payments.Lightning
context.Prompt.Inactive = false; context.Prompt.Inactive = false;
context.Prompt.Currency = _network.CryptoCode; context.Prompt.Currency = _network.CryptoCode;
context.Prompt.Divisibility = 11; context.Prompt.Divisibility = 11;
context.Prompt.RateDivisibility = 8;
context.Prompt.PaymentMethodFee = 0.0m; context.Prompt.PaymentMethodFee = 0.0m;
return Task.CompletedTask; return Task.CompletedTask;
} }

View file

@ -62,6 +62,7 @@ namespace BTCPayServer.Payments.Lightning
context.Prompt.Currency = _Network.CryptoCode; context.Prompt.Currency = _Network.CryptoCode;
context.Prompt.PaymentMethodFee = 0m; context.Prompt.PaymentMethodFee = 0m;
context.Prompt.Divisibility = 11; context.Prompt.Divisibility = 11;
context.Prompt.RateDivisibility = 8;
return Task.CompletedTask; return Task.CompletedTask;
} }

View file

@ -870,8 +870,16 @@ namespace BTCPayServer.Services.Invoices
public string Currency { get; set; } public string Currency { get; set; }
[JsonIgnore] [JsonIgnore]
public decimal Rate => Currency is null ? throw new InvalidOperationException("Currency of the payment prompt isn't set") : ParentEntity.GetInvoiceRate(Currency); public decimal Rate => Currency is null ? throw new InvalidOperationException("Currency of the payment prompt isn't set") : ParentEntity.GetInvoiceRate(Currency);
/// <summary>
/// The maximum divisibility supported by the underlying payment method
/// </summary>
public int Divisibility { get; set; } public int Divisibility { get; set; }
/// <summary> /// <summary>
/// The divisibility to use when calculating the amount to pay.
/// If null, it will use the <see cref="Divisibility"/>.
/// </summary>
public int? RateDivisibility { get; set; }
/// <summary>
/// Total additional fee imposed by this specific payment method. /// Total additional fee imposed by this specific payment method.
/// It includes the <see cref="TweakFee"/>. /// It includes the <see cref="TweakFee"/>.
/// </summary> /// </summary>
@ -909,24 +917,25 @@ namespace BTCPayServer.Services.Invoices
accounting.TxRequired = accounting.TxCount; accounting.TxRequired = accounting.TxCount;
var grossDue = i.Price + i.PaidFee; var grossDue = i.Price + i.PaidFee;
var rate = Rate; var rate = Rate;
var divisibility = RateDivisibility ?? Divisibility;
if (i.MinimumNetDue > 0.0m) if (i.MinimumNetDue > 0.0m)
{ {
accounting.TxRequired++; accounting.TxRequired++;
grossDue += rate * PaymentMethodFee; grossDue += rate * PaymentMethodFee;
} }
accounting.TotalDue = Coins(grossDue / rate, Divisibility); accounting.TotalDue = Coins(grossDue / rate, divisibility);
accounting.Paid = Coins(i.PaidAmount.Gross / rate, Divisibility); accounting.Paid = Coins(i.PaidAmount.Gross / rate, divisibility);
accounting.PaymentMethodPaid = Coins(thisPaymentMethodPayments.Sum(p => p.PaidAmount.Gross), Divisibility); accounting.PaymentMethodPaid = Coins(thisPaymentMethodPayments.Sum(p => p.PaidAmount.Gross), divisibility);
// This one deal with the fact where it might looks like a slight over payment due to the dust of another payment method. // This one deal with the fact where it might looks like a slight over payment due to the dust of another payment method.
// So if we detect the NetDue is zero, just cap dueUncapped to 0 // So if we detect the NetDue is zero, just cap dueUncapped to 0
var dueUncapped = i.NetDue == 0.0m ? 0.0m : grossDue - i.PaidAmount.Gross; var dueUncapped = i.NetDue == 0.0m ? 0.0m : grossDue - i.PaidAmount.Gross;
accounting.DueUncapped = Coins(dueUncapped / rate, Divisibility); accounting.DueUncapped = Coins(dueUncapped / rate, divisibility);
accounting.Due = Max(accounting.DueUncapped, 0.0m); accounting.Due = Max(accounting.DueUncapped, 0.0m);
accounting.PaymentMethodFee = Coins((grossDue - i.Price) / rate, Divisibility); accounting.PaymentMethodFee = Coins((grossDue - i.Price) / rate, divisibility);
accounting.MinimumTotalDue = Max(Smallest(Divisibility), Coins((grossDue * (1.0m - ((decimal)i.PaymentTolerance / 100.0m))) / rate, Divisibility)); accounting.MinimumTotalDue = Max(Smallest(divisibility), Coins((grossDue * (1.0m - ((decimal)i.PaymentTolerance / 100.0m))) / rate, divisibility));
return accounting; return accounting;
} }