@using System.Threading @using BTCPayServer.Abstractions.Extensions @using BTCPayServer.Data @using BTCPayServer.Payments @using BTCPayServer.PayoutProcessors @using BTCPayServer.Services @using BTCPayServer.Services.Stores @using Microsoft.AspNetCore.Http @using Microsoft.AspNetCore.Routing @using NBitcoin @implements IAsyncDisposable; @code { private BringinService.BringinStoreSettings? _settings; private bool _isLoaded = false; private CancellationTokenSource _cts = new CancellationTokenSource(); [Inject] private IHttpContextAccessor HttpContextAccessor { get; set; } [Inject] private DisplayFormatter DisplayFormatter { get; set; } [Inject] private BringinService BringinService { get; set; } [Inject] private LinkGenerator LinkGenerator { get; set; } [Inject] private StoreRepository StoreRepository { get; set; } [Inject] private BTCPayNetworkProvider BTCPayNetworkProvider { get; set; } [Inject] private IHttpClientFactory HttpClientFactory { get; set; } [Inject] private PayoutProcessorService PayoutProcessorService { get; set; } [Parameter] public string StoreId { get; set; } private decimal? LastFiatBalance { get; set; } private DateTimeOffset? LastDataFetch { get; set; } private decimal? LastFiatRate { get; set; } private string SaveError { get; set; } private bool ApiKeyError { get => _apiKeyError; set { if (_apiKeyError == value) return; _apiKeyError = value; InvokeAsync(StateHasChanged); } } private bool IsLoaded { get => _isLoaded; set { if (_isLoaded == value) return; _isLoaded = value; InvokeAsync(StateHasChanged); } } private bool EditMode { get => _editMode; set { if (_editMode == value) return; _editMode = value; InvokeAsync(StateHasChanged); } } private bool _editMode; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { 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. Configure now"; _callbackLink = LinkGenerator.GetUriByAction(HttpContextAccessor.HttpContext, "Callback", "Bringin", new {StoreId}); _settings = BringinService.IsInEditMode(StoreId) ? await BringinService.Update(StoreId) : await BringinService.Get(StoreId); _pms = (await StoreRepository.FindStore(StoreId)).GetSupportedPaymentMethods(BTCPayNetworkProvider).Select(method => method.PaymentId).Where(id => id.CryptoCode == "BTC").ToArray(); _pps = (await PayoutProcessorService.GetProcessors(new PayoutProcessorService.PayoutProcessorQuery() { Stores = new[] {StoreId}, PaymentMethods = _pms.Select(p => p.ToString()).ToArray() })).Select(data => PaymentMethodId.TryParse(data.PaymentMethod)).Where(id => id is not null).ToArray(); EditMode = BringinService.IsInEditMode(StoreId); IsLoaded = true; _ = FetchBalanceAndRate(); BringinService.EditModeChanged += EditModeChanged; } await base.OnAfterRenderAsync(firstRender); } private string _callbackLink; private void EditModeChanged(object sender, (string storeId, bool editMode) e) { if (e.storeId != StoreId) { return; } if (EditMode == e.editMode) return; _ = e.editMode ? Edit() : CancelEdit(); InvokeAsync(StateHasChanged); } private async Task Edit() { if (_saving) return; SaveError = null; ApiKeyError = false; _settings = await BringinService.Update(StoreId); await TestApiKey(); EditMode = true; } private async Task CancelEdit() { if (_saving) return; SaveError = null; await BringinService.CancelEdit(StoreId); EditMode = false; _settings = await BringinService.Get(StoreId); } private async Task TestApiKey() { ApiKeyError = false; if (string.IsNullOrEmpty(_settings?.ApiKey)) return; try { var network = BTCPayNetworkProvider.GetNetwork("BTC"); var userId = await _settings.CreateClient(HttpClientFactory, network.NBitcoinNetwork).GetUserId(); ApiKeyError = string.IsNullOrEmpty(userId); } catch (Exception e) { ApiKeyError = true; } } private bool _saving; private PaymentMethodId[] _pms; private PaymentMethodId[] _pps; private bool _apiKeyError; private async Task Save() { if (_saving) return; try { _saving = true; await TestApiKey(); if (ApiKeyError) return; SaveError = null; if (!EditMode) return; await BringinService.Update(StoreId, _settings); EditMode = false; fetcherCTS?.Cancel(); } finally { _saving = false; } } CancellationTokenSource fetcherCTS; private async Task FetchBalanceAndRate() { if (_cts.IsCancellationRequested) return; fetcherCTS = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token); try { if (_settings?.ApiKey is null || EditMode) return; var network = BTCPayNetworkProvider.GetNetwork("BTC"); var client = _settings.CreateClient(HttpClientFactory, network.NBitcoinNetwork); LastFiatBalance = await client.GetFiatBalance(); LastFiatRate = (await client.GetRate()).BringinPrice; LastDataFetch = DateTimeOffset.UtcNow; _ = InvokeAsync(StateHasChanged); } finally { try { await Task.Delay(TimeSpan.FromMinutes(1), fetcherCTS.Token); } catch { } await FetchBalanceAndRate(); } } private void UpdateDestinationValue(BringinService.BringinStoreSettings.PaymentMethodSettings settings, object eValue) { var newValue = Math.Min(100, Math.Round(Convert.ToDecimal(eValue), 2)); settings.PercentageToForward = newValue; } public async ValueTask DisposeAsync() { await _cts.CancelAsync(); BringinService.EditModeChanged -= EditModeChanged; } public string ApiKey { get => _settings.ApiKey; set { if (_settings.ApiKey == value) return; _settings.ApiKey = value; InvokeAsync(StateHasChanged); } } public string TimeAgo { get { var res = LastDataFetch is null ? "" : LastDataFetch.Value.ToTimeAgo(); Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(_ => InvokeAsync(StateHasChanged)); return res; } } private Task CreateManual() { SaveError = null; _manualOrder = true; return InvokeAsync(StateHasChanged); } private Task CancelManual() { SaveError = null; _manualOrder = false; ManualOrderResult = null; return InvokeAsync(StateHasChanged); } public async Task SubmitOrder() { _saving = true; await InvokeAsync(StateHasChanged); var pm = PaymentMethodId.TryParse(ManualOrderPaymentMethod); if (pm is null) { SaveError = "Invalid payment method"; return; } try { SaveError = null; ManualOrderResult = await BringinService.CreateOrder(StoreId, pm, Money.Coins(ManualOrderAmount.Value), true); } catch (Exception e) { SaveError = e.Message; _saving = false; } await InvokeAsync(StateHasChanged); } private string ManualOrderResult = null; private bool _manualOrder = false; private string ManualOrderPaymentMethod = null; private decimal? ManualOrderAmount = null; // private bool ManualOrderPayout = true; public string PmiLink; public string OnboardLink; private async void ResetBalance(PaymentMethodId pmi) { if (_saving) return; try { _saving = true; await BringinService.ResetBalance(StoreId, pmi); } finally { _saving = false; } } }
@if (!IsLoaded) {

Loading Bringin offramp

} else {

Bringin Off-ramp @if (EditMode) { } else if (_settings?.Enabled is true) { } else if (_settings is not null) { }

@if (_manualOrder) { } else if (_settings is not null && !EditMode) { } else if (_settings is not null && EditMode) { if (ApiKeyError) { } if (!string.IsNullOrEmpty(_settings.ApiKey) && !ApiKeyError) { } }
@if (_settings is null) {

Bringin is a service that allows you to automatically convert your BTC to EUR and send it to your bank account. Start configuring it by clicking on the button below.

} else if (_manualOrder) { var items = new List(); items.AddRange(BringinService.SupportedMethods.Where(s => _pms.Contains(s.PaymentMethod)).Select(s => s.PaymentMethod));

Create an order irrespective of the current balance tracked by the plugin.

@if (!string.IsNullOrEmpty(SaveError)) {
@SaveError
} @if (!string.IsNullOrEmpty(ManualOrderResult)) {
Payout created: @ManualOrderResult
} else {
@* *@
@if (!string.IsNullOrEmpty(ManualOrderPaymentMethod)) { // var fiat = BringinService.SupportedMethods.First(s => s.PaymentMethod.ToString() == ManualOrderPaymentMethod)?.FiatMinimum is true;
BTC
@*
*@ @*
*@ @* *@ @* *@ @*
*@ @*
*@
} }
} else if (!EditMode) { @if (LastFiatBalance is not null) {
Balance

@LastFiatBalance

EUR @TimeAgo
} @foreach (var method in _settings.MethodSettings) { var pmi = PaymentMethodId.TryParse(method.Key); if (pmi is null) continue; if (!_pms.Contains(pmi)) continue;
@pmi.ToPrettyString()
@if (LastFiatRate is null || !method.Value.FiatThreshold) {

@DisplayFormatter.Currency(method.Value.CurrentBalance, "BTC", DisplayFormatter.CurrencyFormat.None)

BTC pending to forward once @DisplayFormatter.Currency(method.Value.Threshold, method.Value.FiatThreshold ? "EUR" : "BTC") is reached.
} else if (LastFiatRate is not null && method.Value.FiatThreshold) { var balanceInFiat = method.Value.CurrentBalance * LastFiatRate; var thresholdinBtc = method.Value.Threshold / LastFiatRate; var percentage = (balanceInFiat / method.Value.Threshold) * 100m;

@DisplayFormatter.Currency(method.Value.CurrentBalance, "BTC", DisplayFormatter.CurrencyFormat.None)

BTC (@DisplayFormatter.Currency(balanceInFiat.Value, "EUR", DisplayFormatter.CurrencyFormat.Code)) pending to forward once @DisplayFormatter.Currency(thresholdinBtc.Value, "BTC", DisplayFormatter.CurrencyFormat.Code) (@DisplayFormatter.Currency(method.Value.Threshold, "EUR")) is reached. @if (method.Value.CurrentBalance > 0) { //Clear balance }
} @if (method.Value.PendingPayouts.Any()) {

@method.Value.PendingPayouts.Count

pending payouts
} @if (!_pps.Contains(pmi) && PmiLink is not null) {

@((MarkupString) PmiLink)

}
} } else { @if (!string.IsNullOrEmpty(SaveError)) {
@SaveError
} @if (string.IsNullOrEmpty(_settings.ApiKey) || ApiKeyError) {

You can get one here

@if (ApiKeyError) {
Invalid API Key
}
} else {

You can get one here

@if (ApiKeyError) {
Invalid API Key
}
@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);
@pmId.ToPrettyString()
%

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.

@(method.Value.FiatThreshold ? "EUR" : "BTC")

Once the threshold is reached, we create a payout sending the balance to Bringin to be converted.

@if (supportedMethod?.FiatMinimum is not true) {
}
}
} } }