Add the concept of RateDivisibility

This commit is contained in:
nicolas.dorier 2024-10-04 23:08:30 +09:00
parent 64ba8248d2
commit abc8161a08
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
3 changed files with 17 additions and 13 deletions

View File

@ -875,13 +875,7 @@ namespace BTCPayServer.Controllers
return extension?.Image ?? "";
}
var cd = this._CurrencyNameTable.GetCurrencyData(prompt.Currency, false);
// 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);
string ShowMoney(decimal value) => MoneyExtensions.ShowMoney(value, prompt.RateDivisibility ?? prompt.Divisibility);
var model = new PaymentModel
{
Activated = prompt.Activated,

View File

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

View File

@ -870,8 +870,16 @@ namespace BTCPayServer.Services.Invoices
public string Currency { get; set; }
[JsonIgnore]
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; }
/// <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.
/// It includes the <see cref="TweakFee"/>.
/// </summary>
@ -909,24 +917,25 @@ namespace BTCPayServer.Services.Invoices
accounting.TxRequired = accounting.TxCount;
var grossDue = i.Price + i.PaidFee;
var rate = Rate;
var divisibility = RateDivisibility ?? Divisibility;
if (i.MinimumNetDue > 0.0m)
{
accounting.TxRequired++;
grossDue += rate * PaymentMethodFee;
}
accounting.TotalDue = Coins(grossDue / rate, Divisibility);
accounting.Paid = Coins(i.PaidAmount.Gross / rate, Divisibility);
accounting.PaymentMethodPaid = Coins(thisPaymentMethodPayments.Sum(p => p.PaidAmount.Gross), Divisibility);
accounting.TotalDue = Coins(grossDue / rate, divisibility);
accounting.Paid = Coins(i.PaidAmount.Gross / rate, 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.
// 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;
accounting.DueUncapped = Coins(dueUncapped / rate, Divisibility);
accounting.DueUncapped = Coins(dueUncapped / rate, divisibility);
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;
}