Compare commits

...

24 Commits

Author SHA1 Message Date
6d0f9120b8 prep for 1.10.2 2023-06-07 18:02:51 +02:00
aafb4a7f2a Fix stale invoice api for settle invoice
fixes #5049
2023-06-07 17:57:03 +02:00
ae432ff237 Fix: Crash on migation of old instances (Fix #5051) 2023-06-07 10:20:39 +02:00
cdc318c71a Pay Button: Fix circular reference when serializing JSON
When apps were set, the `GetAllApps` included the store data, which led to a circular reference when serializing the JSON. That data isn't necessary here, so we can just drop it before rendering.

Fixes #5038.
2023-06-05 12:35:06 +02:00
94d1cec8a9 Hide Sensitive Info: Fix script location
The script snippet needs to be located outside of the theme if-conditions, otherwise it only works if no custom theme is applied.
2023-06-05 12:34:54 +02:00
c0bc19ea59 Update Changelog 2023-06-02 18:21:57 +09:00
6f07714cd9 Language update 2023-06-02 18:18:10 +09:00
a9d2cac23c bump 1.10.1 2023-06-02 18:15:56 +09:00
693b46126b Bump Bitcoin core to 25.0 (#5032) 2023-06-02 16:41:35 +09:00
bbff9710bf fix cart + form combination bug fixes #5031 2023-06-02 09:34:55 +02:00
358e122775 Fix tests 2023-06-01 22:17:42 +09:00
3818468932 Pluginify on chain wallet setup (#4999)
* Pluginify on chain wallet setup

This PR fixes a few logical points in the wallet setup flow to allow more extensive plugin flexibility; It also fixes an issue when building plugins that requires an Altcoin config profile. Here is an example showcasing the Liquid+ plugin using this to enforce that it is a hot wallet (a requirement it has) and that import to RPC is always set, and a new option that is used to configure the wallet further https://i.imgur.com/pDPQ73v.gif

* Update BTCPayServer/Controllers/UIStoresController.Onchain.cs

* update nbx
2023-06-01 21:18:28 +09:00
3d2554fbe1 Make role name show uneditable when not creating 2023-05-31 15:49:34 +02:00
4309603317 Hide topup items from cart 2023-05-31 15:49:34 +02:00
f733c9ea77 Form Builder: Improve wording
Element -> Field. Something bas and I came across while reviewing the blog post.
2023-05-31 14:57:11 +02:00
775ee01171 fix store role deletion fixes #5027 2023-05-31 13:42:38 +02:00
33ec790137 Changelog 2023-05-31 11:50:10 +09:00
0c575c888c Remove payment requirement for marking expired invoices (#5006)
* Remove payment requirement for marking expired invoices

Allows to manually mark expired invoices, regardless of registered payments. See dennisreimann/btcpayserver-plugin-lnbank#34 for context, in which BTCPay Server sometimes did not register payments that were received to a LNbank wallet (this got fixed in btcpayserver/BTCPayServer.Lightning#129)

* Refactor conditions for better readability
2023-05-31 11:49:01 +09:00
24f7e51e3a Small adjustements 2023-05-31 11:27:03 +09:00
0a0cf97c55 Do not cleanup unreachable stores (#5025) 2023-05-31 11:22:37 +09:00
16b988d097 UI: Only display applicable refund options
Fixes #5019.
2023-05-30 12:51:51 +02:00
5edc0ff6ef UI: Fix visual bug with Hide Sensitive Info
Fixes #5011
2023-05-30 11:20:58 +02:00
375b96e508 UI: Center-align recovery phrase
Fixes #5007.
2023-05-30 11:19:16 +02:00
1e72b12074 UI: Store selector link distinguishes between owner and user
The `IsOwner` property went missing with #4940, so everyone landed on the invoices list when switching stores. This brings back the original behaviour of linking to the Dashboard, if the user has the permission to access it.

Fixes #5015.
2023-05-30 11:18:34 +02:00
37 changed files with 165 additions and 129 deletions

View File

@ -12,6 +12,8 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/btcpayserver/btcpayserver</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Configurations>Debug;Release;Altcoins-Debug;Altcoins-Release</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<PropertyGroup>
<Version Condition=" '$(Version)' == '' ">1.7.2</Version>

View File

@ -4,7 +4,7 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="NBXplorer.Client" Version="4.2.3" />
<PackageReference Include="NBXplorer.Client" Version="4.2.5" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="2.0.1" />
</ItemGroup>
<ItemGroup Condition="'$(Altcoins)' != 'true'">

View File

@ -306,6 +306,8 @@ retry:
foreach ((CurrencyPair key, Task<RateResult> value) in result)
{
var rateResult = value.GetAwaiter().GetResult();
if (key.ToString() == "BTG_USD")
continue; // shitcoin not supported by bitfinex anymore
TestLogs.LogInformation($"Testing {key}");
if (brokenShitcoins.Contains(key.ToString()))
continue;

View File

@ -73,7 +73,7 @@ services:
- "sshd_datadir:/root/.ssh"
devlnd:
image: btcpayserver/bitcoin:24.1-1
image: btcpayserver/bitcoin:25.0
environment:
BITCOIN_NETWORK: regtest
BITCOIN_WALLETDIR: "/data/wallets"
@ -135,7 +135,7 @@ services:
bitcoind:
restart: unless-stopped
image: btcpayserver/bitcoin:24.1-1
image: btcpayserver/bitcoin:25.0
environment:
BITCOIN_NETWORK: regtest
BITCOIN_WALLETDIR: "/data/wallets"

View File

@ -70,7 +70,7 @@ services:
- "sshd_datadir:/root/.ssh"
devlnd:
image: btcpayserver/bitcoin:24.1-1
image: btcpayserver/bitcoin:25.0
environment:
BITCOIN_NETWORK: regtest
BITCOIN_WALLETDIR: "/data/wallets"
@ -121,7 +121,7 @@ services:
bitcoind:
restart: unless-stopped
image: btcpayserver/bitcoin:24.1-1
image: btcpayserver/bitcoin:25.0
environment:
BITCOIN_NETWORK: regtest
BITCOIN_WALLETDIR: "/data/wallets"

View File

@ -23,15 +23,17 @@
@if (Model.Balance.OffchainBalance != null)
{
<div class="balance">
<h3 class="d-inline-block me-1" data-balance="@Model.TotalOffchain" data-sensitive>@Model.TotalOffchain</h3>
<span class="text-secondary fw-semibold text-nowrap">
<span class="currency">@Model.CryptoCode</span> in channels
</span>
<div class="d-flex align-items-baseline gap-1">
<h3 class="d-inline-block me-1" data-balance="@Model.TotalOffchain" data-sensitive>@Model.TotalOffchain</h3>
<span class="text-secondary fw-semibold text-nowrap">
<span class="currency">@Model.CryptoCode</span> in channels
</span>
</div>
<div class="balance-details collapse" id="balanceDetailsOffchain">
@if (Model.Balance.OffchainBalance.Opening != null)
{
<div class="mt-2">
<div class="mt-2 d-flex align-items-baseline gap-1">
<span class="fw-semibold" data-balance="@Model.Balance.OffchainBalance.Opening" data-sensitive>
@Model.Balance.OffchainBalance.Opening
</span>
@ -42,7 +44,7 @@
}
@if (Model.Balance.OffchainBalance.Local != null)
{
<div class="mt-2">
<div class="mt-2 d-flex align-items-baseline gap-1">
<span class="fw-semibold" data-balance="@Model.Balance.OffchainBalance.Local" data-sensitive>
@Model.Balance.OffchainBalance.Local
</span>
@ -53,7 +55,7 @@
}
@if (Model.Balance.OffchainBalance.Remote != null)
{
<div class="mt-2">
<div class="mt-2 d-flex align-items-baseline gap-1">
<span class="fw-semibold" data-balance="@Model.Balance.OffchainBalance.Remote" data-sensitive>
@Model.Balance.OffchainBalance.Remote
</span>
@ -64,7 +66,7 @@
}
@if (Model.Balance.OffchainBalance.Closing != null)
{
<div class="mt-2">
<div class="mt-2 d-flex align-items-baseline gap-1">
<span class="fw-semibold" data-balance="@Model.Balance.OffchainBalance.Closing" data-sensitive>
@Model.Balance.OffchainBalance.Closing
</span>
@ -79,14 +81,16 @@
@if (Model.Balance.OnchainBalance != null)
{
<div class="balance">
<h3 class="d-inline-block me-1" data-balance="@Model.TotalOnchain" data-sensitive>@Model.TotalOnchain</h3>
<span class="text-secondary fw-semibold text-nowrap">
<span class="currency">@Model.CryptoCode</span> on-chain
</span>
<div class="d-flex align-items-baseline gap-1">
<h3 class="d-inline-block me-1" data-balance="@Model.TotalOnchain" data-sensitive>@Model.TotalOnchain</h3>
<span class="text-secondary fw-semibold text-nowrap">
<span class="currency">@Model.CryptoCode</span> on-chain
</span>
</div>
<div class="balance-details collapse" id="balanceDetailsOnchain">
@if (Model.Balance.OnchainBalance.Confirmed != null)
{
<div class="mt-2">
<div class="mt-2 d-flex align-items-baseline gap-1">
<span class="fw-semibold" data-balance="@Model.Balance.OnchainBalance.Confirmed" data-sensitive>
@Model.Balance.OnchainBalance.Confirmed
</span>
@ -97,7 +101,7 @@
}
@if (Model.Balance.OnchainBalance.Unconfirmed != null)
{
<div class="mt-2">
<div class="mt-2 d-flex align-items-baseline gap-1">
<span class="fw-semibold" data-balance="@Model.Balance.OnchainBalance.Unconfirmed" data-sensitive>
@Model.Balance.OnchainBalance.Unconfirmed
</span>
@ -108,7 +112,7 @@
}
@if (Model.Balance.OnchainBalance.Reserved != null)
{
<div class="mt-2">
<div class="mt-2 d-flex align-items-baseline gap-1">
<span class="fw-semibold" data-balance="@Model.Balance.OnchainBalance.Reserved" data-sensitive>
@Model.Balance.OnchainBalance.Reserved
</span>

View File

@ -3,7 +3,6 @@
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Client
@using BTCPayServer.Services
@inject SignInManager<ApplicationUser> SignInManager
@inject BTCPayServerEnvironment Env
@inject IFileService FileService
@model BTCPayServer.Components.StoreSelector.StoreSelectorViewModel
@ -33,8 +32,7 @@
else
{
<a asp-controller="UIStores" asp-action="Dashboard" permission="@Policies.CanModifyStoreSettings" asp-route-storeId="@Model.CurrentStoreId" id="StoreSelectorHome" class="navbar-brand py-2">@{await LogoContent();}</a>
<a asp-controller="UIInvoice" asp-action="ListInvoices" not-permission="@Policies.CanModifyStoreSettings" asp-route-storeId="@Model.CurrentStoreId" id="StoreSelectorHome" class="navbar-brand py-2">@{await LogoContent();}</a>
<a asp-controller="UIInvoice" asp-action="ListInvoices" not-permission="@Policies.CanModifyStoreSettings" asp-route-storeId="@Model.CurrentStoreId" id="StoreSelectorHome" class="navbar-brand py-2">@{await LogoContent();}</a>
}
@if (Model.Options.Any())
{

View File

@ -1,5 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Identity;
@ -38,12 +39,14 @@ namespace BTCPayServer.Components.StoreSelector
.FirstOrDefault()?
.Network.CryptoCode;
var walletId = cryptoCode != null ? new WalletId(store.Id, cryptoCode) : null;
var role = store.GetStoreRoleOfUser(userId);
return new StoreSelectorOption
{
Text = store.StoreName,
Value = store.Id,
Selected = store.Id == currentStore?.Id,
WalletId = walletId
WalletId = walletId,
IsOwner = role != null && role.Permissions.Contains(Policies.CanModifyStoreSettings)
};
})
.OrderBy(s => s.Text)

View File

@ -17,7 +17,7 @@
<header class="mb-3">
@if (Model.Balance != null)
{
<div class="balance">
<div class="balance d-flex align-items-baseline gap-1">
<h3 class="d-inline-block me-1" data-balance="@Model.Balance" data-sensitive>@Model.Balance</h3>
<span class="text-secondary fw-semibold currency">@Model.CryptoCode</span>
</div>

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Amazon.S3.Transfer;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
@ -167,9 +166,17 @@ namespace BTCPayServer.Controllers
[FromServices] StoreRepository storeRepository,
string role)
{
await storeRepository.SetDefaultRole(role);
TempData[WellKnownTempData.SuccessMessage] = "Role set default";
var resolved = await storeRepository.ResolveStoreRoleId(null, role);
if (resolved is null)
{
TempData[WellKnownTempData.ErrorMessage] = "Role could not be set as default";
}
else
{
await storeRepository.SetDefaultRole(role);
TempData[WellKnownTempData.SuccessMessage] = "Role set default";
}
return RedirectToAction(nameof(ListRoles));
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
@ -250,7 +251,7 @@ namespace BTCPayServer.Controllers
CryptoCode = cryptoCode,
Method = method,
SetupRequest = request,
Confirmation = string.IsNullOrEmpty(request.ExistingMnemonic),
Confirmation = !isImport,
Network = network,
Source = isImport ? "SeedImported" : "NBXplorerGenerated",
IsHotWallet = isImport ? request.SavePrivateKeys : method == WalletSetupMethod.HotWallet,
@ -311,7 +312,7 @@ namespace BTCPayServer.Controllers
var result = await UpdateWallet(vm);
if (!ModelState.IsValid || !(result is RedirectToActionResult))
if (!ModelState.IsValid || result is not RedirectToActionResult)
return result;
if (!isImport)

View File

@ -141,7 +141,7 @@ namespace BTCPayServer.Controllers
"Delete"));
}
[HttpPost("{storeId}/roles/{roleId}/delete")]
[HttpPost("{storeId}/roles/{role}/delete")]
public async Task<IActionResult> DeleteRolePost(
string storeId,
[FromServices] StoreRepository storeRepository,

View File

@ -111,12 +111,6 @@ namespace BTCPayServer.Hosting
settings.DeprecatedLightningConnectionStringCheck = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.UnreachableStoreCheck)
{
await UnreachableStoreCheck();
settings.UnreachableStoreCheck = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.ConvertMultiplierToSpread)
{
await ConvertMultiplierToSpread();
@ -657,13 +651,6 @@ WHERE cte.""Id""=p.""Id""
settings1.TargetCurrency = app.StoreData.GetStoreBlob().DefaultCurrency;
app.SetSettings(settings1);
}
items = AppService.Parse(settings1.PerksTemplate);
newTemplate = AppService.SerializeTemplate(items);
if (settings1.PerksTemplate != newTemplate)
{
settings1.PerksTemplate = newTemplate;
app.SetSettings(settings1);
};
break;
case PointOfSaleAppType.AppType:
@ -674,13 +661,6 @@ WHERE cte.""Id""=p.""Id""
settings2.Currency = app.StoreData.GetStoreBlob().DefaultCurrency;
app.SetSettings(settings2);
}
items = AppService.Parse(settings2.Template);
newTemplate = AppService.SerializeTemplate(items);
if (settings2.Template != newTemplate)
{
settings2.Template = newTemplate;
app.SetSettings(settings2);
};
break;
}
}
@ -1068,11 +1048,6 @@ retry:
}
}
private Task UnreachableStoreCheck()
{
return _StoreRepository.CleanUnreachableStores();
}
private async Task DeprecatedLightningConnectionStringCheck()
{
using var ctx = _DBContextFactory.CreateContext();

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using NBXplorer.Models;
namespace BTCPayServer.Models.StoreViewModels

View File

@ -60,6 +60,11 @@ namespace BTCPayServer.Plugins.PayButton.Controllers
}
var apps = await _appService.GetAllApps(_userManager.GetUserId(User), false, store.Id);
// unset app store data, because we don't need it and inclusion leads to circular references when serializing to JSON
foreach (var app in apps)
{
app.App.StoreData = null;
}
var appUrl = HttpContext.Request.GetAbsoluteRoot().WithTrailingSlash();
var model = new PayButtonViewModel
{

View File

@ -356,7 +356,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
meta.Merge(formResponseJObject);
entity.Metadata = InvoiceMetadata.FromJObject(meta);
});
if (price is 0 && storeBlob.ReceiptOptions.Enabled is true)
if (price is 0 && storeBlob.ReceiptOptions?.Enabled is true)
{
return RedirectToAction(nameof(UIInvoiceController.InvoiceReceipt), "UIInvoice", new { invoiceId = invoice.Data.Id });
}

View File

@ -176,7 +176,7 @@ namespace BTCPayServer.Services.Apps
res.Add(new InvoiceStatsItem
{
ItemCode = item.Id,
FiatPrice = lineItem.Price.Value,
FiatPrice = lineItem.Price,
Date = e.InvoiceTime.Date
});
}

