mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-18 08:04:26 +01:00
update bringin
This commit is contained in:
@@ -117,6 +117,71 @@ public class BringinClient
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal Balance { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public async Task<GetTransactionListResponse> GetTransactions()
|
||||
{
|
||||
var content = new StringContent(JsonConvert.SerializeObject(new
|
||||
{
|
||||
startDate = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds(),
|
||||
endDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
|
||||
}), Encoding.UTF8, "application/json");
|
||||
var response = await HttpClient.PostAsync($"/api/v0/account/transactions", content);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return JObject.Parse(responseContent).ToObject<GetTransactionListResponse>();
|
||||
}
|
||||
|
||||
var error = JObject.Parse(responseContent).ToObject<BringinErrorResponse>();
|
||||
throw new BringinException(error);
|
||||
}
|
||||
|
||||
public class GetTransactionListResponse
|
||||
{
|
||||
[JsonProperty("transactions")]
|
||||
public BringinTransaction[] Transactions { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class BringinTransaction
|
||||
{
|
||||
// {
|
||||
// "orderId": "3521154c-30b4-480c-834d-38f80d507963",
|
||||
// "type": "OFFRAMP_WITHOUT_FIAT_WITHDRAWAL",
|
||||
// "subType": "LIGHTNING",
|
||||
// "sourceAmount": "100000",
|
||||
// "sourceCurrency": "BTC",
|
||||
// "destinationAmount": "3816",
|
||||
// "destinationAddress": "b0a4c862-c941-4d3c-8727-18e5097a3b5a",
|
||||
// "destinationCurrency": "EUR",
|
||||
// "status": "SUCCESSFUL",
|
||||
// "createdAt": "2024-01-18T14:02:59.709Z",
|
||||
// }
|
||||
[JsonProperty("orderId")]
|
||||
public string OrderId { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("subType")]
|
||||
public string SubType { get; set; }
|
||||
[JsonProperty("sourceAmount")]
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal SourceAmount { get; set; }
|
||||
[JsonProperty("sourceCurrency")]
|
||||
public string SourceCurrency { get; set; }
|
||||
[JsonProperty("destinationAmount")]
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal DestinationAmount { get; set; }
|
||||
[JsonProperty("destinationCurrency")]
|
||||
public string DestinationCurrency { get; set; }
|
||||
[JsonProperty("destinationAddress")]
|
||||
public string DestinationAddress { get; set; }
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
}
|
||||
//
|
||||
// public class GetOrderResponse
|
||||
// {
|
||||
@@ -154,6 +219,7 @@ public class BringinClient
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
[JsonProperty("invoice")] public string Invoice { get; set; }
|
||||
[JsonProperty("depositAddress")] public string DepositAddress { get; set; }
|
||||
|
||||
[JsonProperty("expiresAt")] public long Expiry { get; set; }
|
||||
}
|
||||
@@ -188,6 +254,7 @@ public class BringinClient
|
||||
public string Message { get; set; }
|
||||
public string StatusCode { get; set; }
|
||||
public string ErrorCode { get; set; }
|
||||
public JObject ErrorDetails { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
public JToken ErrorDetails { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ public class BringinException : Exception
|
||||
{
|
||||
private readonly BringinClient.BringinErrorResponse _error;
|
||||
|
||||
public BringinException(BringinClient.BringinErrorResponse error)
|
||||
public BringinException(BringinClient.BringinErrorResponse error):base(error.Message?? error.ErrorMessage)
|
||||
{
|
||||
_error = error;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
@@ -27,6 +28,15 @@ using PayoutData = BTCPayServer.Data.PayoutData;
|
||||
|
||||
namespace BTCPayServer.Plugins.Bringin;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
|
||||
public static string ToHumanReadable(this string str)
|
||||
{
|
||||
return string.Join(' ', str.Split('_', '-').Select(part =>
|
||||
CultureInfo.CurrentCulture.TextInfo.ToTitleCase(part.ToLower(CultureInfo.CurrentCulture))));
|
||||
}
|
||||
}
|
||||
public class BringinService : EventHostedServiceBase
|
||||
{
|
||||
private readonly ILogger<BringinService> _logger;
|
||||
@@ -265,7 +275,7 @@ public class BringinService : EventHostedServiceBase
|
||||
|
||||
var rate = await bringinClient.GetRate();
|
||||
var thresholdAmount = supportedMethod.FiatMinimumAmount / rate.BringinPrice;
|
||||
if (amountBtc.ToDecimal(MoneyUnit.BTC) < thresholdAmount)
|
||||
if (amountBtc.ToDecimal(MoneyUnit.BTC) <= thresholdAmount)
|
||||
{
|
||||
throw new Exception($"Amount is too low. Minimum amount is {Money.Coins(thresholdAmount)} BTC");
|
||||
}
|
||||
@@ -283,15 +293,17 @@ public class BringinService : EventHostedServiceBase
|
||||
|
||||
if (!payout)
|
||||
{
|
||||
return order.Invoice;
|
||||
return order.Invoice?? order.DepositAddress;
|
||||
}
|
||||
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
|
||||
|
||||
var destination = !string.IsNullOrEmpty(order.Invoice)? (IClaimDestination) new BoltInvoiceClaimDestination(order.Invoice, BOLT11PaymentRequest.Parse(order.Invoice, network.NBitcoinNetwork)):
|
||||
new AddressClaimDestination(BitcoinAddress.Create(order.DepositAddress, network.NBitcoinNetwork));
|
||||
var claim = await _pullPaymentHostedService.Claim(new ClaimRequest()
|
||||
{
|
||||
PaymentMethodId = paymentMethodId,
|
||||
StoreId = storeId,
|
||||
Destination = new BoltInvoiceClaimDestination(order.Invoice, BOLT11PaymentRequest.Parse(order.Invoice, network.NBitcoinNetwork)),
|
||||
Destination = destination,
|
||||
Value = orderMoney.ToUnit(MoneyUnit.BTC),
|
||||
PreApprove = true,
|
||||
Metadata = JObject.FromObject(new
|
||||
@@ -379,7 +391,8 @@ public class BringinService : EventHostedServiceBase
|
||||
|
||||
public static readonly SupportedMethodOptions[] SupportedMethods = new[]
|
||||
{
|
||||
new SupportedMethodOptions(new PaymentMethodId("BTC", LightningPaymentType.Instance), true, 15, "LIGHTNING")
|
||||
new SupportedMethodOptions(new PaymentMethodId("BTC", LightningPaymentType.Instance), true, 15, "LIGHTNING"),
|
||||
new SupportedMethodOptions(new PaymentMethodId("BTC", BitcoinPaymentType.Instance), true, 20, "ON_CHAIN"),
|
||||
};
|
||||
|
||||
private ConcurrentDictionary<string, (IDisposable, BringinStoreSettings, DateTimeOffset Expiry)> _editModes = new();
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
@using NBitcoin
|
||||
@implements IAsyncDisposable;
|
||||
|
||||
|
||||
@code {
|
||||
private BringinService.BringinStoreSettings? _settings;
|
||||
private bool _isLoaded = false;
|
||||
@@ -75,7 +74,7 @@
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_readOnly = !(await AuthorizationService.AuthorizeAsync(HttpContextAccessor.HttpContext.User, StoreId, Policies.CanModifyStoreSettings )).Succeeded;
|
||||
_readOnly = !(await AuthorizationService.AuthorizeAsync(HttpContextAccessor.HttpContext.User, StoreId, Policies.CanModifyStoreSettings)).Succeeded;
|
||||
OnboardLink = LinkGenerator.GetUriByAction(HttpContextAccessor.HttpContext, "Onboard", "Bringin", new {StoreId});
|
||||
PmiLink = $"A payout processor has not been configured for this payment method. Payouts generated by Bringin will not be automatically handled. <a href=\"{LinkGenerator.GetUriByAction(HttpContextAccessor.HttpContext, "ConfigureStorePayoutProcessors", "UIPayoutProcessors", new {StoreId})}\">Configure now</a>";
|
||||
_callbackLink = LinkGenerator.GetUriByAction(HttpContextAccessor.HttpContext, "Callback", "Bringin", new {StoreId});
|
||||
@@ -194,6 +193,7 @@
|
||||
LastFiatBalance = await client.GetFiatBalance();
|
||||
LastFiatRate = (await client.GetRate()).BringinPrice;
|
||||
LastDataFetch = DateTimeOffset.UtcNow;
|
||||
LastTxs = await client.GetTransactions();
|
||||
_ = InvokeAsync(StateHasChanged);
|
||||
}
|
||||
finally
|
||||
@@ -210,6 +210,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
public BringinClient.GetTransactionListResponse LastTxs { get; set; }
|
||||
|
||||
|
||||
private void UpdateDestinationValue(BringinService.BringinStoreSettings.PaymentMethodSettings settings, object eValue)
|
||||
{
|
||||
@@ -417,9 +419,9 @@
|
||||
<div class="form-group">
|
||||
@* <label class="form-label">Payment method</label> *@
|
||||
<select @bind="ManualOrderPaymentMethod" class="form-select">
|
||||
<option value="">Select a payment method</option>
|
||||
@foreach (var opt in items)
|
||||
{
|
||||
<option value="">Select a payment method</option>
|
||||
<option value="@opt.ToString()">@opt.ToPrettyString()</option>
|
||||
}
|
||||
</select>
|
||||
@@ -569,49 +571,96 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@foreach (var method in _settings.MethodSettings)
|
||||
{
|
||||
var pmId = PaymentMethodId.TryParse(method.Key);
|
||||
if (pmId is null)
|
||||
continue;
|
||||
var supportedMethod = BringinService.SupportedMethods.FirstOrDefault(s => s.PaymentMethod.ToString() == method.Key);
|
||||
<div class="card col-xxl-constrain col-12 @(_settings.MethodSettings.Count > 1 ? "col-xl-6" : "")">
|
||||
<h5 class="card-header border-bottom-0 text-muted">@pmId.ToPrettyString()</h5>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Percentage</label>
|
||||
<input type="range" value="@method.Value.PercentageToForward" @oninput="@((e) => UpdateDestinationValue(method.Value, e.Value))" min="0" step='0.01' class="form-range" max="100"/>
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="number" step='0.01' value="@method.Value.PercentageToForward" @onchange="@((e) => UpdateDestinationValue(method.Value, e.Value))" class="form-control form-control-sm"/>
|
||||
<span class="input-group-text">%</span>
|
||||
</div>
|
||||
<p class="text-muted my-2">Every time an invoice becomes Settled, we take the sum of all settled payments of this payment method, get the specified percentage of it and add it to the current balance.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Threshold</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="number" @bind="method.Value.Threshold" min="@supportedMethod?.FiatMinimumAmount" class="form-control form-control-sm"/>
|
||||
<span class="input-group-text">@(method.Value.FiatThreshold ? "EUR" : "BTC")</span>
|
||||
</div>
|
||||
<p class="text-muted my-2">Once the threshold is reached, we create a payout sending the balance to Bringin to be converted.</p>
|
||||
|
||||
</div>
|
||||
@if (supportedMethod?.FiatMinimum is not true)
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row gx-5">
|
||||
@for (int i = 0; i < _settings.MethodSettings.Count; i++)
|
||||
{
|
||||
var method = _settings.MethodSettings.ElementAt(i);
|
||||
var pmId = PaymentMethodId.TryParse(method.Key);
|
||||
if (pmId is null)
|
||||
continue;
|
||||
var supportedMethod = BringinService.SupportedMethods.FirstOrDefault(s => s.PaymentMethod.ToString() == method.Key);
|
||||
<div class="col-xxl-constrain col-12 @(_settings.MethodSettings.Count > 1 ? $"col-xl-6 {(i == 0 ? "border-end" : "")}" : "")">
|
||||
<h5 class=" border-bottom-0 text-muted mb-4">@pmId.ToPrettyString()</h5>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" class="btcpay-toggle me-2" @bind="method.Value.FiatThreshold"/>
|
||||
<label class="form-label mb-0 me-1">Threshold in EUR</label>
|
||||
<label class="form-label">Percentage</label>
|
||||
<input type="range" value="@method.Value.PercentageToForward" @oninput="@((e) => UpdateDestinationValue(method.Value, e.Value))" min="0" step='0.01' class="form-range" max="100"/>
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="number" step='0.01' value="@method.Value.PercentageToForward" @onchange="@((e) => UpdateDestinationValue(method.Value, e.Value))" class="form-control form-control-sm"/>
|
||||
<span class="input-group-text">%</span>
|
||||
</div>
|
||||
<p class="text-muted my-2">Every time an invoice becomes Settled, we take the sum of all settled payments of this payment method, get the specified percentage of it and add it to the current balance.</p>
|
||||
</div>
|
||||
}
|
||||
<div class="form-group">
|
||||
<label class="form-label">Threshold</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="number" @bind="method.Value.Threshold" min="@supportedMethod?.FiatMinimumAmount" class="form-control form-control-sm"/>
|
||||
<span class="input-group-text">@(method.Value.FiatThreshold ? "EUR" : "BTC")</span>
|
||||
</div>
|
||||
<p class="text-muted my-2">Once the threshold is reached, we create a payout sending the balance to Bringin to be converted.</p>
|
||||
|
||||
</div>
|
||||
@if (supportedMethod?.FiatMinimum is not true)
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" class="btcpay-toggle me-2" @bind="method.Value.FiatThreshold"/>
|
||||
<label class="form-label mb-0 me-1">Threshold in EUR</label>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@if (LastTxs is not null)
|
||||
{
|
||||
<div class="widget store-numbers">
|
||||
<header>
|
||||
<h4 class="text-muted">Bringin Transactions</h4>
|
||||
</header>
|
||||
@if (LastTxs.Transactions.Any())
|
||||
{
|
||||
<div class="table-responsive my-0 " style=" max-height: 400px;">
|
||||
<table class="table table-hover mt-3 mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th class="text-end">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var tx in LastTxs.Transactions.OrderByDescending(transaction => transaction.CreatedAt))
|
||||
{
|
||||
<tr>
|
||||
<td>@tx.CreatedAt.ToTimeAgo()</td>
|
||||
<td>@tx.SubType.ToHumanReadable()</td>
|
||||
<td>
|
||||
@tx.Status.ToHumanReadable()
|
||||
</td>
|
||||
<td class="amount-col">
|
||||
<span data-sensitive>@(tx.SourceCurrency == "BTC" ? Money.Satoshis(tx.SourceAmount).ToDecimal(MoneyUnit.BTC): tx.SourceAmount)@tx.SourceCurrency -> @tx.DestinationAmount @tx.DestinationCurrency </span>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3 mb-0">
|
||||
There are no recent transactions.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
Reference in New Issue
Block a user