View File

@ -834,20 +834,13 @@ namespace BTCPayServer.Services.Invoices
public bool CanMarkComplete()
{
return (Status == InvoiceStatusLegacy.Paid) ||
(Status == InvoiceStatusLegacy.New) ||
((Status == InvoiceStatusLegacy.New || Status == InvoiceStatusLegacy.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidPartial) ||
((Status == InvoiceStatusLegacy.New || Status == InvoiceStatusLegacy.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidLate) ||
(Status != InvoiceStatusLegacy.Complete && ExceptionStatus == InvoiceExceptionStatus.Marked) ||
(Status == InvoiceStatusLegacy.Invalid);
return Status is InvoiceStatusLegacy.New or InvoiceStatusLegacy.Paid or InvoiceStatusLegacy.Expired or InvoiceStatusLegacy.Invalid ||
(Status != InvoiceStatusLegacy.Complete && ExceptionStatus == InvoiceExceptionStatus.Marked);
}
public bool CanMarkInvalid()
{
return (Status == InvoiceStatusLegacy.Paid) ||
(Status == InvoiceStatusLegacy.New) ||
((Status == InvoiceStatusLegacy.New || Status == InvoiceStatusLegacy.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidPartial) ||
((Status == InvoiceStatusLegacy.New || Status == InvoiceStatusLegacy.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidLate) ||
return Status is InvoiceStatusLegacy.New or InvoiceStatusLegacy.Paid or InvoiceStatusLegacy.Expired ||
(Status != InvoiceStatusLegacy.Invalid && ExceptionStatus == InvoiceExceptionStatus.Marked);
}

View File

@ -521,8 +521,14 @@ namespace BTCPayServer.Services.Invoices
invoiceData.Status = legacyStatus.ToLowerInvariant();
invoiceData.ExceptionStatus = InvoiceExceptionStatus.Marked.ToString().ToLowerInvariant();
_eventAggregator.Publish(new InvoiceEvent(ToEntity(invoiceData), eventName));
await context.SaveChangesAsync();
try
{
await context.SaveChangesAsync();
}
finally
{
_eventAggregator.Publish(new InvoiceEvent(ToEntity(invoiceData), eventName));
}
}
return true;

View File

@ -34,7 +34,7 @@ public class PosAppCartItem
public string Id { get; set; }
[JsonProperty(PropertyName = "price")]
public PosAppCartItemPrice Price { get; set; }
public decimal Price { get; set; }
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
@ -54,8 +54,6 @@ public class PosAppCartItemPrice
[JsonProperty(PropertyName = "formatted")]
public string Formatted { get; set; }
[JsonProperty(PropertyName = "value")]
public decimal Value { get; set; }
[JsonProperty(PropertyName = "type")]
public ViewPointOfSaleViewModel.ItemPriceType Type { get; set; }

View File

@ -7,7 +7,6 @@ namespace BTCPayServer.Services
[JsonProperty("MigrateHotwalletProperty2")]
public bool MigrateHotwalletProperty { get; set; }
public bool MigrateU2FToFIDO2 { get; set; }
public bool UnreachableStoreCheck { get; set; }
public bool DeprecatedLightningConnectionStringCheck { get; set; }
public bool ConvertMultiplierToSpread { get; set; }
public bool ConvertNetworkFeeProperty { get; set; }

View File

@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Amazon.S3;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client;
using BTCPayServer.Data;
@ -229,18 +230,19 @@ namespace BTCPayServer.Services.Stores
/// <param name="storeId"></param>
/// <param name="role"></param>
/// <returns></returns>
public async Task<StoreRoleId?> ResolveStoreRoleId(string storeId, string? role)
public async Task<StoreRoleId?> ResolveStoreRoleId(string? storeId, string? role)
{
if (string.IsNullOrWhiteSpace(role))
return null;
var isStoreRole = role.Contains("::", StringComparison.OrdinalIgnoreCase);
if(isStoreRole && (string.IsNullOrEmpty(storeId) || !role.Contains(storeId, StringComparison.InvariantCultureIgnoreCase)))
if (storeId?.Contains("::", StringComparison.OrdinalIgnoreCase) is true)
return null;
var roleId = StoreRoleId.Parse(role);
if (roleId.StoreId != null && roleId.StoreId != storeId)
return null;
if ((await GetStoreRole(roleId)) != null)
return roleId;
if (string.IsNullOrEmpty(storeId))
return null;
if (roleId.IsServerRole)
roleId = new StoreRoleId(storeId, role);
if ((await GetStoreRole(roleId)) != null)

View File

@ -157,8 +157,6 @@ namespace BTCPayServer.Services
{
_logger.LogError($"Failed to delete user {user.Id}");
}
await _storeRepository.CleanUnreachableStores();
}

View File

@ -41,7 +41,8 @@
}
else
{
<input asp-for="Role" required="required" class="form-control" readonly />
<input type="hidden" asp-for="Role"/>
<input required="required" class="form-control" disabled value="@Model.Role" />
}
<span asp-validation-for="Role" class="text-danger"></span>
</div>

View File

@ -5,6 +5,7 @@
@inject ThemeSettings Theme
@inject IFileService FileService
<script>if (window.localStorage.getItem('btcpay-hide-sensitive-info') === 'true') { document.documentElement.setAttribute('data-hide-sensitive-info', 'true')}</script>
@if (Theme.CustomTheme && !string.IsNullOrEmpty(Theme.CssUri))
{ // legacy customization with CSS URI - keep it for backwards-compatibility
<link href="@Context.Request.GetRelativePathOrAbsolute(Theme.CssUri)" rel="stylesheet" asp-append-version="true" />
@ -25,7 +26,6 @@ else
{
<link href="~/main/themes/default.css" asp-append-version="true" rel="stylesheet" />
<link href="~/main/themes/default-dark.css" asp-append-version="true" rel="stylesheet" id="DarkThemeLinkTag" />
<script>if (window.localStorage.getItem('btcpay-hide-sensitive-info') === 'true') { document.documentElement.setAttribute('data-hide-sensitive-info', 'true')}</script>
<script src="~/js/theme-switch.js" asp-append-version="true"></script>
<noscript><style>.btcpay-theme-switch { display: none !important; }</style></noscript>
}

View File

@ -236,6 +236,10 @@
@for (var index = 0; index < Model.Items.Length; index++)
{
var item = Model.Items[index];
if (item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Topup)
{
continue;
}
var image = item.Image;
var description = item.Description;

View File

@ -116,7 +116,7 @@
</div>
<button type="button" class="btn btn-link py-0 px-2 mt-2 mb-2 gap-1 add fw-semibold d-inline-flex align-items-center" v-on:click.stop="$emit('add-field', $event, path)">
<vc:icon symbol="new" />
Add Form Element
Add Form Field
</button>
</div>
</template>

View File

@ -43,7 +43,7 @@
Write them down on a piece of paper in the exact order:
</p>
</div>
<ol id="RecoveryPhrase" data-mnemonic="@Model.Mnemonic" class="my-5 mx-auto">
<ol id="RecoveryPhrase" data-mnemonic="@Model.Mnemonic" class="d-inline-block my-5 mx-auto ps-4">
@foreach (var word in Model.Words)
{
<li class="text-start text-muted py-2">

View File

@ -23,13 +23,14 @@
<meta name="robots" content="noindex,nofollow">
@if (isProcessing)
{
<script type="text/javascript">
setTimeout(() => { window.location.reload(); }, 10000);
<script type="text/javascript">
setTimeout(() => { window.location.reload(); }, 10000);
</script>
}else if (isFreeInvoice)
}
else if (isFreeInvoice)
{
<script type="text/javascript">
setTimeout(() => { window.location.reload(); }, 2000);
<script type="text/javascript">
setTimeout(() => { window.location.reload(); }, 2000);
</script>
}
<style>

View File

@ -62,27 +62,36 @@
</div>
<hr class="border" />
}
<div class="form-group">
<div class="form-check">
<input id="RateThenOption" asp-for="SelectedRefundOption" type="radio" value="RateThen" class="form-check-input"/>
<label for="RateThenOption" class="form-check-label">@Model.RateThenText</label>
<div class="form-text">The crypto currency price, at the rate the invoice got paid.</div>
@if (Model.CryptoAmountThen > 0)
{
<div class="form-group">
<div class="form-check">
<input id="RateThenOption" asp-for="SelectedRefundOption" type="radio" value="RateThen" class="form-check-input" />
<label for="RateThenOption" class="form-check-label">@Model.RateThenText</label>
<div class="form-text">The crypto currency price, at the rate the invoice got paid.</div>
</div>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input id="CurrentRateOption" asp-for="SelectedRefundOption" type="radio" value="CurrentRate" class="form-check-input"/>
<label for="CurrentRateOption" class="form-check-label">@Model.CurrentRateText</label>
<div class="form-text">The crypto currency price, at the current rate.</div>
}
@if (Model.CryptoAmountNow > 0)
{
<div class="form-group">
<div class="form-check">
<input id="CurrentRateOption" asp-for="SelectedRefundOption" type="radio" value="CurrentRate" class="form-check-input" />
<label for="CurrentRateOption" class="form-check-label">@Model.CurrentRateText</label>
<div class="form-text">The crypto currency price, at the current rate.</div>
</div>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input id="FiatOption" asp-for="SelectedRefundOption" type="radio" value="Fiat" class="form-check-input"/>
<label for="FiatOption" class="form-check-label">@Model.FiatText</label>
<div class="form-text">The invoice currency, at the rate when the refund will be sent.</div>
}
@if (Model.FiatAmount > 0)
{
<div class="form-group">
<div class="form-check">
<input id="FiatOption" asp-for="SelectedRefundOption" type="radio" value="Fiat" class="form-check-input" />
<label for="FiatOption" class="form-check-label">@Model.FiatText</label>
<div class="form-text">The invoice currency, at the rate when the refund will be sent.</div>
</div>
</div>
</div>
}
<div class="form-group">
<div class="form-check">
<input id="CustomOption" asp-for="SelectedRefundOption" type="radio" value="Custom" class="form-check-input"/>

View File

@ -140,7 +140,13 @@
</div>
</div>
</div>
@if (Model.AdditionalOptions is not null)
{
@foreach (var dictKeys in Model.AdditionalOptions)
{
<input type="hidden" asp-for="AdditionalOptions[dictKeys.Key]" />
}
}
<button type="submit" class="btn btn-primary" id="Continue">@(isImport ? "Continue" : "Create")</button>
</form>

View File

@ -19,3 +19,5 @@
}
@RenderBody()
<vc:ui-extension-point location="onchain-wallet-setup-post-body" model="@Model"/>

View File

@ -11,20 +11,20 @@
"pay_in_wallet": "Πληρωμή στο πορτοφόλι",
"pay_by_nfc": "Πληρωμή μέσω NFC",
"pay_by_lnurl": "Πληρωμή με LNURL-Withdraw",
"invoice_id": "ID Παραστατικού Πληρωμής",
"order_id": "ID Παραγγελίας",
"invoice_id": "ID παραστατικού πληρωμής",
"order_id": "ID παραγγελίας",
"total_price": "Συνολική τιμή",
"total_fiat": "Συνολική τιμή σε νόμισμα",
"exchange_rate": "Ισοτιμία",
"amount_paid": "Πληρωθέν ποσό",
"amount_due": "Οφειλόμενο ποσό",
"recommended_fee": "Συνιστώμενη Αμοιβή",
"recommended_fee": "Συνιστώμενη αμοιβή",
"fee_rate": "{{feeRate}} sat/byte",
"network_cost": "Κόστος Δικτύου",
"network_cost": "Κόστος δικτύου",
"tx_count": "{{count}} συναλλαγή",
"qr_text": "Σαρώστε τον κωδικό QR ή πατήστε για να αντιγράψετε τη διεύθυνση.",
"address": "Διεύθυνση",
"lightning": "Αστραπή (Lightning)",
"lightning": "Lightning",
"payment_link": "Σύνδεσμος πληρωμής",
"invoice_paid": "Εξοφλημένο τιμολόγιο",
"invoice_expired": "Η τιμολόγηση έληξε",

View File

@ -3,16 +3,16 @@
"code": "el-GR",
"currentLanguage": "Ελληνικά",
"lang": "Γλώσσα",
"Awaiting Payment...": "Αναμονή Πληρωμής...",
"Awaiting Payment...": "Αναμονή πληρωμής...",
"Pay with": "Πληρώστε με",
"Contact and Refund Email": "Email Επικοινωνίας & Επιστροφής Πληρωμής",
"Contact_Body": "Παρακαλούμε εισάγετε το email σας παρακάτω. Θα επικοινωνήσουμε μαζί σας σε αυτή τη διεύθυνση ηλεκτρονικής αλληλογραφίας εαν προκύψει κάποιο θέμα με την πληρωμή σας.",
"Contact and Refund Email": "Email επικοινωνίας & επιστροφής πληρωμής",
"Contact_Body": "Παρακαλούμε εισάγετε το email σας παρακάτω. Θα επικοινωνήσουμε μαζί σας σε αυτή τη διεύθυνση ηλεκτρονικής αλληλογραφίας εάν προκύψει κάποιο θέμα με την πληρωμή σας.",
"Your email": "Το email σας",
"Continue": "Συνέχεια",
"Please enter a valid email address": "Παρακαλούμε εισάγετε μια έγκυρη διεύθυνση email",
"Order Amount": "Ποσό Παραγγελίας",
"Network Cost": "Κόστος Δικτύου",
"Already Paid": "Πληρώθηκαν Ήδη",
"Order Amount": "Ποσό παραγγελίας",
"Network Cost": "Κόστος δικτύου",
"Already Paid": "Πληρώθηκαν ήδη",
"Due": "Οφειλόμενα",
"Scan": "Σάρωση",
"Copy": "Αντιγραφή",
@ -34,14 +34,14 @@
"InvoiceExpired_Body_1": "Το παρών παραστατικό πληρωμής έχει λήξει. Ένα παραστατικό πληρωμής ισχύει μόνο για {{maxTimeMinutes}} λεπτά.\nΜπορείτε να επιστρέψετε στο {{storeName}} εάν θα θέλατε να υποβάλετε ξανά την πληρωμή σας.",
"InvoiceExpired_Body_2": "Εάν επιχειρήσατε να στείλετε την πληρωμή σας, αυτή ακόμη δεν έχει γίνει αποδεκτή απο το δίκτυο. Δέν έχουμε λάβει ακόμη την πληρωμή σας.",
"InvoiceExpired_Body_3": "Εάν την λάβουμε αργότερα, είτε θα εκτέλεσουμε την παραγγελία σας ή θα επικοινωνήσουμε μαζί σας για να οργανώσουμε την επιστροφή των χρημάτων σας...",
"Invoice ID": "ID Παραστατικού Πληρωμής",
"Order ID": "ID Παραγγελίας",
"Invoice ID": "ID παραστατικού πληρωμής",
"Order ID": "ID παραγγελίας",
"Return to StoreName": "Επιστροφή στο {{storeName}}",
"This invoice has been paid": "Αυτό το παραστατικό έχει πληρωθεί",
"This invoice has been archived": "Αυτό το παραστατικό έχει αρχειοθετηθεί",
"Archived_Body": "Παρακαλούμε επικοινωνήστε με το κατάστημα για πληροφορίες σχετικά με την παραγγελία ή εάν χρειάζεστε βοήθεια",
"BOLT 11 Invoice": "Παραστατικό BOLT 11",
"Node Info": "Πληροφορίες Κόμβου",
"Node Info": "Πληροφορίες κόμβου",
"txCount": "{{count}} συναλλαγή",
"txCount_plural": "{{count}} συναλλαγών",
"Pay with CoinSwitch": "Πληρώστε με CoinSwitch",

View File

@ -10,18 +10,17 @@
/* Hide sensitive info */
[data-hide-sensitive-info="true"] [data-sensitive] {
visibility: hidden;
display: inline-flex;
position: relative;
visibility: hidden;
overflow: hidden;
}
[data-hide-sensitive-info="true"] [data-sensitive]:before {
content: '***';
content: '***********************';
visibility: visible;
position: absolute;
}
[data-hide-sensitive-info="true"] .text-end [data-sensitive]:before {
right: 0;
top: .2em;
}
[data-hide-sensitive-info="true"] .store-wallet-balance .ct-label.ct-vertical.ct-start {

View File

@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<Version>1.10.0</Version>
<Version>1.10.2</Version>
</PropertyGroup>
</Project>

View File

@ -1,5 +1,24 @@
# Changelog
## 1.10.2
### Bug fixes
* Fix: Stale data when fetching invoice after webhook (#5049) @Kukks
* Fix: Crash on migation of old instances (#5051) @NicolasDorier
* Fix: Hide sensitive info feature not working with custom theme (#5044) @dennisreimann
* Fix: Pay button not rendering on the invoice page (#5043) @dennisreimann
## 1.10.1
### Bug fixes
* Point of Sale bug after filling out form Shop + cart (#5031) @Kukks
### Improvements
* Language translation update for el-GR
## 1.10.0
Notice: Due to the substantial disk space consumption, we are removing all data pertaining to past webhook deliveries (#5005).
@ -32,6 +51,7 @@ This data, generally used for debugging integrations, will be regularly purged.
* Improve create first store case (#4951) @dennisreimann @dstrukt
* Improve Refund UI/UX (#4934 #3839 #4812) @dennisreimann @dstrukt
* Prune old webhook delivery data (#5005) @NicolasDorier
* Can mark expired invoices as complete or invalid (#5006) @dennisreimann
## 1.9.3