Compare commits

...

107 Commits

Author SHA1 Message Date
94be2b46d5 docker build should use right api.nuget.org server 2018-12-10 23:36:54 +09:00
4b4d0d2d19 Adding working server for api.nuget.org 2018-12-10 22:35:43 +09:00
0d06cf63b7 Use enum for invoice status and invoice exception 2018-12-10 21:48:28 +09:00
7b24c02d51 bump 2018-12-10 20:34:34 +09:00
e89e8226e4 Fix build 2018-12-10 17:34:27 +09:00
a533a96598 Remove XFrame for PoS 2018-12-10 16:39:21 +09:00
27321c0919 bump 2018-12-10 16:04:28 +09:00
058472d325 Show restricted macaroon for LND 2018-12-10 16:03:58 +09:00
b5c9a03052 Can mark invoice as complete 2018-12-10 15:34:48 +09:00
07dad3affa bump 2018-12-07 19:35:25 +09:00
8afc103ae7 Show REST connection information for LND in a QR Code 2018-12-07 19:31:07 +09:00
591d7b4b80 Can show external service link with BTCPAY_EXTERNALSERVICES 2018-12-07 18:42:39 +09:00
2162afc78e Lightning network warnings 2018-12-07 17:54:10 +09:00
25e226d219 Clarify the code 2018-12-07 14:37:07 +09:00
8472bfe90d Add test for bad bitid signature 2018-12-07 14:34:07 +09:00
93645b2fbe Fix error 500 if token not found 2018-12-07 13:48:39 +09:00
d53c987f2e bump 2018-12-06 17:25:50 +09:00
682693a9f0 Update translations 2018-12-06 17:23:42 +09:00
e836faf792 Stop setting BIP70 link info 2018-12-06 17:12:51 +09:00
6e27233be8 Remove BIP70 support 2018-12-06 17:08:28 +09:00
9209984a2f Remove useless argument from GetInvoice 2018-12-06 17:05:27 +09:00
1477630c78 Remove anonymous access to invoice data 2018-12-06 16:58:04 +09:00
ab670080c7 bump 2018-12-06 12:29:13 +09:00
8198f98376 Code simplification 2018-12-06 12:26:42 +09:00
65b4697229 Properly error 401 if request is not signed correctly 2018-12-06 12:22:05 +09:00
e75a1a8b70 Improve ledger feedback for asking authorization to access xpub 2018-12-04 21:22:27 +09:00
5a958da84d bump 2018-12-04 13:04:56 +09:00
cad602ad14 Fix several issues in cart
* Fix: Only USD currency with 2 decimals were properly handled for tips
* Fix: All PoS apps would were sharing the same basket
* Fix: Currency formatting was not using server side information
* Fix: Various bug of formatting for decimal 0 and more than 2.
2018-12-04 13:04:26 +09:00
1f14bd6188 Add button and qr code to the bitpay translator 2018-12-04 11:53:25 +09:00
156f52b76f Add bitpay translator 2018-12-03 23:59:08 +09:00
d674b8ac71 Merge pull request #430 from dalijolijo/master
bump
2018-12-01 22:40:48 +09:00
861150971f bump 2018-12-01 10:15:57 +00:00
a653421514 Merge pull request #428 from mariodian/fix-pos-cart-currency
Fix currency format for total amount
2018-12-01 14:09:01 +09:00
8f234a02cb Add currency formats for major currencies 2018-12-01 12:59:45 +08:00
92ecf99427 bump 2018-12-01 13:23:56 +09:00
705dbf12d7 Change translation of the expiration screen 2018-12-01 13:19:35 +09:00
fe11b11c13 Add Polski and Srpski 2018-12-01 12:02:53 +09:00
f2a43ad1f3 Escape js properties in html template 2018-11-30 21:14:09 +08:00
cbbe5cfb25 - fix currency format for numbers over 999
- fix cart table
2018-11-30 20:01:47 +08:00
0eccc6085b bump 2018-11-30 04:34:38 -06:00
a89da1f705 Recoding test to respect new ordering in CSV 2018-11-30 04:34:18 -06:00
5b297e539a Additional fields and ordering based on feedback 2018-11-30 04:18:37 -06:00
1d932c3753 Improve invoice script if no PoS data available 2018-11-30 04:17:57 -06:00
5a77fc74ba quickly fix changelly button style (#423)
Fix the button for now so it doesn't appear broken.
2018-11-30 04:17:44 -06:00
7b47b96252 Always using quotes for CSV export 2018-11-30 03:15:23 -06:00
a4bec83ecc Fixing warnings on invariant culture, hate this for being so verbose 2018-11-30 02:51:23 -06:00
8509a0de18 Basic export CSV and JSON tests 2018-11-30 02:34:43 -06:00
8e30b7430d Adding PaymentType and destination, CSV export 2018-11-30 02:04:26 -06:00
9235d32a45 Export of payments made on invoices 2018-11-30 01:22:39 -06:00
dd503570ac bump 2018-11-30 11:30:30 +09:00
613281a1e7 Fix form processing when cart is enabled (#424) 2018-11-30 11:29:27 +09:00
bab7bf6633 bump 2018-11-27 15:17:32 +09:00
1831692761 Enable shopping cart, add items to cart, enable tips (#410)
Modal cart, remove items, checkout

Fix removal and adding of cart items

Improve cart UI

Add cart bundle, remove unused js files from the view when cart isn't used

Do not enable cart by default

Do not put modal into the view when the cart is disabled

Escape js properties

Work with amounts as cents

Make animation speed look constant

Enable tips in the cart

Fix cart UI
2018-11-27 15:14:32 +09:00
e144d2479b Add POS Data to Invoice UI (#409)
* Add POS Data in Invoice UI

* fix build

* extract in helper and add UTs

* add in unit test coverage through mvc view too
2018-11-27 15:13:09 +09:00
c25831316e bump nbx 2018-11-26 12:02:50 +09:00
60b72aabe8 fix test 2018-11-24 13:38:23 +09:00
c8fcb0ab18 Use framework dependent build for ARM 2018-11-23 16:14:13 +09:00
9911d18390 Do not push latest images to dockerhub 2018-11-23 14:12:47 +09:00
e24630ac1e Remove qemu install requirement for the host 2018-11-23 14:08:14 +09:00
4c1fd3edae More comment on ARM build 2018-11-23 14:02:44 +09:00
f65492dd66 Use stretch slim for arm 2018-11-23 14:00:33 +09:00
5d978c7670 Use manifest image for building arm images 2018-11-23 13:58:21 +09:00
11788cece9 No need to create latest tag 2018-11-23 13:18:35 +09:00
1aaa55dc62 Make test less flaky 2018-11-23 13:09:30 +09:00
ce57a2b8fb Do not tag latest 2018-11-23 12:59:48 +09:00
0604cc5bd0 bump 2018-11-23 11:37:05 +09:00
3d2c0bcc6c Use specific sdk and runtime version for arm 2018-11-23 11:23:27 +09:00
0f222979a6 CircleCI multiarch Docker images (#416)
* Preparing final version of CircleCI docker building

* Removing test job requirement for building Docker images

We'll already monitor build before tagging, would be too many checks

* Adding pushing of manifest for tag

* Easy access to docker/circle config files for edit

* Generalizing script with $DOCKERHUB_REPO variable
2018-11-23 11:21:01 +09:00
a1eb6a14f5 Fix all script because of docker-compose team screwing up (https://github.com/docker/compose/issues/6316) 2018-11-22 16:16:10 +09:00
186ce01022 add pairing code to tokens page after authorize (#412) 2018-11-22 15:13:35 +09:00
0096ec1d12 bump nbxplorer 2018-11-21 20:41:51 +09:00
2929d7bf51 Fixing MONA_BTC rate breaking tests and CircleCI because zaif is down 2018-11-20 15:42:45 -06:00
d90fb5764d Add noindex,nofollow to invoices, checkouts and fix create invoice ui bug (#407)
* add noindex,nofollow on invoices

* fix create invoice button and add noindex,nofollow to checkout and invoice pages
2018-11-19 13:20:48 +09:00
4dccd0c733 Add better instruction on how to customize the theme 2018-11-17 12:43:11 +09:00
300d912331 bump 2018-11-17 11:43:41 +09:00
9d21c89151 Preserving title with custom amount (#403)
* Preserving title with custom amount

* Custom button texts for complete localization

* Update tests, now checking custom amount description and button text

* Support for Custom CSS in POS
2018-11-17 11:39:43 +09:00
24a8c4015c Bump 2018-11-17 11:35:20 +09:00
5eb40d6b7f Bugfixing redirect button (#405) 2018-11-17 11:32:31 +09:00
36f486e91b Add test for the parser 2018-11-17 01:45:59 +09:00
5b684ac26e Make really sure we don't generate segwit addresses for non segwit coins 2018-11-17 01:39:32 +09:00
85062725bd bump 2018-11-17 01:21:56 +09:00
401d9c8565 DerivationSchemeParser should not override a label 2018-11-17 01:21:34 +09:00
6f276ac1bc Do not crash if derivation strategy is empty 2018-11-17 01:09:28 +09:00
4350785cef Remove double slash 2018-11-17 00:23:51 +09:00
9a2a85ac3d Update translations 2018-11-17 00:22:18 +09:00
d030a61322 bump 2018-11-17 00:16:31 +09:00
dacb6dca41 bump .net core 2018-11-17 00:13:22 +09:00
c40fc69087 Use the choiceKey of PoS item as ItemCode 2018-11-16 23:16:44 +09:00
eff983135c showcase the custom field in PoS template 2018-11-16 18:36:18 +09:00
479303dd9e Tweaking UI for custom amounts (#398)
* Tweaking appearance of custom amount card

* Allowing POS items to have custom amounts, good for donations/tips

* Prepending currency symbol in POS

* Fixing regression, thanks unit test
2018-11-16 12:31:38 +09:00
e9b2088f7d change default title for pointofsale 2018-11-14 17:45:46 +09:00
4af5b94013 Add tooling which pull transifex translation automatically, add Slovenčina. (Close #386) 2018-11-14 16:48:25 +09:00
441398402d Remove global.json because .403 became suddenly unavailable 2018-11-13 16:41:49 +09:00
258d4fda3f bump 2018-11-13 16:37:43 +09:00
8e667f6c3f Allow empty template (Fix #303) 2018-11-13 16:32:13 +09:00
a996cc2e6d Fix margins, change template (#397) 2018-11-13 16:29:18 +09:00
9b8a8690e7 Change links to gitbook 2018-11-13 16:21:58 +09:00
f2387fd6b5 Workaround to compile on circle 2018-11-13 16:16:57 +09:00
888036a99d use docker on Circle CI 2018-11-13 15:55:10 +09:00
539c0ed7f0 show dotnet info on CI 2018-11-13 15:47:25 +09:00
95e065a462 Add tooltip to update store (#382) 2018-11-13 15:36:07 +09:00
087f20cb6c Fix small view error in logs (#392) 2018-11-12 22:25:39 +09:00
7adf321956 Checkout Experience Language Setting (#393)
* fix check out experience default language validation of preset value not found

* Update CheckoutExperienceViewModel.cs
2018-11-12 22:17:00 +09:00
dc749462ec automatically detect the btcpay server url in btcpay.js 2018-11-10 23:43:48 +09:00
16b57f24a2 Fix #383 2018-11-10 23:25:11 +09:00
b16b1c3e8b - add item image and description (#391)
- fix margins
2018-11-10 15:38:26 +09:00
fee56873b5 Handle exception if log file do not exists. 2018-11-09 21:43:10 +09:00
105 changed files with 3443 additions and 1503 deletions

View File

@ -5,25 +5,102 @@ jobs:
docker_layer_caching: true
steps:
- checkout
test:
machine: true
steps:
- checkout
- run:
command: |
echo "117.18.232.200 api.nuget.org" | sudo tee -a /etc/hosts
lsb_release -a
wget -q https://packages.microsoft.com/config/ubuntu/14.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-2.1
dotnet --info
dotnet build /p:TreatWarningsAsErrors=true
cd BTCPayServer.Tests
dotnet test --filter Fast=Fast
docker-compose up -d dev
dotnet test --filter Integration=Integration
# publish jobs require $DOCKERHUB_REPO, $DOCKERHUB_USER, $DOCKERHUB_PASS defined
publish_docker_linuxamd64:
machine:
docker_layer_caching: true
steps:
- checkout
- run:
command: |
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
#
sudo docker build --add-host "api.nuget.org:117.18.232.200" --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f Dockerfile.linuxamd64 .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-amd64
publish_docker_linuxarm:
machine:
docker_layer_caching: true
steps:
- checkout
- run:
command: |
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
#
sudo docker build --add-host "api.nuget.org:117.18.232.200" --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f Dockerfile.linuxarm32v7 .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
publish_docker_multiarch:
machine:
enabled: true
image: circleci/classic:201808-01
steps:
- run:
command: |
# Turn on Experimental features
sudo mkdir $HOME/.docker
sudo sh -c 'echo "{ \"experimental\": \"enabled\" }" >> $HOME/.docker/config.json'
#
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
#
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 --os linux --arch amd64
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 --os linux --arch arm --variant v7
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG -p
workflows:
version: 2
build_and_test:
jobs:
- test
publish:
jobs:
- publish_docker_linuxamd64:
filters:
# ignore any commit on any branch by default
branches:
ignore: /.*/
# only act on version tags
tags:
only: /v[1-9]+(\.[0-9]+)*/
- publish_docker_linuxarm:
filters:
branches:
ignore: /.*/
tags:
only: /v[1-9]+(\.[0-9]+)*/
- publish_docker_multiarch:
requires:
- publish_docker_linuxamd64
- publish_docker_linuxarm
filters:
branches:
ignore: /.*/
tags:
only: /v[1-9]+(\.[0-9]+)*/

View File

@ -6,6 +6,7 @@
<IsPackable>false</IsPackable>
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
<LangVersion>7.2</LangVersion>
<UserSecretsId>AB0AC1DD-9D26-485B-9416-56A33F268117</UserSecretsId>
</PropertyGroup>
<ItemGroup>

View File

@ -43,10 +43,12 @@ using System.Security.Cryptography.X509Certificates;
using BTCPayServer.Lightning;
using BTCPayServer.Models.WalletViewModels;
using System.Security.Claims;
using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Models.ServerViewModels;
using BTCPayServer.Security;
using NBXplorer.Models;
using RatesViewModel = BTCPayServer.Models.StoreViewModels.RatesViewModel;
using NBitpayClient.Extensions;
namespace BTCPayServer.Tests
{
@ -349,57 +351,6 @@ namespace BTCPayServer.Tests
}
}
[Fact]
[Trait("Integration", "Integration")]
public void CanPayUsingBIP70()
{
using (var tester = ServerTester.Create())
{
tester.Start();
var user = tester.NewAccount();
user.GrantAccess();
user.RegisterDerivationScheme("BTC");
Assert.True(user.BitPay.TestAccess(Facade.Merchant));
var invoice = user.BitPay.CreateInvoice(new Invoice()
{
Buyer = new Buyer() { email = "test@fwf.com" },
Price = 5000.0m,
Currency = "USD",
PosData = "posData",
OrderId = "orderId",
//RedirectURL = redirect + "redirect",
//NotificationURL = CallbackUri + "/notification",
ItemDesc = "Some description",
FullNotifications = true
}, Facade.Merchant);
Assert.False(invoice.Refundable);
var url = new BitcoinUrlBuilder(invoice.PaymentUrls.BIP72);
var request = url.GetPaymentRequest();
var payment = request.CreatePayment();
Transaction tx = new Transaction();
tx.Outputs.AddRange(request.Details.Outputs.Select(o => new TxOut(o.Amount, o.Script)));
var cashCow = tester.ExplorerNode;
tx = cashCow.FundRawTransaction(tx).Transaction;
tx = cashCow.SignRawTransaction(tx);
payment.Transactions.Add(tx);
payment.RefundTo.Add(new PaymentOutput(Money.Coins(1.0m), new Key().ScriptPubKey));
var ack = payment.SubmitPayment();
Assert.NotNull(ack);
Eventually(() =>
{
var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant);
Assert.Equal("paid", localInvoice.Status);
Assert.True(localInvoice.Refundable);
});
}
}
[Fact]
[Trait("Integration", "Integration")]
public async Task CanSetLightningServer()
@ -595,7 +546,7 @@ namespace BTCPayServer.Tests
var fetcher = new RateFetcher(factory);
Assert.True(RateRules.TryParse("X_X=kraken(X_BTC) * kraken(BTC_X)", out var rule));
foreach(var pair in new[] { "DOGE_USD", "DOGE_CAD", "DASH_CAD", "DASH_USD", "DASH_EUR" })
foreach (var pair in new[] { "DOGE_USD", "DOGE_CAD", "DASH_CAD", "DASH_USD", "DASH_EUR" })
{
var result = fetcher.FetchRate(CurrencyPair.Parse(pair), rule).GetAwaiter().GetResult();
Assert.NotNull(result.BidAsk);
@ -811,7 +762,7 @@ namespace BTCPayServer.Tests
output.ScriptPubKey = invoiceAddress.ScriptPubKey;
using (var cts = new CancellationTokenSource(10000))
using (var listener = tester.ExplorerClient.CreateNotificationSession())
using (var listener = tester.ExplorerClient.CreateWebsocketNotificationSession())
{
listener.ListenAllDerivationSchemes();
var replaced = tester.ExplorerNode.SignRawTransaction(tx);
@ -933,6 +884,17 @@ namespace BTCPayServer.Tests
var result = client.SendAsync(message).GetAwaiter().GetResult();
result.EnsureSuccessStatusCode();
/////////////////////
// Have error 403 with bad signature
client = new HttpClient();
HttpRequestMessage mess = new HttpRequestMessage(HttpMethod.Get, tester.PayTester.ServerUri.AbsoluteUri + "tokens");
mess.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
mess.Headers.Add("x-signature", "3045022100caa123193afc22ef93d9c6b358debce6897c09dd9869fe6fe029c9cb43623fac022000b90c65c50ba8bbbc6ebee8878abe5659e17b9f2e1b27d95eda4423da5608fe");
mess.Headers.Add("x-identity", "04b4d82095947262dd70f94c0a0e005ec3916e3f5f2181c176b8b22a52db22a8c436c4703f43a9e8884104854a11e1eb30df8fdf116e283807a1f1b8fe4c182b99");
mess.Method = HttpMethod.Get;
result = client.SendAsync(mess).GetAwaiter().GetResult();
Assert.Equal(System.Net.HttpStatusCode.Unauthorized, result.StatusCode);
//
}
}
@ -1322,6 +1284,19 @@ namespace BTCPayServer.Tests
result = parser.Parse(tpub);
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
parser = new DerivationSchemeParser(Network.RegTest);
var parsed = parser.Parse("xpub6DG1rMYXiQtCc6CfdLFD9CtxqhzzRh7j6Sq6EdE9abgYy3cfDRrniLLv2AdwqHL1exiLnnKR5XXcaoiiexf3Y9R6J6rxkJtqJHzNzMW9QMZ-[p2sh]");
Assert.Equal("tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[p2sh]", parsed.ToString());
// Let's make sure we can't generate segwit with dogecoin
parser = new DerivationSchemeParser(NBitcoin.Altcoins.Dogecoin.Instance.Regtest);
parsed = parser.Parse("xpub6DG1rMYXiQtCc6CfdLFD9CtxqhzzRh7j6Sq6EdE9abgYy3cfDRrniLLv2AdwqHL1exiLnnKR5XXcaoiiexf3Y9R6J6rxkJtqJHzNzMW9QMZ-[p2sh]");
Assert.Equal("tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[legacy]", parsed.ToString());
parser = new DerivationSchemeParser(NBitcoin.Altcoins.Dogecoin.Instance.Regtest);
parsed = parser.Parse("tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[p2sh]");
Assert.Equal("tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[legacy]", parsed.ToString());
}
[Fact]
@ -1447,12 +1422,19 @@ namespace BTCPayServer.Tests
var vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert.IsType<ViewResult>(apps.UpdatePointOfSale(appId).Result).Model);
vmpos.Title = "hello";
vmpos.Currency = "CAD";
vmpos.Template =
"apple:\n" +
" price: 5.0\n" +
" title: good apple\n" +
"orange:\n" +
" price: 10.0\n";
vmpos.ButtonText = "{0} Purchase";
vmpos.CustomButtonText = "Nicolas Sexy Hair";
vmpos.CustomTipText = "Wanna tip?";
vmpos.Template = @"
apple:
price: 5.0
title: good apple
orange:
price: 10.0
donation:
price: 1.02
custom: true
";
Assert.IsType<RedirectToActionResult>(apps.UpdatePointOfSale(appId, vmpos).Result);
vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert.IsType<ViewResult>(apps.UpdatePointOfSale(appId).Result).Model);
Assert.Equal("hello", vmpos.Title);
@ -1460,19 +1442,225 @@ namespace BTCPayServer.Tests
var publicApps = user.GetController<AppsPublicController>();
var vmview = Assert.IsType<ViewPointOfSaleViewModel>(Assert.IsType<ViewResult>(publicApps.ViewPointOfSale(appId).Result).Model);
Assert.Equal("hello", vmview.Title);
Assert.Equal(2, vmview.Items.Length);
Assert.Equal(3, vmview.Items.Length);
Assert.Equal("good apple", vmview.Items[0].Title);
Assert.Equal("orange", vmview.Items[1].Title);
Assert.Equal(10.0m, vmview.Items[1].Price.Value);
Assert.Equal("$5.00", vmview.Items[0].Price.Formatted);
Assert.IsType<RedirectResult>(publicApps.ViewPointOfSale(appId, 0, null, null, null, null, "orange").Result);
var invoice = user.BitPay.GetInvoices().First();
Assert.Equal(10.00m, invoice.Price);
Assert.Equal("CAD", invoice.Currency);
Assert.Equal("orange", invoice.ItemDesc);
Assert.Equal("{0} Purchase", vmview.ButtonText);
Assert.Equal("Nicolas Sexy Hair", vmview.CustomButtonText);
Assert.Equal("Wanna tip?", vmview.CustomTipText);
Assert.IsType<RedirectToActionResult>(publicApps.ViewPointOfSale(appId, 0, null, null, null, null, "orange").Result);
//
var invoices = user.BitPay.GetInvoices();
var orangeInvoice = invoices.First();
Assert.Equal(10.00m, orangeInvoice.Price);
Assert.Equal("CAD", orangeInvoice.Currency);
Assert.Equal("orange", orangeInvoice.ItemDesc);
// testing custom amount
var action = Assert.IsType<RedirectToActionResult>(publicApps.ViewPointOfSale(appId, 5, null, null, null, null, "donation").Result);
Assert.Equal(nameof(InvoiceController.Checkout), action.ActionName);
invoices = user.BitPay.GetInvoices();
var donationInvoice = invoices.Single(i => i.Price == 5m);
Assert.NotNull(donationInvoice);
Assert.Equal("CAD", donationInvoice.Currency);
Assert.Equal("donation", donationInvoice.ItemDesc);
}
}
[Fact]
[Trait("Fast", "Fast")]
public void PosDataParser_ParsesCorrectly()
{
var testCases =
new List<(string input, Dictionary<string, string> expectedOutput)>()
{
{ (null, new Dictionary<string, string>())},
{("", new Dictionary<string, string>())},
{("{}", new Dictionary<string, string>())},
{("non-json-content", new Dictionary<string, string>(){ {string.Empty, "non-json-content"}})},
{("[1,2,3]", new Dictionary<string, string>(){ {string.Empty, "[1,2,3]"}})},
{("{ \"key\": \"value\"}", new Dictionary<string, string>(){ {"key", "value"}})},
{("{ \"key\": true}", new Dictionary<string, string>(){ {"key", "True"}})},
{("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}",
new Dictionary<string, string>(){ {"key", "value"}, {"key2", "value,value2"}})},
{("{ invalidjson file here}", new Dictionary<string, string>(){ {String.Empty, "{ invalidjson file here}"}})}
};
testCases.ForEach(tuple =>
{
Assert.Equal(tuple.expectedOutput, InvoiceController.PosDataParser.ParsePosData(tuple.input));
});
}
[Fact]
[Trait("Integration", "Integration")]
public async Task PosDataParser_ParsesCorrectly_Slower()
{
using (var tester = ServerTester.Create())
{
tester.Start();
var user = tester.NewAccount();
user.GrantAccess();
user.RegisterDerivationScheme("BTC");
var controller = tester.PayTester.GetController<InvoiceController>(null);
var testCases =
new List<(string input, Dictionary<string, string> expectedOutput)>()
{
{ (null, new Dictionary<string, string>())},
{("", new Dictionary<string, string>())},
{("{}", new Dictionary<string, string>())},
{("non-json-content", new Dictionary<string, string>(){ {string.Empty, "non-json-content"}})},
{("[1,2,3]", new Dictionary<string, string>(){ {string.Empty, "[1,2,3]"}})},
{("{ \"key\": \"value\"}", new Dictionary<string, string>(){ {"key", "value"}})},
{("{ \"key\": true}", new Dictionary<string, string>(){ {"key", "True"}})},
{("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}",
new Dictionary<string, string>(){ {"key", "value"}, {"key2", "value,value2"}})},
{("{ invalidjson file here}", new Dictionary<string, string>(){ {String.Empty, "{ invalidjson file here}"}})}
};
var tasks = new List<Task>();
foreach (var valueTuple in testCases)
{
tasks.Add(user.BitPay.CreateInvoiceAsync(new Invoice(1, "BTC")
{
PosData = valueTuple.input
}).ContinueWith(async task =>
{
var result = await controller.Invoice(task.Result.Id);
var viewModel =
Assert.IsType<InvoiceDetailsModel>(
Assert.IsType<ViewResult>(result).Model);
Assert.Equal(valueTuple.expectedOutput, viewModel.PosData);
}));
}
await Task.WhenAll(tasks);
}
}
[Fact]
[Trait("Integration", "Integration")]
public void CanExportInvoicesJson()
{
using (var tester = ServerTester.Create())
{
tester.Start();
var user = tester.NewAccount();
user.GrantAccess();
user.RegisterDerivationScheme("BTC");
var invoice = user.BitPay.CreateInvoice(new Invoice()
{
Price = 500,
Currency = "USD",
PosData = "posData",
OrderId = "orderId",
ItemDesc = "Some \", description",
FullNotifications = true
}, Facade.Merchant);
// ensure 0 invoices exported because there are no payments yet
var jsonResult = user.GetController<InvoiceController>().Export("json").GetAwaiter().GetResult();
var result = Assert.IsType<ContentResult>(jsonResult);
Assert.Equal("application/json", result.ContentType);
Assert.Equal("[]", result.Content);
var cashCow = tester.ExplorerNode;
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
var firstPayment = invoice.CryptoInfo[0].TotalDue - Money.Satoshis(10);
cashCow.SendToAddress(invoiceAddress, firstPayment);
Eventually(() =>
{
var jsonResultPaid = user.GetController<InvoiceController>().Export("json").GetAwaiter().GetResult();
var paidresult = Assert.IsType<ContentResult>(jsonResultPaid);
Assert.Equal("application/json", paidresult.ContentType);
Assert.Contains("\"ItemDesc\": \"Some \\\", description\"", paidresult.Content);
Assert.Contains("\"FiatPrice\": 500.0", paidresult.Content);
Assert.Contains("\"ConversionRate\": 5000.0", paidresult.Content);
Assert.Contains("\"PaymentDue\": \"0.10020000 BTC\"", paidresult.Content);
Assert.Contains($"\"InvoiceId\": \"{invoice.Id}\",", paidresult.Content);
});
/*
[
{
"ReceivedDate": "2018-11-30T10:27:13Z",
"StoreId": "FKaSZrXLJ2tcLfCyeiYYfmZp1UM5nZ1LDecQqbwBRuHi",
"OrderId": "orderId",
"InvoiceId": "4XUkgPMaTBzwJGV9P84kPC",
"CreatedDate": "2018-11-30T10:27:06Z",
"ExpirationDate": "2018-11-30T10:42:06Z",
"MonitoringDate": "2018-11-30T11:42:06Z",
"PaymentId": "6e5755c3357b20fd66f5fc478778d81371eab341e7112ab66ed6122c0ec0d9e5-1",
"CryptoCode": "BTC",
"Destination": "mhhSEQuoM993o6vwnBeufJ4TaWov2ZUsPQ",
"PaymentType": "OnChain",
"PaymentDue": "0.10020000 BTC",
"PaymentPaid": "0.10009990 BTC",
"PaymentOverpaid": "0.00000000 BTC",
"ConversionRate": 5000.0,
"FiatPrice": 500.0,
"FiatCurrency": "USD",
"ItemCode": null,
"ItemDesc": "Some \", description",
"Status": "new"
}
]
*/
}
}
[Fact]
[Trait("Integration", "Integration")]
public void CanExportInvoicesCsv()
{
using (var tester = ServerTester.Create())
{
tester.Start();
var user = tester.NewAccount();
user.GrantAccess();
user.RegisterDerivationScheme("BTC");
var invoice = user.BitPay.CreateInvoice(new Invoice()
{
Price = 500,
Currency = "USD",
PosData = "posData",
OrderId = "orderId",
ItemDesc = "Some \", description",
FullNotifications = true
}, Facade.Merchant);
var cashCow = tester.ExplorerNode;
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
var firstPayment = invoice.CryptoInfo[0].TotalDue - Money.Satoshis(10);
cashCow.SendToAddress(invoiceAddress, firstPayment);
Eventually(() =>
{
var exportResultPaid = user.GetController<InvoiceController>().Export("csv").GetAwaiter().GetResult();
var paidresult = Assert.IsType<ContentResult>(exportResultPaid);
Assert.Equal("application/csv", paidresult.ContentType);
Assert.Contains($",\"orderId\",\"{invoice.Id}\",", paidresult.Content);
Assert.Contains($",\"OnChain\",\"0.10020000 BTC\",\"0.10009990 BTC\",\"0.00000000 BTC\",\"5000.0\",\"500.0\"", paidresult.Content);
Assert.Contains($",\"USD\",\"\",\"Some ``, description\",\"new\"", paidresult.Content);
});
/*
ReceivedDate,StoreId,OrderId,InvoiceId,CreatedDate,ExpirationDate,MonitoringDate,PaymentId,CryptoCode,Destination,PaymentType,PaymentDue,PaymentPaid,PaymentOverpaid,ConversionRate,FiatPrice,FiatCurrency,ItemCode,ItemDesc,Status
"11/30/2018 10:28:42 AM","7AagXzWdWhLLR3Zar25YLiw2uHAJDzVT4oXGKC9bBCis","orderId","GxtJsWbgxxAXXoCurqyeK6","11/30/2018 10:28:40 AM","11/30/2018 10:43:40 AM","11/30/2018 11:43:40 AM","ec0341537f565d213bc64caa352fbbf9e0deb31cab1f91bccf89db0dd1604457-0","BTC","mqWghCp9RVw8fNgQMLjawyKStxpGfWBk1L","OnChain","0.10020000 BTC","0.10009990 BTC","0.00000000 BTC","5000.0","500.0","USD","","Some ``, description","new"
*/
}
}
[Fact]
[Trait("Integration", "Integration")]
public void CanCreateAndDeleteApps()
@ -1574,7 +1762,7 @@ namespace BTCPayServer.Tests
Assert.True(IsMapped(invoice, ctx));
cashCow.SendToAddress(invoiceAddress, firstPayment);
var invoiceEntity = repo.GetInvoice(null, invoice.Id, true).GetAwaiter().GetResult();
var invoiceEntity = repo.GetInvoice(invoice.Id, true).GetAwaiter().GetResult();
Assert.Single(invoiceEntity.HistoricalAddresses);
Assert.Null(invoiceEntity.HistoricalAddresses[0].UnAssigned);
@ -1592,7 +1780,7 @@ namespace BTCPayServer.Tests
Assert.True(IsMapped(invoice, ctx));
Assert.True(IsMapped(localInvoice, ctx));
invoiceEntity = repo.GetInvoice(null, invoice.Id, true).GetAwaiter().GetResult();
invoiceEntity = repo.GetInvoice(invoice.Id, true).GetAwaiter().GetResult();
var historical1 = invoiceEntity.HistoricalAddresses.FirstOrDefault(h => h.GetAddress() == invoice.BitcoinAddress);
Assert.NotNull(historical1.UnAssigned);
var historical2 = invoiceEntity.HistoricalAddresses.FirstOrDefault(h => h.GetAddress() == localInvoice.BitcoinAddress);
@ -1784,12 +1972,12 @@ namespace BTCPayServer.Tests
var user = tester.NewAccount();
user.GrantAccess();
user.RegisterDerivationScheme("BTC");
var serverController = user.GetController<ServerController>();
var vm = Assert.IsType<LogsViewModel>(Assert.IsType<ViewResult>(await serverController.LogsView()).Model);
}
}
}
[Fact]
[Trait("Fast", "Fast")]
public void CheckRatesProvider()

View File

@ -0,0 +1,109 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using NBitcoin.DataEncoders;
using Newtonsoft.Json.Linq;
using Xunit;
using System.IO;
namespace BTCPayServer.Tests
{
/// <summary>
/// This class hold easy to run utilities for dev time
/// </summary>
public class UtilitiesTests
{
/// <summary>
/// Download transifex transactions and put them in BTCPayServer\wwwroot\locales
/// </summary>
[Trait("Utilities", "Utilities")]
[Fact]
public async Task PullTransifexTranslations()
{
// 1. Generate an API Token on https://www.transifex.com/user/settings/api/
// 2. Run "dotnet user-secrets set TransifexAPIToken <youapitoken>"
var client = new TransifexClient(GetTransifexAPIToken());
var json = await client.GetTransifexAsync("https://api.transifex.com/organizations/btcpayserver/projects/btcpayserver/resources/enjson/");
var langs = new[] { "en" }.Concat(((JObject)json["stats"]).Properties().Select(n => n.Name)).ToArray();
var langsDir = Path.Combine(Services.LanguageService.TryGetSolutionDirectoryInfo().FullName, "BTCPayServer", "wwwroot", "locales");
JObject sourceLang = null;
Task.WaitAll(langs.Select(async l =>
{
bool isSourceLang = l == "en";
if (l == "no")
return;
var j = await client.GetTransifexAsync($"https://www.transifex.com/api/2/project/btcpayserver/resource/enjson/translation/{l}/");
if(!isSourceLang)
{
while (sourceLang == null)
await Task.Delay(10);
}
var content = j["content"].Value<string>();
if (l == "ne_NP")
l = "np_NP";
if (l == "zh_CN")
l = "zh-SP";
if (l == "kk")
l = "kk-KZ";
var langCode = l.Replace("_", "-");
var langFile = Path.Combine(langsDir, langCode + ".json");
var jobj = JObject.Parse(content);
jobj["code"] = langCode;
if ((string)jobj["currentLanguage"] == "English" && !isSourceLang)
return; // Not translated
jobj.AddFirst(new JProperty("NOTICE_WARN", "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/"));
if (isSourceLang)
{
sourceLang = jobj;
}
else
{
if(jobj["InvoiceExpired_Body_3"].Value<string>() == sourceLang["InvoiceExpired_Body_3"].Value<string>())
{
jobj["InvoiceExpired_Body_3"] = string.Empty;
}
}
content = jobj.ToString(Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(Path.Combine(langsDir, langFile), content);
}).ToArray());
}
private static string GetTransifexAPIToken()
{
var builder = new ConfigurationBuilder();
builder.AddUserSecrets("AB0AC1DD-9D26-485B-9416-56A33F268117");
var config = builder.Build();
var token = config["TransifexAPIToken"];
Assert.False(token == null, "TransifexAPIToken is not set.\n 1.Generate an API Token on https://www.transifex.com/user/settings/api/ \n 2.Run \"dotnet user-secrets set TransifexAPIToken <youapitoken>\"");
return token;
}
}
public class TransifexClient
{
public TransifexClient(string apiToken)
{
Client = new HttpClient();
APIToken = apiToken;
}
public HttpClient Client { get; }
public string APIToken { get; }
public async Task<JObject> GetTransifexAsync(string uri)
{
var message = new HttpRequestMessage(HttpMethod.Get, uri);
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Encoders.Base64.EncodeData(Encoding.ASCII.GetBytes($"api:{APIToken}")));
message.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var response = await Client.SendAsync(message);
return await response.Content.ReadAsAsync<JObject>();
}
}
}

View File

@ -1 +1,2 @@
docker exec -ti btcpayservertests_bitcoind_1 bitcoin-cli -datadir="/data" $args
$bitcoind_container_id=$(docker ps -q --filter label=com.docker.compose.project=btcpayservertests --filter label=com.docker.compose.service=bitcoind)
docker exec -ti $bitcoind_container_id bitcoin-cli -datadir="/data" $args

View File

@ -1,3 +1,4 @@
#!/bin/bash
docker exec -ti btcpayservertests_bitcoind_1 bitcoin-cli -datadir="/data" "$@"
bitcoind_container_id="$(docker ps -q --filter label=com.docker.compose.project=btcpayservertests --filter label=com.docker.compose.service=bitcoind)"
docker exec -ti "$bitcoind_container_id" bitcoin-cli -datadir="/data" "$@"

View File

@ -69,7 +69,7 @@ services:
nbxplorer:
image: nicolasdorier/nbxplorer:1.1.0.12
image: nicolasdorier/nbxplorer:2.0.0.2
restart: unless-stopped
ports:
- "32838:32838"

View File

@ -1 +1,2 @@
docker exec -ti btcpayservertests_customer_lightningd_1 lightning-cli $args
$customer_lightning_container_id=$(docker ps -q --filter label=com.docker.compose.project=btcpayservertests --filter label=com.docker.compose.service=customer_lightningd)
docker exec -ti $customer_lightning_container_id lightning-cli $args

View File

@ -1,3 +1,4 @@
#!/bin/bash
docker exec -ti btcpayservertests_customer_lightningd_1 lightning-cli "$@"
customer_lightning_container_id="$(docker ps -q --filter label=com.docker.compose.project=btcpayservertests --filter label=com.docker.compose.service=customer_lightningd)"
docker exec -ti $customer_lightning_container_id lightning-cli "$@"

View File

@ -1 +1,2 @@
docker exec -ti btcpayservertests_litecoind_1 litecoin-cli -datadir="/data" $args
$litecoind_container_id=$(docker ps -q --filter label=com.docker.compose.project=btcpayservertests --filter label=com.docker.compose.service=litecoind)
docker exec -ti $litecoind_container_id litecoin-cli -datadir="/data" $args

View File

@ -1,3 +1,4 @@
#!/bin/bash
docker exec -ti btcpayservertests_litecoind_1 litecoin-cli -datadir="/data" "$@"
litecoind_container_id="$(docker ps -q --filter label=com.docker.compose.project=btcpayservertests --filter label=com.docker.compose.service=litecoind)"
docker exec -ti "$litecoind_container_id" litecoin-cli -datadir="/data" "$@"

View File

@ -1 +1,2 @@
docker exec -ti btcpayservertests_merchant_lightningd_1 lightning-cli $args
$merchant_lightning_container_id=$(docker ps -q --filter label=com.docker.compose.project=btcpayservertests --filter label=com.docker.compose.service=merchant_lightningd)
docker exec -ti $merchant_lightning_container_id lightning-cli $args

View File

@ -1,3 +1,4 @@
#!/bin/bash
docker exec -ti btcpayservertests_merchant_lightningd_1 lightning-cli "$@"
merchant_lightning_container_id="$(docker ps -q --filter label=com.docker.compose.project=btcpayservertests --filter label=com.docker.compose.service=merchant_lightningd)"
docker exec -ti $merchant_lightning_container_id lightning-cli "$@"

View File

@ -242,6 +242,8 @@ namespace BTCPayServer.Authentication
using (var ctx = _Factory.CreateContext())
{
var token = await ctx.PairedSINData.FindAsync(tokenId);
if (token == null)
return null;
return CreateTokenEntity(token);
}
}

View File

@ -24,7 +24,7 @@ namespace BTCPayServer
DefaultRateRules = new[]
{
"MONA_X = MONA_BTC * BTC_X",
"MONA_BTC = zaif(MONA_BTC)"
"MONA_BTC = bittrex(MONA_BTC)"
},
CryptoImagePath = "imlegacy/monacoin.png",
LightningImagePath = "imlegacy/mona-lightning.svg",

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Version>1.0.3.8</Version>
<Version>1.0.3.28</Version>
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
</PropertyGroup>
<PropertyGroup>
@ -33,7 +33,7 @@
<EmbeddedResource Include="Currencies.txt" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.0.1" />
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.0.3" />
<PackageReference Include="BuildBundlerMinifier" Version="2.7.385" />
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.5.3" />
<PackageReference Include="Hangfire" Version="1.6.20" />
@ -46,10 +46,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="NBitcoin" Version="4.1.1.68" />
<PackageReference Include="NBitcoin" Version="4.1.1.73" />
<PackageReference Include="NBitpayClient" Version="1.0.0.30" />
<PackageReference Include="DBreeze" Version="1.92.0" />
<PackageReference Include="NBXplorer.Client" Version="1.0.3.10" />
<PackageReference Include="NBXplorer.Client" Version="2.0.0.1" />
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.2" />
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" />
<PackageReference Include="NicolasDorier.RateLimits" Version="1.0.0.3" />
@ -68,7 +68,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.5" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.6" />
<PackageReference Include="YamlDotNet" Version="5.2.1" />
</ItemGroup>
@ -133,7 +133,7 @@
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Server\LndRestServices.cshtml">
<Content Update="Views\Home\BitpayTranslator.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Server\SSHService.cshtml">
@ -151,7 +151,7 @@
<Content Update="Views\Public\PayButtonHandle.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Server\LndGrpcServices.cshtml">
<Content Update="Views\Server\LndServices.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Server\Maintenance.cshtml">

View File

@ -141,6 +141,18 @@ namespace BTCPayServer.Configuration
Logs.Configuration.LogInformation("Supported chains: " + String.Join(',', supportedChains.ToArray()));
var services = conf.GetOrDefault<string>("externalservices", null);
if(services != null)
{
foreach(var service in services.Split(new[] { ';', ',' })
.Select(p => p.Split(':'))
.Where(p => p.Length == 2)
.Select(p => (Name: p[0], Link: p[1])))
{
ExternalServices.AddOrReplace(service.Name, service.Link);
}
}
PostgresConnectionString = conf.GetOrDefault<string>("postgres", null);
MySQLConnectionString = conf.GetOrDefault<string>("mysql", null);
BundleJsCss = conf.GetOrDefault<bool>("bundlejscss", true);
@ -248,6 +260,8 @@ namespace BTCPayServer.Configuration
public string RootPath { get; set; }
public Dictionary<string, LightningConnectionString> InternalLightningByCryptoCode { get; set; } = new Dictionary<string, LightningConnectionString>();
public Dictionary<string, string> ExternalServices { get; set; } = new Dictionary<string, string>();
public ExternalServices ExternalServicesByCryptoCode { get; set; } = new ExternalServices();
public BTCPayNetworkProvider NetworkProvider { get; set; }

View File

@ -33,6 +33,7 @@ namespace BTCPayServer.Configuration
app.Option("--postgres", $"Connection string to a PostgreSQL database (default: SQLite)", CommandOptionType.SingleValue);
app.Option("--mysql", $"Connection string to a MySQL database (default: SQLite)", CommandOptionType.SingleValue);
app.Option("--externalurl", $"The expected external URL of this service, to use if BTCPay is behind a reverse proxy (default: empty, use the incoming HTTP request to figure out)", CommandOptionType.SingleValue);
app.Option("--externalservices", $"Links added to external services inside Server Settings / Services under the format service1:path2;service2:path2.(default: empty)", CommandOptionType.SingleValue);
app.Option("--bundlejscss", $"Bundle JavaScript and CSS files for better performance (default: true)", CommandOptionType.SingleValue);
app.Option("--rootpath", "The root path in the URL to access BTCPay (default: /)", CommandOptionType.SingleValue);
app.Option("--sshconnection", "SSH server to manage BTCPay under the form user@server:port (default: root@externalhost or empty)", CommandOptionType.SingleValue);

View File

@ -15,28 +15,58 @@ namespace BTCPayServer.Controllers
{
public PointOfSaleSettings()
{
Title = "My awesome Point of Sale";
Title = "Tea shop";
Currency = "USD";
Template =
"tea:\n" +
" price: 0.02\n" +
" title: Green Tea # title is optional, defaults to the keys\n\n" +
"coffee:\n" +
" price: 1\n\n" +
"bamba:\n" +
" price: 3\n\n" +
"beer:\n" +
" price: 7\n\n" +
"hat:\n" +
" price: 15\n\n" +
"tshirt:\n" +
" price: 25";
"green tea:\n" +
" price: 1\n" +
" title: Green Tea\n" +
" description: Lovely, fresh and tender, Meng Ding Gan Lu ('sweet dew') is grown in the lush Meng Ding Mountains of the southwestern province of Sichuan where it has been cultivated for over a thousand years.\n" +
" image: https://cdn.pixabay.com/photo/2015/03/26/11/03/green-tea-692339__480.jpg\n\n" +
"black tea:\n" +
" price: 1\n" +
" title: Black Tea\n" +
" description: Tian Jian Tian Jian means 'heavenly tippy tea' in Chinese, and it describes the finest grade of dark tea. Our Tian Jian dark tea is from Hunan province which is famous for making some of the best dark teas available.\n" +
" image: https://cdn.pixabay.com/photo/2016/11/29/13/04/beverage-1869716__480.jpg\n\n" +
"rooibos:\n" +
" price: 1.2\n" +
" title: Rooibos\n" +
" description: Rooibos is a dramatic red tea made from a South African herb that contains polyphenols and flavonoids. Often called 'African redbush tea', Rooibos herbal tea delights the senses and delivers potential health benefits with each caffeine-free sip.\n" +
" image: https://cdn.pixabay.com/photo/2017/01/08/08/14/water-1962388__480.jpg\n\n" +
"pu erh:\n" +
" price: 2\n" +
" title: Pu Erh\n" +
" description: This loose pur-erh tea is produced in Yunnan Province, China. The process in a relatively high humidity environment has mellowed the elemental character of the tea when compared to young Pu-erh.\n" +
" image: https://cdn.pixabay.com/photo/2018/07/21/16/56/tea-cup-3552917__480.jpg\n\n" +
"herbal tea:\n" +
" price: 1.8\n" +
" title: Herbal Tea\n" +
" description: Chamomile tea is made from the flower heads of the chamomile plant. The medicinal use of chamomile dates back to the ancient Egyptians, Romans and Greeks. Pay us what you want!\n" +
" image: https://cdn.pixabay.com/photo/2015/07/02/20/57/chamomile-829538__480.jpg\n" +
" custom: true\n\n" +
"fruit tea:\n" +
" price: 1.5\n" +
" title: Fruit Tea\n" +
" description: The Tibetan Himalayas, the land is majestic and beautiful—a spiritual place where, despite the perilous environment, many journey seeking enlightenment. Pay us what you want!\n" +
" image: https://cdn.pixabay.com/photo/2016/09/16/11/24/darts-1673812__480.jpg\n" +
" custom: true";
EnableShoppingCart = false;
ShowCustomAmount = true;
}
public string Title { get; set; }
public string Currency { get; set; }
public string Template { get; set; }
public bool EnableShoppingCart { get; set; }
public bool ShowCustomAmount { get; set; }
public const string BUTTON_TEXT_DEF = "Buy for {0}";
public string ButtonText { get; set; } = BUTTON_TEXT_DEF;
public const string CUSTOM_BUTTON_TEXT_DEF = "Pay";
public string CustomButtonText { get; set; } = CUSTOM_BUTTON_TEXT_DEF;
public const string CUSTOM_TIP_TEXT_DEF = "Do you want to leave a tip?";
public string CustomTipText { get; set; } = CUSTOM_TIP_TEXT_DEF;
public string CustomCSSLink { get; set; }
}
[HttpGet]
@ -50,9 +80,14 @@ namespace BTCPayServer.Controllers
var vm = new UpdatePointOfSaleViewModel()
{
Title = settings.Title,
EnableShoppingCart = settings.EnableShoppingCart,
ShowCustomAmount = settings.ShowCustomAmount,
Currency = settings.Currency,
Template = settings.Template
Template = settings.Template,
ButtonText = settings.ButtonText ?? PointOfSaleSettings.BUTTON_TEXT_DEF,
CustomButtonText = settings.CustomButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF,
CustomTipText = settings.CustomTipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
CustomCSSLink = settings.CustomCSSLink
};
if (HttpContext?.Request != null)
{
@ -115,9 +150,14 @@ namespace BTCPayServer.Controllers
app.SetSettings(new PointOfSaleSettings()
{
Title = vm.Title,
EnableShoppingCart = vm.EnableShoppingCart,
ShowCustomAmount = vm.ShowCustomAmount,
Currency = vm.Currency.ToUpperInvariant(),
Template = vm.Template
Template = vm.Template,
ButtonText = vm.ButtonText,
CustomButtonText = vm.CustomButtonText,
CustomTipText = vm.CustomTipText,
CustomCSSLink = vm.CustomCSSLink
});
await UpdateAppSettings(app);
StatusMessage = "App updated";

View File

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Filters;
using BTCPayServer.Models.AppViewModels;
using BTCPayServer.Security;
using BTCPayServer.Services.Apps;
@ -31,6 +33,7 @@ namespace BTCPayServer.Controllers
[HttpGet]
[Route("/apps/{appId}/pos")]
[XFrameOptionsAttribute(null)]
public async Task<IActionResult> ViewPointOfSale(string appId)
{
var app = await _AppsHelper.GetApp(appId, AppType.PointOfSale);
@ -40,12 +43,28 @@ namespace BTCPayServer.Controllers
var currency = _AppsHelper.GetCurrencyData(settings.Currency, false);
double step = currency == null ? 1 : Math.Pow(10, -(currency.Divisibility));
var numberFormatInfo = _AppsHelper.Currencies.GetNumberFormatInfo(currency.Code) ?? _AppsHelper.Currencies.GetNumberFormatInfo("USD");
return View(new ViewPointOfSaleViewModel()
{
Title = settings.Title,
Step = step.ToString(CultureInfo.InvariantCulture),
EnableShoppingCart = settings.EnableShoppingCart,
ShowCustomAmount = settings.ShowCustomAmount,
Items = _AppsHelper.Parse(settings.Template, settings.Currency)
CurrencyCode = currency.Code,
CurrencySymbol = currency.Symbol,
CurrencyInfo = new ViewPointOfSaleViewModel.CurrencyInfoData()
{
CurrencySymbol = string.IsNullOrEmpty(currency.Symbol) ? currency.Code : currency.Symbol,
Divisibility = currency.Divisibility,
DecimalSeparator = numberFormatInfo.CurrencyDecimalSeparator,
ThousandSeparator = numberFormatInfo.NumberGroupSeparator,
Prefixed = new[] { 0, 2 }.Contains(numberFormatInfo.CurrencyPositivePattern)
},
Items = _AppsHelper.Parse(settings.Template, settings.Currency),
ButtonText = settings.ButtonText,
CustomButtonText = settings.CustomButtonText,
CustomTipText = settings.CustomTipText,
CustomCSSLink = settings.CustomCSSLink
});
}
@ -69,7 +88,7 @@ namespace BTCPayServer.Controllers
if (app == null)
return NotFound();
var settings = app.GetSettings<PointOfSaleSettings>();
if (string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount)
if (string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount && !settings.EnableShoppingCart)
{
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
}
@ -83,10 +102,12 @@ namespace BTCPayServer.Controllers
return NotFound();
title = choice.Title;
price = choice.Price.Value;
if (amount > price)
price = amount;
}
else
{
if (!settings.ShowCustomAmount)
if (!settings.ShowCustomAmount && !settings.EnableShoppingCart)
return NotFound();
price = amount;
title = settings.Title;
@ -95,6 +116,7 @@ namespace BTCPayServer.Controllers
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
var invoice = await _InvoiceController.CreateInvoiceCore(new NBitpayClient.Invoice()
{
ItemCode = choiceKey ?? string.Empty,
ItemDesc = title,
Currency = settings.Currency,
Price = price,
@ -104,7 +126,7 @@ namespace BTCPayServer.Controllers
RedirectURL = redirectUrl,
FullNotifications = true
}, store, HttpContext.Request.GetAbsoluteRoot());
return Redirect(invoice.Data.Url);
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
}
}
@ -113,7 +135,7 @@ namespace BTCPayServer.Controllers
{
ApplicationDbContextFactory _ContextFactory;
CurrencyNameTable _Currencies;
public CurrencyNameTable Currencies => _Currencies;
public AppsHelper(ApplicationDbContextFactory contextFactory, CurrencyNameTable currencies)
{
_ContextFactory = contextFactory;
@ -140,38 +162,61 @@ namespace BTCPayServer.Controllers
}
}
public ViewPointOfSaleViewModel.Item[] Parse(string template, string currency)
{
if (string.IsNullOrWhiteSpace(template))
return Array.Empty<ViewPointOfSaleViewModel.Item>();
var input = new StringReader(template);
YamlStream stream = new YamlStream();
stream.Load(input);
var root = (YamlMappingNode)stream.Documents[0].RootNode;
return root
.Children
.Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlMappingNode })
.Select(kv => new PosHolder { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlMappingNode })
.Where(kv => kv.Value != null)
.Select(c => new ViewPointOfSaleViewModel.Item()
{
Description = c.GetDetailString("description"),
Id = c.Key,
Title = c.Value.Children
.Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode })
.Where(kv => kv.Value != null)
.Where(cc => cc.Key == "title")
.FirstOrDefault()?.Value?.Value ?? c.Key,
Price = c.Value.Children
.Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode })
.Where(kv => kv.Value != null)
.Where(cc => cc.Key == "price")
Image = c.GetDetailString("image"),
Title = c.GetDetailString("title") ?? c.Key,
Price = c.GetDetail("price")
.Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice()
{
Value = decimal.Parse(cc.Value.Value, CultureInfo.InvariantCulture),
Formatted = FormatCurrency(cc.Value.Value, currency)
})
.Single()
}).Single(),
Custom = c.GetDetailString("custom") == "true"
})
.ToArray();
}
private class PosHolder
{
public string Key { get; set; }
public YamlMappingNode Value { get; set; }
public IEnumerable<PosScalar> GetDetail(string field)
{
var res = Value.Children
.Where(kv => kv.Value != null)
.Select(kv => new PosScalar { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode })
.Where(cc => cc.Key == field);
return res;
}
public string GetDetailString(string field)
{
return GetDetail(field).FirstOrDefault()?.Value?.Value;
}
}
private class PosScalar
{
public string Key { get; set; }
public YamlScalarNode Value { get; set; }
}
public string FormatCurrency(string price, string currency)
{
return decimal.Parse(price, CultureInfo.InvariantCulture).ToString("C", _Currencies.GetCurrencyProvider(currency));

View File

@ -6,16 +6,85 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using BTCPayServer.Models;
using NBitcoin.DataEncoders;
using NBitcoin.Payment;
using System.Net.Http;
using Newtonsoft.Json.Linq;
using NBitcoin;
using Newtonsoft.Json;
namespace BTCPayServer.Controllers
{
public class HomeController : Controller
{
public IHttpClientFactory HttpClientFactory { get; }
public HomeController(IHttpClientFactory httpClientFactory)
{
HttpClientFactory = httpClientFactory;
}
public IActionResult Index()
{
return View("Home");
}
[Route("translate")]
public IActionResult BitpayTranslator()
{
return View(new BitpayTranslatorViewModel());
}
[HttpPost]
[Route("translate")]
public async Task<IActionResult> BitpayTranslator(BitpayTranslatorViewModel vm)
{
if (!ModelState.IsValid)
return View(vm);
vm.BitpayLink = vm.BitpayLink ?? string.Empty;
vm.BitpayLink = vm.BitpayLink.Trim();
if (!vm.BitpayLink.StartsWith("bitcoin:", StringComparison.OrdinalIgnoreCase))
{
var invoiceId = vm.BitpayLink.Substring(vm.BitpayLink.LastIndexOf("=", StringComparison.OrdinalIgnoreCase) + 1);
vm.BitpayLink = $"bitcoin:?r=https://bitpay.com/i/{invoiceId}";
}
try
{
BitcoinUrlBuilder urlBuilder = new BitcoinUrlBuilder(vm.BitpayLink);
#pragma warning disable CS0618 // Type or member is obsolete
if (!urlBuilder.PaymentRequestUrl.DnsSafeHost.EndsWith("bitpay.com", StringComparison.OrdinalIgnoreCase))
{
throw new Exception("This tool only work with bitpay");
}
var client = HttpClientFactory.CreateClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, urlBuilder.PaymentRequestUrl);
#pragma warning restore CS0618 // Type or member is obsolete
request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/payment-request"));
var result = await client.SendAsync(request);
// {"network":"main","currency":"BTC","requiredFeeRate":29.834,"outputs":[{"amount":255900,"address":"1PgPo5d4swD6pKfCgoXtoW61zqTfX9H7tj"}],"time":"2018-12-03T14:39:47.162Z","expires":"2018-12-03T14:54:47.162Z","memo":"Payment request for BitPay invoice HHfG8cprRMzZG6MErCqbjv for merchant VULTR Holdings LLC","paymentUrl":"https://bitpay.com/i/HHfG8cprRMzZG6MErCqbjv","paymentId":"HHfG8cprRMzZG6MErCqbjv"}
var str = await result.Content.ReadAsStringAsync();
try
{
var jobj = JObject.Parse(str);
vm.Address = ((JArray)jobj["outputs"])[0]["address"].Value<string>();
var amount = Money.Satoshis(((JArray)jobj["outputs"])[0]["amount"].Value<long>());
vm.Amount = amount.ToString();
vm.BitcoinUri = $"bitcoin:{vm.Address}?amount={amount.ToString()}";
}
catch (JsonReaderException)
{
ModelState.AddModelError(nameof(vm.BitpayLink), $"Invalid or expired bitpay invoice");
return View(vm);
}
}
catch (Exception ex)
{
ModelState.AddModelError(nameof(vm.BitpayLink), $"Error while requesting {ex.Message}");
return View(vm);
}
return View(vm);
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";

View File

@ -40,16 +40,18 @@ namespace BTCPayServer.Controllers
[HttpGet]
[Route("invoices/{id}")]
[AllowAnonymous]
public async Task<DataWrapper<InvoiceResponse>> GetInvoice(string id, string token)
public async Task<DataWrapper<InvoiceResponse>> GetInvoice(string id)
{
var invoice = await _InvoiceRepository.GetInvoice(null, id);
var invoice = (await _InvoiceRepository.GetInvoices(new InvoiceQuery()
{
InvoiceId = id,
StoreId = new[] { HttpContext.GetStoreData().Id }
})).FirstOrDefault();
if (invoice == null)
throw new BitpayHttpException(404, "Object not found");
var resp = invoice.EntityToDTO(_NetworkProvider);
return new DataWrapper<InvoiceResponse>(resp);
}
[HttpGet]
[Route("invoices")]
public async Task<DataWrapper<InvoiceResponse[]>> GetInvoices(

View File

@ -1,117 +0,0 @@
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Filters;
using BTCPayServer.Logging;
using BTCPayServer.Payments;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBitcoin.Payment;
namespace BTCPayServer.Controllers
{
public partial class InvoiceController
{
[HttpGet]
[Route("i/{invoiceId}/{cryptoCode?}")]
[AcceptMediaTypeConstraint("application/bitcoin-paymentrequest")]
public async Task<IActionResult> GetInvoiceRequest(string invoiceId, string cryptoCode = null)
{
if (cryptoCode == null)
cryptoCode = "BTC";
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
var network = _NetworkProvider.GetNetwork(cryptoCode);
var paymentMethodId = new PaymentMethodId(cryptoCode, Payments.PaymentTypes.BTCLike);
if (invoice == null || invoice.IsExpired() || network == null || !invoice.Support(paymentMethodId))
return NotFound();
var dto = invoice.EntityToDTO(_NetworkProvider);
var paymentMethod = dto.CryptoInfo.First(c => c.GetpaymentMethodId() == paymentMethodId);
PaymentRequest request = new PaymentRequest
{
DetailsVersion = 1
};
request.Details.Expires = invoice.ExpirationTime;
request.Details.Memo = invoice.ProductInformation.ItemDesc;
request.Details.Network = network.NBitcoinNetwork;
request.Details.Outputs.Add(new PaymentOutput() { Amount = paymentMethod.Due, Script = BitcoinAddress.Create(paymentMethod.Address, network.NBitcoinNetwork).ScriptPubKey });
request.Details.MerchantData = Encoding.UTF8.GetBytes(invoice.Id);
request.Details.Time = DateTimeOffset.UtcNow;
request.Details.PaymentUrl = new Uri(invoice.ServerUrl.WithTrailingSlash() + ($"i/{invoice.Id}"), UriKind.Absolute);
var store = await _StoreRepository.FindStore(invoice.StoreId);
if (store == null)
throw new BitpayHttpException(401, "Unknown store");
if (store.StoreCertificate != null)
{
try
{
request.Sign(store.StoreCertificate, PKIType.X509SHA256);
}
catch (Exception ex)
{
Logs.PayServer.LogWarning(ex, "Error while signing payment request");
}
}
return new PaymentRequestActionResult(request);
}
[HttpPost]
[Route("i/{invoiceId}", Order = 99)]
[Route("i/{invoiceId}/{cryptoCode}", Order = 99)]
[MediaTypeConstraint("application/bitcoin-payment")]
public async Task<IActionResult> PostPayment(string invoiceId, string cryptoCode = null)
{
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
if (cryptoCode == null)
cryptoCode = "BTC";
var network = _NetworkProvider.GetNetwork(cryptoCode);
if (network == null || invoice == null || invoice.IsExpired() || !invoice.Support(new PaymentMethodId(cryptoCode, Payments.PaymentTypes.BTCLike)))
return NotFound();
var wallet = _WalletProvider.GetWallet(network);
if (wallet == null)
return NotFound();
var payment = PaymentMessage.Load(Request.Body, network.NBitcoinNetwork);
var unused = wallet.BroadcastTransactionsAsync(payment.Transactions);
await _InvoiceRepository.AddRefundsAsync(invoiceId, payment.RefundTo.Select(p => new TxOut(p.Amount, p.Script)).ToArray(), network.NBitcoinNetwork);
return new PaymentAckActionResult(payment.CreateACK(invoiceId + " is currently processing, thanks for your purchase..."));
}
}
public class PaymentRequestActionResult : IActionResult
{
PaymentRequest req;
public PaymentRequestActionResult(PaymentRequest req)
{
this.req = req;
}
public Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.Headers["Content-Transfer-Encoding"] = "binary";
context.HttpContext.Response.ContentType = "application/bitcoin-paymentrequest";
req.WriteTo(context.HttpContext.Response.Body);
return Task.CompletedTask;
}
}
public class PaymentAckActionResult : IActionResult
{
PaymentACK req;
public PaymentAckActionResult(PaymentACK req)
{
this.req = req;
}
public Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.Headers["Content-Transfer-Encoding"] = "binary";
context.HttpContext.Response.ContentType = "application/bitcoin-paymentack";
req.WriteTo(context.HttpContext.Response.Body);
return Task.CompletedTask;
}
}
}

View File

@ -2,25 +2,28 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Mime;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.Filters;
using BTCPayServer.Models;
using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Changelly;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Security;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Invoices.Export;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using NBitcoin;
using NBitpayClient;
using NBXplorer;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Controllers
{
@ -28,11 +31,13 @@ namespace BTCPayServer.Controllers
{
[HttpGet]
[Route("invoices/{invoiceId}")]
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
public async Task<IActionResult> Invoice(string invoiceId)
{
var invoice = (await _InvoiceRepository.GetInvoices(new InvoiceQuery()
{
InvoiceId = invoiceId,
UserId = GetUserId(),
IncludeAddresses = true,
IncludeEvents = true
})).FirstOrDefault();
@ -41,13 +46,12 @@ namespace BTCPayServer.Controllers
var dto = invoice.EntityToDTO(_NetworkProvider);
var store = await _StoreRepository.FindStore(invoice.StoreId);
InvoiceDetailsModel model = new InvoiceDetailsModel()
{
StoreName = store.StoreName,
StoreLink = Url.Action(nameof(StoresController.UpdateStore), "Stores", new { storeId = store.Id }),
Id = invoice.Id,
Status = invoice.Status,
State = invoice.GetInvoiceState().ToString(),
TransactionSpeed = invoice.SpeedPolicy == SpeedPolicy.HighSpeed ? "high" :
invoice.SpeedPolicy == SpeedPolicy.MediumSpeed ? "medium" :
invoice.SpeedPolicy == SpeedPolicy.LowMediumSpeed ? "low-medium" :
@ -58,13 +62,14 @@ namespace BTCPayServer.Controllers
MonitoringDate = invoice.MonitoringExpiration,
OrderId = invoice.OrderId,
BuyerInformation = invoice.BuyerInformation,
Fiat = _CurrencyNameTable.DisplayFormatCurrency((decimal)dto.Price, dto.Currency),
Fiat = _CurrencyNameTable.DisplayFormatCurrency(dto.Price, dto.Currency),
NotificationEmail = invoice.NotificationEmail,
NotificationUrl = invoice.NotificationURL,
RedirectUrl = invoice.RedirectURL,
ProductInformation = invoice.ProductInformation,
StatusException = invoice.ExceptionStatus,
Events = invoice.Events
Events = invoice.Events,
PosData = PosDataParser.ParsePosData(dto.PosData)
};
foreach (var data in invoice.GetPaymentMethods(null))
@ -74,9 +79,9 @@ namespace BTCPayServer.Controllers
var paymentMethodId = data.GetId();
var cryptoPayment = new InvoiceDetailsModel.CryptoPayment();
cryptoPayment.PaymentMethod = ToString(paymentMethodId);
cryptoPayment.Due = accounting.Due.ToString() + $" {paymentMethodId.CryptoCode}";
cryptoPayment.Paid = accounting.CryptoPaid.ToString() + $" {paymentMethodId.CryptoCode}";
cryptoPayment.Overpaid = (accounting.DueUncapped > Money.Zero ? Money.Zero : -accounting.DueUncapped).ToString() + $" {paymentMethodId.CryptoCode}";
cryptoPayment.Due = $"{accounting.Due} {paymentMethodId.CryptoCode}";
cryptoPayment.Paid = $"{accounting.CryptoPaid} {paymentMethodId.CryptoCode}";
cryptoPayment.Overpaid = $"{accounting.OverpaidHelper} {paymentMethodId.CryptoCode}";
var onchainMethod = data.GetPaymentMethodDetails() as Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod;
if (onchainMethod != null)
@ -207,7 +212,7 @@ namespace BTCPayServer.Controllers
private async Task<PaymentModel> GetInvoiceModel(string invoiceId, string paymentMethodIdStr)
{
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
if (invoice == null)
return null;
var store = await _StoreRepository.FindStore(invoice.StoreId);
@ -296,7 +301,9 @@ namespace BTCPayServer.Controllers
throw new NotSupportedException(),
TxCount = accounting.TxRequired,
BtcPaid = accounting.Paid.ToString(),
Status = invoice.Status,
#pragma warning disable CS0618 // Type or member is obsolete
Status = invoice.StatusString,
#pragma warning restore CS0618 // Type or member is obsolete
NetworkFee = paymentMethodDetails.GetTxFee(),
IsMultiCurrency = invoice.GetPayments().Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
ChangellyEnabled = changelly != null,
@ -367,8 +374,8 @@ namespace BTCPayServer.Controllers
{
if (!HttpContext.WebSockets.IsWebSocketRequest)
return NotFound();
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
if (invoice == null || invoice.Status == "complete" || invoice.Status == "invalid" || invoice.Status == "expired")
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
if (invoice == null || invoice.Status == InvoiceStatus.Complete || invoice.Status == InvoiceStatus.Invalid || invoice.Status == InvoiceStatus.Expired)
return NotFound();
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
CompositeDisposable leases = new CompositeDisposable();
@ -435,15 +442,18 @@ namespace BTCPayServer.Controllers
var list = await ListInvoicesProcess(searchTerm, skip, count);
foreach (var invoice in list)
{
var state = invoice.GetInvoiceState();
model.Invoices.Add(new InvoiceModel()
{
Status = invoice.Status + (invoice.ExceptionStatus == null ? string.Empty : $" ({invoice.ExceptionStatus})"),
ShowCheckout = invoice.Status == "new",
Status = state.ToString(),
ShowCheckout = invoice.Status == InvoiceStatus.New,
Date = invoice.InvoiceTime,
InvoiceId = invoice.Id,
OrderId = invoice.OrderId ?? string.Empty,
RedirectUrl = invoice.RedirectURL ?? string.Empty,
AmountCurrency = $"{invoice.ProductInformation.Price.ToString(CultureInfo.InvariantCulture)} {invoice.ProductInformation.Currency}"
AmountCurrency = $"{invoice.ProductInformation.Price.ToString(CultureInfo.InvariantCulture)} {invoice.ProductInformation.Currency}",
CanMarkInvalid = state.CanMarkInvalid(),
CanMarkComplete = state.CanMarkComplete()
});
}
return View(model);
@ -469,6 +479,28 @@ namespace BTCPayServer.Controllers
return list;
}
[HttpGet]
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
[BitpayAPIConstraint(false)]
public async Task<IActionResult> Export(string format, string searchTerm = null)
{
var model = new InvoiceExport();
var invoices = await ListInvoicesProcess(searchTerm, 0, int.MaxValue);
var res = model.Process(invoices, format);
var cd = new ContentDisposition
{
FileName = $"btcpay-export-{DateTime.UtcNow.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture)}.{format}",
Inline = true
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("X-Content-Type-Options", "nosniff");
return Content(res, "application/" + format);
}
[HttpGet]
[Route("invoices/create")]
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
@ -561,17 +593,60 @@ namespace BTCPayServer.Controllers
});
}
[HttpPost]
[Route("invoices/invalidatepaid")]
[HttpGet]
[Route("invoices/{invoiceId}/changestate/{newState}")]
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
[BitpayAPIConstraint(false)]
public async Task<IActionResult> InvalidatePaidInvoice(string invoiceId)
public IActionResult ChangeInvoiceState(string invoiceId, string newState)
{
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
if (newState == "invalid")
{
return View("Confirm", new ConfirmModel()
{
Action = "Make invoice invalid",
Title = "Change invoice state",
Description = $"You will transition the state of this invoice to \"invalid\", do you want to continue?",
});
}
else if (newState == "complete")
{
return View("Confirm", new ConfirmModel()
{
Action = "Make invoice complete",
Title = "Change invoice state",
Description = $"You will transition the state of this invoice to \"complete\", do you want to continue?",
ButtonClass = "btn-primary"
});
}
else
return NotFound();
}
[HttpPost]
[Route("invoices/{invoiceId}/changestate/{newState}")]
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
[BitpayAPIConstraint(false)]
public async Task<IActionResult> ChangeInvoiceStateConfirm(string invoiceId, string newState)
{
var invoice = (await _InvoiceRepository.GetInvoices(new InvoiceQuery()
{
InvoiceId = invoiceId,
UserId = GetUserId()
})).FirstOrDefault();
if (invoice == null)
return NotFound();
await _InvoiceRepository.UpdatePaidInvoiceToInvalid(invoiceId);
_EventAggregator.Publish(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 1008, "invoice_markedInvalid"));
if (newState == "invalid")
{
await _InvoiceRepository.UpdatePaidInvoiceToInvalid(invoiceId);
_EventAggregator.Publish(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 1008, "invoice_markedInvalid"));
StatusMessage = "Invoice marked invalid";
}
else if(newState == "complete")
{
await _InvoiceRepository.UpdatePaidInvoiceToComplete(invoiceId);
_EventAggregator.Publish(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 2008, "invoice_markedComplete"));
StatusMessage = "Invoice marked complete";
}
return RedirectToAction(nameof(ListInvoices));
}
@ -586,5 +661,43 @@ namespace BTCPayServer.Controllers
{
return _UserManager.GetUserId(User);
}
public class PosDataParser
{
public static Dictionary<string, string> ParsePosData(string posData)
{
var result = new Dictionary<string,string>();
if (string.IsNullOrEmpty(posData))
{
return result;
}
try
{
var jObject =JObject.Parse(posData);
foreach (var item in jObject)
{
switch (item.Value.Type)
{
case JTokenType.Array:
result.Add(item.Key, string.Join(',', item.Value.AsEnumerable()));
break;
default:
result.Add(item.Key, item.Value.ToString());
break;
}
}
}
catch
{
result.Add(string.Empty, posData);
}
return result;
}
}
}
}

View File

@ -99,7 +99,7 @@ namespace BTCPayServer.Controllers
if (!Uri.IsWellFormedUriString(entity.RedirectURL, UriKind.Absolute))
entity.RedirectURL = null;
entity.Status = "new";
entity.Status = InvoiceStatus.New;
entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);
HashSet<CurrencyPair> currencyPairsToFetch = new HashSet<CurrencyPair>();

View File

@ -443,7 +443,7 @@ namespace BTCPayServer.Controllers
if (!is2faTokenValid)
{
ModelState.AddModelError("model.Code", "Verification code is invalid.");
ModelState.AddModelError(nameof(model.Code), "Verification code is invalid.");
return View(model);
}

View File

@ -435,12 +435,27 @@ namespace BTCPayServer.Controllers
});
}
}
result.HasSSH = _Options.SSHSettings != null;
foreach(var externalService in _Options.ExternalServices)
{
result.ExternalServices.Add(new ServicesViewModel.ExternalService()
{
Name = externalService.Key,
Link = this.Request.GetRelativePath(externalService.Value)
});
}
if(_Options.SSHSettings != null)
{
result.ExternalServices.Add(new ServicesViewModel.ExternalService()
{
Name = "SSH",
Link = this.Url.Action(nameof(SSHService))
});
}
return View(result);
}
[Route("server/services/lnd-grpc/{cryptoCode}/{index}")]
public IActionResult LndGrpcServices(string cryptoCode, int index, uint? nonce)
[Route("server/services/lnd/{cryptoCode}/{index}")]
public IActionResult LndServices(string cryptoCode, int index, uint? nonce)
{
if (!_dashBoard.IsFullySynched(cryptoCode, out var unusud))
{
@ -451,9 +466,18 @@ namespace BTCPayServer.Controllers
if (external == null)
return NotFound();
var model = new LndGrpcServicesViewModel();
if (external.ConnectionType == LightningConnectionType.LndGRPC)
{
model.Host = $"{external.BaseUri.DnsSafeHost}:{external.BaseUri.Port}";
model.SSL = external.BaseUri.Scheme == "https";
model.ConnectionType = "GRPC";
}
else if(external.ConnectionType == LightningConnectionType.LndREST)
{
model.Uri = external.BaseUri.AbsoluteUri;
model.ConnectionType = "REST";
}
model.Host = $"{external.BaseUri.DnsSafeHost}:{external.BaseUri.Port}";
model.SSL = external.BaseUri.Scheme == "https";
if (external.CertificateThumbprint != null)
{
model.CertificateThumbprint = Encoders.Hex.EncodeData(external.CertificateThumbprint);
@ -462,10 +486,14 @@ namespace BTCPayServer.Controllers
{
model.Macaroon = Encoders.Hex.EncodeData(external.Macaroon);
}
if (external.RestrictedMacaroon != null)
{
model.RestrictedMacaroon = Encoders.Hex.EncodeData(external.RestrictedMacaroon);
}
if (nonce != null)
{
var configKey = GetConfigKey("lnd-grpc", cryptoCode, index, nonce.Value);
var configKey = GetConfigKey("lnd", cryptoCode, index, nonce.Value);
var lnConfig = _LnConfigProvider.GetConfig(configKey);
if (lnConfig != null)
{
@ -492,29 +520,44 @@ namespace BTCPayServer.Controllers
return Json(conf);
}
[Route("server/services/lnd-grpc/{cryptoCode}/{index}")]
[Route("server/services/lnd/{cryptoCode}/{index}")]
[HttpPost]
public IActionResult LndGrpcServicesPost(string cryptoCode, int index)
public IActionResult LndServicesPost(string cryptoCode, int index)
{
var external = GetExternalLndConnectionString(cryptoCode, index);
if (external == null)
return NotFound();
LightningConfigurations confs = new LightningConfigurations();
LightningConfiguration conf = new LightningConfiguration();
conf.Type = "grpc";
conf.ChainType = _Options.NetworkType.ToString();
conf.CryptoCode = cryptoCode;
conf.Host = external.BaseUri.DnsSafeHost;
conf.Port = external.BaseUri.Port;
conf.SSL = external.BaseUri.Scheme == "https";
conf.Macaroon = external.Macaroon == null ? null : Encoders.Hex.EncodeData(external.Macaroon);
conf.CertificateThumbprint = external.CertificateThumbprint == null ? null : Encoders.Hex.EncodeData(external.CertificateThumbprint);
confs.Configurations.Add(conf);
if (external.ConnectionType == LightningConnectionType.LndGRPC)
{
LightningConfiguration conf = new LightningConfiguration();
conf.Type = "grpc";
conf.ChainType = _Options.NetworkType.ToString();
conf.CryptoCode = cryptoCode;
conf.Host = external.BaseUri.DnsSafeHost;
conf.Port = external.BaseUri.Port;
conf.SSL = external.BaseUri.Scheme == "https";
conf.Macaroon = external.Macaroon == null ? null : Encoders.Hex.EncodeData(external.Macaroon);
conf.RestrictedMacaroon = external.RestrictedMacaroon == null ? null : Encoders.Hex.EncodeData(external.RestrictedMacaroon);
conf.CertificateThumbprint = external.CertificateThumbprint == null ? null : Encoders.Hex.EncodeData(external.CertificateThumbprint);
confs.Configurations.Add(conf);
}
else if (external.ConnectionType == LightningConnectionType.LndREST)
{
var restconf = new LNDRestConfiguration();
restconf.Type = "lnd-rest";
restconf.ChainType = _Options.NetworkType.ToString();
restconf.CryptoCode = cryptoCode;
restconf.Uri = external.BaseUri.AbsoluteUri;
restconf.Macaroon = external.Macaroon == null ? null : Encoders.Hex.EncodeData(external.Macaroon);
restconf.RestrictedMacaroon = external.RestrictedMacaroon == null ? null : Encoders.Hex.EncodeData(external.RestrictedMacaroon);
restconf.CertificateThumbprint = external.CertificateThumbprint == null ? null : Encoders.Hex.EncodeData(external.CertificateThumbprint);
confs.Configurations.Add(restconf);
}
var nonce = RandomUtils.GetUInt32();
var configKey = GetConfigKey("lnd-grpc", cryptoCode, index, nonce);
var configKey = GetConfigKey("lnd", cryptoCode, index, nonce);
_LnConfigProvider.KeepConfig(configKey, confs);
return RedirectToAction(nameof(LndGrpcServices), new { cryptoCode = cryptoCode, nonce = nonce });
return RedirectToAction(nameof(LndServices), new { cryptoCode = cryptoCode, nonce = nonce });
}
private LightningConnectionString GetExternalLndConnectionString(string cryptoCode, int index)
@ -536,29 +579,19 @@ namespace BTCPayServer.Controllers
return null;
}
}
return connectionString;
}
[Route("server/services/lnd-rest/{cryptoCode}/{index}")]
public IActionResult LndRestServices(string cryptoCode, int index, uint? nonce)
{
if (!_dashBoard.IsFullySynched(cryptoCode, out var unusud))
if (connectionString.RestrictedMacaroonFilePath != null)
{
StatusMessage = $"Error: {cryptoCode} is not fully synched";
return RedirectToAction(nameof(Services));
try
{
connectionString.RestrictedMacaroon = System.IO.File.ReadAllBytes(connectionString.RestrictedMacaroonFilePath);
}
catch
{
Logs.Configuration.LogWarning($"{cryptoCode}: The restrictedmacaroon file path of the external LND grpc config was not found ({connectionString.RestrictedMacaroonFilePath})");
}
connectionString.RestrictedMacaroonFilePath = null;
}
var external = GetExternalLndConnectionString(cryptoCode, index);
if (external == null)
return NotFound();
var model = new LndRestServicesViewModel();
model.BaseApiUrl = external.BaseUri.ToString();
if (external.CertificateThumbprint != null)
model.CertificateThumbprint = Encoders.Hex.EncodeData(external.CertificateThumbprint);
if (external.Macaroon != null)
model.Macaroon = Encoders.Hex.EncodeData(external.Macaroon);
return View(model);
return connectionString;
}
[Route("server/services/ssh")]
@ -666,18 +699,24 @@ namespace BTCPayServer.Controllers
if (string.IsNullOrEmpty(file)) return View("Logs", vm);
vm.Log = "";
var path = Path.Combine(di.FullName, file);
using (var fileStream = new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
try
{
using (var reader = new StreamReader(fileStream))
using (var fileStream = new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
{
vm.Log = await reader.ReadToEndAsync();
using (var reader = new StreamReader(fileStream))
{
vm.Log = await reader.ReadToEndAsync();
}
}
}
catch
{
return NotFound();
}
}
return View("Logs", vm);

View File

@ -163,6 +163,7 @@ namespace BTCPayServer.Controllers
// - The user is clicking on continue without changing anything
(!vm.Confirmation && willBeExcluded == wasExcluded);
showAddress = showAddress && strategy != null;
if (!showAddress)
{
try

View File

@ -789,7 +789,8 @@ namespace BTCPayServer.Controllers
StatusMessage = "Server initiated pairing code: " + pairingCode;
return RedirectToAction(nameof(ListTokens), new
{
storeId = store.Id
storeId = store.Id,
pairingCode = pairingCode
});
}
else

View File

@ -81,5 +81,10 @@ namespace BTCPayServer.Data
get; set;
}
public List<PendingInvoiceData> PendingInvoices { get; set; }
public Services.Invoices.InvoiceState GetInvoiceState()
{
return new Services.Invoices.InvoiceState(Status, ExceptionStatus);
}
}
}

View File

@ -63,10 +63,18 @@ namespace BTCPayServer
electrumMapping.Add(p2wpkh, Array.Empty<string>());
var parts = str.Split('-');
bool hasLabel = false;
for (int i = 0; i < parts.Length; i++)
{
if (IsLabel(parts[i]))
{
if (!hasLabel)
{
hintedLabels.Clear();
if (!Network.Consensus.SupportSegwit)
hintedLabels.Add("legacy");
}
hasLabel = true;
hintedLabels.Add(parts[i].Substring(1, parts[i].Length - 2).ToLowerInvariant());
continue;
}

View File

@ -11,23 +11,14 @@ namespace BTCPayServer.Events
public InvoiceDataChangedEvent(InvoiceEntity invoice)
{
InvoiceId = invoice.Id;
Status = invoice.Status;
ExceptionStatus = invoice.ExceptionStatus;
State = invoice.GetInvoiceState();
}
public string InvoiceId { get; set; }
public string Status { get; internal set; }
public string ExceptionStatus { get; internal set; }
public string InvoiceId { get; }
public InvoiceState State { get; }
public override string ToString()
{
if (string.IsNullOrEmpty(ExceptionStatus) || ExceptionStatus == "false")
{
return $"Invoice status is {Status}";
}
else
{
return $"Invoice status is {Status} (Exception status: {ExceptionStatus})";
}
return $"Invoice status is {State}";
}
}
}

View File

@ -168,6 +168,15 @@ namespace BTCPayServer
request.Path.ToUriComponent());
}
public static string GetRelativePath(this HttpRequest request, string path)
{
if (path.Length > 0 && path[0] != '/')
path = $"/{path}";
return string.Concat(
request.PathBase.ToUriComponent(),
path);
}
public static string GetAbsoluteUri(this HttpRequest request, string redirectUrl)
{
bool isRelative =

View File

@ -330,7 +330,7 @@ namespace BTCPayServer.HostedServices
{
leases.Add(_EventAggregator.Subscribe<InvoiceEvent>(async e =>
{
var invoice = await _InvoiceRepository.GetInvoice(null, e.Invoice.Id);
var invoice = await _InvoiceRepository.GetInvoice(e.Invoice.Id);
if (invoice == null)
return;
List<Task> tasks = new List<Task>();
@ -345,6 +345,7 @@ namespace BTCPayServer.HostedServices
e.Name == "invoice_paidInFull" ||
e.Name == "invoice_failedToConfirm" ||
e.Name == "invoice_markedInvalid" ||
e.Name == "invoice_markedComplete" ||
e.Name == "invoice_failedToConfirm" ||
e.Name == "invoice_completed" ||
e.Name == "invoice_expiredPaidPartial"

View File

@ -61,14 +61,14 @@ namespace BTCPayServer.HostedServices
private async Task UpdateInvoice(UpdateInvoiceContext context)
{
var invoice = context.Invoice;
if (invoice.Status == "new" && invoice.ExpirationTime < DateTimeOffset.UtcNow)
if (invoice.Status == InvoiceStatus.New && invoice.ExpirationTime < DateTimeOffset.UtcNow)
{
context.MarkDirty();
await _InvoiceRepository.UnaffectAddress(invoice.Id);
context.Events.Add(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 1004, "invoice_expired"));
invoice.Status = "expired";
if(invoice.ExceptionStatus == "paidPartial")
invoice.Status = InvoiceStatus.Expired;
if(invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial)
context.Events.Add(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 2000, "invoice_expiredPaidPartial"));
}
@ -78,57 +78,57 @@ namespace BTCPayServer.HostedServices
if (paymentMethod == null)
return;
var network = _NetworkProvider.GetNetwork(paymentMethod.GetId().CryptoCode);
if (invoice.Status == "new" || invoice.Status == "expired")
if (invoice.Status == InvoiceStatus.New || invoice.Status == InvoiceStatus.Expired)
{
if (accounting.Paid >= accounting.MinimumTotalDue)
{
if (invoice.Status == "new")
if (invoice.Status == InvoiceStatus.New)
{
context.Events.Add(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 1003, "invoice_paidInFull"));
invoice.Status = "paid";
invoice.ExceptionStatus = accounting.Paid > accounting.TotalDue ? "paidOver" : null;
invoice.Status = InvoiceStatus.Paid;
invoice.ExceptionStatus = accounting.Paid > accounting.TotalDue ? InvoiceExceptionStatus.PaidOver : InvoiceExceptionStatus.None;
await _InvoiceRepository.UnaffectAddress(invoice.Id);
context.MarkDirty();
}
else if (invoice.Status == "expired" && invoice.ExceptionStatus != "paidLate")
else if (invoice.Status == InvoiceStatus.Expired && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidLate)
{
invoice.ExceptionStatus = "paidLate";
invoice.ExceptionStatus = InvoiceExceptionStatus.PaidLate;
context.Events.Add(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 1009, "invoice_paidAfterExpiration"));
context.MarkDirty();
}
}
if (accounting.Paid < accounting.MinimumTotalDue && invoice.GetPayments().Count != 0 && invoice.ExceptionStatus != "paidPartial")
if (accounting.Paid < accounting.MinimumTotalDue && invoice.GetPayments().Count != 0 && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidPartial)
{
invoice.ExceptionStatus = "paidPartial";
invoice.ExceptionStatus = InvoiceExceptionStatus.PaidPartial;
context.MarkDirty();
}
}
// Just make sure RBF did not cancelled a payment
if (invoice.Status == "paid")
if (invoice.Status == InvoiceStatus.Paid)
{
if (accounting.MinimumTotalDue <= accounting.Paid && accounting.Paid <= accounting.TotalDue && invoice.ExceptionStatus == "paidOver")
if (accounting.MinimumTotalDue <= accounting.Paid && accounting.Paid <= accounting.TotalDue && invoice.ExceptionStatus == InvoiceExceptionStatus.PaidOver)
{
invoice.ExceptionStatus = null;
invoice.ExceptionStatus = InvoiceExceptionStatus.None;
context.MarkDirty();
}
if (accounting.Paid > accounting.TotalDue && invoice.ExceptionStatus != "paidOver")
if (accounting.Paid > accounting.TotalDue && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidOver)
{
invoice.ExceptionStatus = "paidOver";
invoice.ExceptionStatus = InvoiceExceptionStatus.PaidOver;
context.MarkDirty();
}
if (accounting.Paid < accounting.MinimumTotalDue)
{
invoice.Status = "new";
invoice.ExceptionStatus = accounting.Paid == Money.Zero ? null : "paidPartial";
invoice.Status = InvoiceStatus.New;
invoice.ExceptionStatus = accounting.Paid == Money.Zero ? InvoiceExceptionStatus.None : InvoiceExceptionStatus.PaidPartial;
context.MarkDirty();
}
}
if (invoice.Status == "paid")
if (invoice.Status == InvoiceStatus.Paid)
{
var confirmedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentConfirmed(p, invoice.SpeedPolicy, network));
@ -140,25 +140,25 @@ namespace BTCPayServer.HostedServices
{
await _InvoiceRepository.UnaffectAddress(invoice.Id);
context.Events.Add(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 1013, "invoice_failedToConfirm"));
invoice.Status = "invalid";
invoice.Status = InvoiceStatus.Invalid;
context.MarkDirty();
}
else if (confirmedAccounting.Paid >= accounting.MinimumTotalDue)
{
await _InvoiceRepository.UnaffectAddress(invoice.Id);
context.Events.Add(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 1005, "invoice_confirmed"));
invoice.Status = "confirmed";
invoice.Status = InvoiceStatus.Confirmed;
context.MarkDirty();
}
}
if (invoice.Status == "confirmed")
if (invoice.Status == InvoiceStatus.Confirmed)
{
var completedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentCompleted(p, network));
if (completedAccounting.Paid >= accounting.MinimumTotalDue)
{
context.Events.Add(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 1006, "invoice_completed"));
invoice.Status = "complete";
invoice.Status = InvoiceStatus.Complete;
context.MarkDirty();
}
}
@ -208,7 +208,7 @@ namespace BTCPayServer.HostedServices
private async Task Wait(string invoiceId)
{
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
try
{
var delay = invoice.ExpirationTime - DateTimeOffset.UtcNow;
@ -283,14 +283,14 @@ namespace BTCPayServer.HostedServices
loopCount++;
try
{
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId, true);
var invoice = await _InvoiceRepository.GetInvoice(invoiceId, true);
if (invoice == null)
break;
var updateContext = new UpdateInvoiceContext(invoice);
await UpdateInvoice(updateContext);
if (updateContext.Dirty)
{
await _InvoiceRepository.UpdateInvoiceStatus(invoice.Id, invoice.Status, invoice.ExceptionStatus);
await _InvoiceRepository.UpdateInvoiceStatus(invoice.Id, invoice.GetInvoiceState());
updateContext.Events.Insert(0, new InvoiceDataChangedEvent(invoice));
}
@ -299,8 +299,8 @@ namespace BTCPayServer.HostedServices
_EventAggregator.Publish(evt, evt.GetType());
}
if (invoice.Status == "complete" ||
((invoice.Status == "invalid" || invoice.Status == "expired") && invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
if (invoice.Status == InvoiceStatus.Complete ||
((invoice.Status == InvoiceStatus.Invalid || invoice.Status == InvoiceStatus.Expired) && invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
{
if (await _InvoiceRepository.RemovePendingInvoice(invoice.Id))
_EventAggregator.Publish(new InvoiceStopWatchedEvent(invoice.Id));

View File

@ -14,15 +14,33 @@ namespace BTCPayServer.Models.AppViewModels
[Required]
[MaxLength(5)]
public string Currency { get; set; }
[Required]
[MaxLength(5000)]
public string Template { get; set; }
[Display(Name = "Enable shopping cart")]
public bool EnableShoppingCart { get; set; }
[Display(Name = "User can input custom amount")]
public bool ShowCustomAmount { get; set; }
public string Example1 { get; internal set; }
public string Example2 { get; internal set; }
public string ExampleCallback { get; internal set; }
public string InvoiceUrl { get; internal set; }
[Required]
[MaxLength(30)]
[Display(Name = "Text to display on each buttons for items with a specific price")]
public string ButtonText { get; set; }
[Required]
[MaxLength(30)]
[Display(Name = "Text to display on buttons next to the input allowing the user to enter a custom amount")]
public string CustomButtonText { get; set; }
[Required]
[MaxLength(30)]
[Display(Name = "Do you want to leave a tip?")]
public string CustomTipText { get; set; }
[MaxLength(500)]
[Display(Name = "Custom bootstrap CSS file")]
public string CustomCSSLink { get; set; }
}
}

View File

@ -14,13 +14,37 @@ namespace BTCPayServer.Models.AppViewModels
public string Formatted { get; set; }
public decimal Value { get; set; }
}
public string Description { get; set; }
public string Id { get; set; }
public string Image { get; set; }
public ItemPrice Price { get; set; }
public string Title { get; set; }
public bool Custom { get; set; }
}
public class CurrencyInfoData
{
public bool Prefixed { get; set; }
public string CurrencySymbol { get; set; }
public string ThousandSeparator { get; set; }
public string DecimalSeparator { get; set; }
public int Divisibility { get; internal set; }
}
public CurrencyInfoData CurrencyInfo { get; set; }
public bool EnableShoppingCart { get; set; }
public bool ShowCustomAmount { get; set; }
public string Step { get; set; }
public string Title { get; set; }
public Item[] Items { get; set; }
public string CurrencyCode { get; set; }
public string CurrencySymbol { get; set; }
public string AppId { get; set; }
public string ButtonText { get; set; }
public string CustomButtonText { get; set; }
public string CustomTipText { get; set; }
public string CustomCSSLink { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace BTCPayServer.Models
{
public class BitpayTranslatorViewModel
{
[Display(Name = "Bitpay's invoice URL or obsolete invoice url")]
public string BitpayLink { get; set; }
public string Address { get; set; }
public string Amount { get; set; }
public string BitcoinUri { get; set; }
}
}

View File

@ -81,11 +81,11 @@ namespace BTCPayServer.Models.InvoicingModels
public string BOLT11 { get; set; }
}
public string Status
public string State
{
get; set;
}
public string StatusException { get; set; }
public InvoiceExceptionStatus StatusException { get; set; }
public DateTimeOffset CreatedDate
{
get; set;
@ -143,5 +143,6 @@ namespace BTCPayServer.Models.InvoicingModels
public DateTimeOffset MonitoringDate { get; internal set; }
public List<Data.InvoiceEventData> Events { get; internal set; }
public string NotificationEmail { get; internal set; }
public Dictionary<string, string> PosData { get; set; }
}
}

View File

@ -46,6 +46,9 @@ namespace BTCPayServer.Models.InvoicingModels
{
get; set;
}
public bool CanMarkComplete { get; set; }
public bool CanMarkInvalid { get; set; }
public bool CanMarkStatus => CanMarkComplete || CanMarkInvalid;
public bool ShowCheckout { get; set; }
public string ExceptionStatus { get; set; }
public string AmountCurrency

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
@ -10,8 +11,12 @@ namespace BTCPayServer.Models.ServerViewModels
public string Host { get; set; }
public bool SSL { get; set; }
public string Macaroon { get; set; }
public string RestrictedMacaroon { get; set; }
public string CertificateThumbprint { get; set; }
public string QRCode { get; set; }
public string QRCodeLink { get; set; }
[Display(Name = "REST Uri")]
public string Uri { get; set; }
public string ConnectionType { get; internal set; }
}
}

View File

@ -15,7 +15,13 @@ namespace BTCPayServer.Models.ServerViewModels
public int Index { get; set; }
}
public class ExternalService
{
public string Name { get; set; }
public string Link { get; set; }
}
public List<LNDServiceViewModel> LNDServices { get; set; } = new List<LNDServiceViewModel>();
public bool HasSSH { get; set; }
public List<ExternalService> ExternalServices { get; set; } = new List<ExternalService>();
}
}

View File

@ -58,7 +58,7 @@ namespace BTCPayServer.Models.StoreViewModels
public void SetLanguages(LanguageService langService, string defaultLang)
{
defaultLang = defaultLang ?? "en";
defaultLang = langService.GetLanguages().Any(language => language.Code == defaultLang)? defaultLang : "en";
var choices = langService.GetLanguages().Select(o => new Format() { Name = o.DisplayName, Value = o.Code }).ToArray();
var chosen = choices.FirstOrDefault(f => f.Value == defaultLang) ?? choices.FirstOrDefault();
Languages = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen);

View File

@ -51,7 +51,7 @@ namespace BTCPayServer.Payments.Bitcoin
}
CompositeDisposable leases = new CompositeDisposable();
ConcurrentDictionary<string, NotificationSession> _SessionsByCryptoCode = new ConcurrentDictionary<string, NotificationSession>();
ConcurrentDictionary<string, WebsocketNotificationSession> _SessionsByCryptoCode = new ConcurrentDictionary<string, WebsocketNotificationSession>();
private Timer _ListenPoller;
TimeSpan _PollInterval;
@ -114,7 +114,7 @@ namespace BTCPayServer.Payments.Bitcoin
return;
if (_Cts.IsCancellationRequested)
return;
var session = await client.CreateNotificationSessionAsync(_Cts.Token).ConfigureAwait(false);
var session = await client.CreateWebsocketNotificationSessionAsync(_Cts.Token).ConfigureAwait(false);
if (!_SessionsByCryptoCode.TryAdd(network.CryptoCode, session))
{
await session.DisposeAsync();
@ -187,7 +187,7 @@ namespace BTCPayServer.Payments.Bitcoin
if (cleanup)
{
Logs.PayServer.LogInformation($"Disconnected from WebSocket of NBXplorer ({network.CryptoCode})");
_SessionsByCryptoCode.TryRemove(network.CryptoCode, out NotificationSession unused);
_SessionsByCryptoCode.TryRemove(network.CryptoCode, out WebsocketNotificationSession unused);
if (_SessionsByCryptoCode.Count == 0 && _Cts.IsCancellationRequested)
{
_RunningTask.TrySetResult(true);
@ -205,7 +205,7 @@ namespace BTCPayServer.Payments.Bitcoin
async Task<InvoiceEntity> UpdatePaymentStates(BTCPayWallet wallet, string invoiceId)
{
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId, false);
var invoice = await _InvoiceRepository.GetInvoice(invoiceId, false);
if (invoice == null)
return null;
List<PaymentEntity> updatedPaymentEntities = new List<PaymentEntity>();
@ -315,7 +315,7 @@ namespace BTCPayServer.Payments.Bitcoin
var invoices = await _InvoiceRepository.GetPendingInvoices();
foreach (var invoiceId in invoices)
{
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId, true);
var invoice = await _InvoiceRepository.GetInvoice(invoiceId, true);
if (invoice == null)
continue;
var alreadyAccounted = GetAllBitcoinPaymentData(invoice).Select(p => p.Outpoint).ToHashSet();

View File

@ -73,7 +73,7 @@ namespace BTCPayServer.Payments.Lightning
{
if (Listening(invoiceId))
return;
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
foreach (var paymentMethod in invoice.GetPaymentMethods(_NetworkProvider)
.Where(c => c.GetId().PaymentType == PaymentTypes.LightningLike))
{
@ -194,7 +194,7 @@ namespace BTCPayServer.Payments.Lightning
}, network.CryptoCode, accounted: true);
if (payment != null)
{
var invoice = await _InvoiceRepository.GetInvoice(null, listenedInvoice.InvoiceId);
var invoice = await _InvoiceRepository.GetInvoice(listenedInvoice.InvoiceId);
if (invoice != null)
_Aggregator.Publish(new InvoiceEvent(invoice.EntityToDTO(_NetworkProvider), 1002, "invoice_receivedPayment"));
}

View File

@ -5,7 +5,7 @@
"launchBrowser": true,
"environmentVariables": {
"BTCPAY_NETWORK": "regtest",
"BTCPAY_BUNDLEJSCSS": "true",
"BTCPAY_BUNDLEJSCSS": "false",
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",
@ -24,15 +24,16 @@
"BTCPAY_NETWORK": "regtest",
"BTCPAY_PORT": "14142",
"BTCPAY_HttpsUseDefaultCertificate": "true",
"BTCPAY_BUNDLEJSCSS": "true",
"BTCPAY_BUNDLEJSCSS": "false",
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",
"BTCPAY_BTCEXTERNALLNDREST": "type=lnd-rest;server=https://lnd:lnd@127.0.0.1:53280/lnd-rest/btc/;allowinsecure=true;macaroonfilepath=D:\\admin.macaroon",
"BTCPAY_BTCEXTERNALLNDREST": "type=lnd-rest;server=https://lnd:lnd@127.0.0.1:53280/lnd-rest/btc/;allowinsecure=true",
"BTCPAY_BTCEXPLORERURL": "http://127.0.0.1:32838/",
"ASPNETCORE_ENVIRONMENT": "Development",
"BTCPAY_CHAINS": "btc,ltc",
"BTCPAY_POSTGRES": "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver"
"BTCPAY_POSTGRES": "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver",
"BTCPAY_EXTERNALSERVICES": "totoservice:totolink;"
},
"applicationUrl": "https://localhost:14142/"
}

View File

@ -57,43 +57,37 @@ namespace BTCPayServer.Security
List<Claim> claims = new List<Claim>();
var bitpayAuth = Context.Request.HttpContext.GetBitpayAuth();
string storeId = null;
// Careful, those are not the opposite. failedAuth says if a the tentative failed.
// successAuth, ensure that at least one succeed.
var failedAuth = false;
var successAuth = false;
bool? success = null;
if (!string.IsNullOrEmpty(bitpayAuth.Signature) && !string.IsNullOrEmpty(bitpayAuth.Id))
{
var result = await CheckBitId(Context.Request.HttpContext, bitpayAuth.Signature, bitpayAuth.Id, claims);
storeId = result.StoreId;
failedAuth = !result.SuccessAuth;
successAuth = result.SuccessAuth;
success = result.SuccessAuth;
}
else if (!string.IsNullOrEmpty(bitpayAuth.Authorization))
{
storeId = await CheckLegacyAPIKey(Context.Request.HttpContext, bitpayAuth.Authorization);
if (storeId == null)
{
Logs.PayServer.LogDebug("API key check failed");
failedAuth = true;
}
successAuth = storeId != null;
success = storeId != null;
}
if (failedAuth)
if (success.HasValue)
{
return AuthenticateResult.Fail("Invalid credentials");
}
if (successAuth)
{
if (storeId != null)
if (success.Value)
{
claims.Add(new Claim(Policies.CanCreateInvoice.Key, storeId));
var store = await _StoreRepository.FindStore(storeId);
store.AdditionalClaims.AddRange(claims);
Context.Request.HttpContext.SetStoreData(store);
if (storeId != null)
{
claims.Add(new Claim(Policies.CanCreateInvoice.Key, storeId));
var store = await _StoreRepository.FindStore(storeId);
store.AdditionalClaims.AddRange(claims);
Context.Request.HttpContext.SetStoreData(store);
}
return AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims, Policies.BitpayAuthentication)), Policies.BitpayAuthentication));
}
else
{
return AuthenticateResult.Fail("Invalid credentials");
}
return AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims, Policies.BitpayAuthentication)), Policies.BitpayAuthentication));
}
}
return AuthenticateResult.NoResult();
@ -148,6 +142,10 @@ namespace BTCPayServer.Security
storeId = bitToken.StoreId;
}
}
else
{
return (storeId, false);
}
}
catch (FormatException) { }
return (storeId, true);

View File

@ -0,0 +1,146 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
// Ref: https://www.codeproject.com/Articles/566656/CSV-Serializer-for-NET
namespace BTCPayServer.Services.Invoices.Export
{
/// <summary>
/// Serialize and Deserialize Lists of any object type to CSV.
/// </summary>
public class CsvSerializer<T> where T : class, new()
{
private List<PropertyInfo> _properties;
public bool IgnoreEmptyLines { get; set; } = true;
public bool IgnoreReferenceTypesExceptString { get; set; } = true;
public string NewlineReplacement { get; set; } = ((char)0x254).ToString(CultureInfo.InvariantCulture);
public char Separator { get; set; } = ',';
public string RowNumberColumnTitle { get; set; } = "RowNumber";
public bool UseLineNumbers { get; set; } = false;
public bool UseEofLiteral { get; set; } = false;
/// <summary>
/// Csv Serializer
/// Initialize by selected properties from the type to be de/serialized
/// </summary>
public CsvSerializer()
{
var type = typeof(T);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance
| BindingFlags.GetProperty | BindingFlags.SetProperty);
var q = properties.AsQueryable();
if (IgnoreReferenceTypesExceptString)
{
q = q.Where(a => a.PropertyType.IsValueType || a.PropertyType.Name == "String");
}
var r = from a in q
where a.GetCustomAttribute<CsvIgnoreAttribute>() == null
select a;
_properties = r.ToList();
}
/// <summary>
/// Serialize
/// </summary>
/// <param name="stream">stream</param>
/// <param name="data">data</param>
public string Serialize(IList<T> data)
{
var sb = new StringBuilder();
var values = new List<string>();
sb.AppendLine(GetHeader());
var row = 1;
foreach (var item in data)
{
values.Clear();
if (UseLineNumbers)
{
values.Add(row++.ToString(CultureInfo.InvariantCulture));
}
foreach (var p in _properties)
{
var raw = p.GetValue(item);
var value = raw == null ? "" :
raw.ToString()
.Replace("\"", "``", StringComparison.OrdinalIgnoreCase)
.Replace(Environment.NewLine, NewlineReplacement, StringComparison.OrdinalIgnoreCase);
value = String.Format(CultureInfo.InvariantCulture, "\"{0}\"", value);
values.Add(value);
}
sb.AppendLine(String.Join(Separator.ToString(CultureInfo.InvariantCulture), values.ToArray()));
}
if (UseEofLiteral)
{
values.Clear();
if (UseLineNumbers)
{
values.Add(row++.ToString(CultureInfo.InvariantCulture));
}
values.Add("EOF");
sb.AppendLine(string.Join(Separator.ToString(CultureInfo.InvariantCulture), values.ToArray()));
}
return sb.ToString();
}
/// <summary>
/// Get Header
/// </summary>
/// <returns></returns>
private string GetHeader()
{
var header = _properties.Select(a => a.Name);
if (UseLineNumbers)
{
header = new string[] { RowNumberColumnTitle }.Union(header);
}
return string.Join(Separator.ToString(CultureInfo.InvariantCulture), header.ToArray());
}
}
public class CsvIgnoreAttribute : Attribute { }
public class InvalidCsvFormatException : Exception
{
/// <summary>
/// Invalid Csv Format Exception
/// </summary>
/// <param name="message">message</param>
public InvalidCsvFormatException(string message)
: base(message)
{
}
public InvalidCsvFormatException(string message, Exception ex)
: base(message, ex)
{
}
}
}

View File

@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Payments.Bitcoin;
using BTCPayServer.Services.Invoices;
using Newtonsoft.Json;
namespace BTCPayServer.Services.Invoices.Export
{
public class InvoiceExport
{
public string Process(InvoiceEntity[] invoices, string fileFormat)
{
var csvInvoices = new List<ExportInvoiceHolder>();
foreach (var i in invoices)
{
csvInvoices.AddRange(convertFromDb(i));
}
if (String.Equals(fileFormat, "json", StringComparison.OrdinalIgnoreCase))
return processJson(csvInvoices);
else if (String.Equals(fileFormat, "csv", StringComparison.OrdinalIgnoreCase))
return processCsv(csvInvoices);
else
throw new Exception("Export format not supported");
}
private string processJson(List<ExportInvoiceHolder> invoices)
{
var serializerSett = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var json = JsonConvert.SerializeObject(invoices, Formatting.Indented, serializerSett);
return json;
}
private string processCsv(List<ExportInvoiceHolder> invoices)
{
var serializer = new CsvSerializer<ExportInvoiceHolder>();
var csv = serializer.Serialize(invoices);
return csv;
}
private IEnumerable<ExportInvoiceHolder> convertFromDb(InvoiceEntity invoice)
{
var exportList = new List<ExportInvoiceHolder>();
// in this first version we are only exporting invoices that were paid
foreach (var payment in invoice.GetPayments())
{
// not accounted payments are payments which got double spent like RBfed
if (!payment.Accounted)
continue;
var cryptoCode = payment.GetPaymentMethodId().CryptoCode;
var pdata = payment.GetCryptoPaymentData();
var pmethod = invoice.GetPaymentMethod(payment.GetPaymentMethodId(), null);
var accounting = pmethod.Calculate();
var details = pmethod.GetPaymentMethodDetails();
var target = new ExportInvoiceHolder
{
ReceivedDate = payment.ReceivedTime.UtcDateTime,
PaymentId = pdata.GetPaymentId(),
CryptoCode = cryptoCode,
ConversionRate = pmethod.Rate,
PaymentType = details.GetPaymentType() == Payments.PaymentTypes.BTCLike ? "OnChain" : "OffChain",
Destination = details.GetPaymentDestination(),
PaymentDue = $"{accounting.MinimumTotalDue} {cryptoCode}",
PaymentPaid = $"{accounting.CryptoPaid} {cryptoCode}",
PaymentOverpaid = $"{accounting.OverpaidHelper} {cryptoCode}",
OrderId = invoice.OrderId,
StoreId = invoice.StoreId,
InvoiceId = invoice.Id,
CreatedDate = invoice.InvoiceTime.UtcDateTime,
ExpirationDate = invoice.ExpirationTime.UtcDateTime,
MonitoringDate = invoice.MonitoringExpiration.UtcDateTime,
#pragma warning disable CS0618 // Type or member is obsolete
Status = invoice.StatusString,
#pragma warning restore CS0618 // Type or member is obsolete
ItemCode = invoice.ProductInformation?.ItemCode,
ItemDesc = invoice.ProductInformation?.ItemDesc,
FiatPrice = invoice.ProductInformation?.Price ?? 0,
FiatCurrency = invoice.ProductInformation?.Currency,
};
exportList.Add(target);
}
exportList = exportList.OrderBy(a => a.ReceivedDate).ToList();
return exportList;
}
}
public class ExportInvoiceHolder
{
public DateTime ReceivedDate { get; set; }
public string StoreId { get; set; }
public string OrderId { get; set; }
public string InvoiceId { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ExpirationDate { get; set; }
public DateTime MonitoringDate { get; set; }
public string PaymentId { get; set; }
public string CryptoCode { get; set; }
public string Destination { get; set; }
public string PaymentType { get; set; }
public string PaymentDue { get; set; }
public string PaymentPaid { get; set; }
public string PaymentOverpaid { get; set; }
public decimal ConversionRate { get; set; }
public decimal FiatPrice { get; set; }
public string FiatCurrency { get; set; }
public string ItemCode { get; set; }
public string ItemDesc { get; set; }
public string Status { get; set; }
}
}

View File

@ -226,15 +226,23 @@ namespace BTCPayServer.Services.Invoices
#pragma warning restore CS0618
}
public string Status
[JsonIgnore]
public InvoiceStatus Status
{
get;
set;
}
public string ExceptionStatus
[JsonProperty(PropertyName = "status")]
[Obsolete("Use Status instead")]
public string StatusString => InvoiceState.ToString(Status);
[JsonIgnore]
public InvoiceExceptionStatus ExceptionStatus
{
get; set;
}
[JsonProperty(PropertyName = "exceptionStatus")]
[Obsolete("Use ExceptionStatus instead")]
public string ExceptionStatusString => InvoiceState.ToString(ExceptionStatus);
[Obsolete("Use GetPayments instead")]
public List<PaymentEntity> Payments
@ -341,7 +349,10 @@ namespace BTCPayServer.Services.Invoices
CurrentTime = DateTimeOffset.UtcNow,
InvoiceTime = InvoiceTime,
ExpirationTime = ExpirationTime,
Status = Status,
#pragma warning disable CS0618 // Type or member is obsolete
Status = StatusString,
ExceptionStatus = ExceptionStatus == InvoiceExceptionStatus.None ? new JValue(false) : new JValue(ExceptionStatusString),
#pragma warning restore CS0618 // Type or member is obsolete
Currency = ProductInformation.Currency,
Flags = new Flags() { Refundable = Refundable },
PaymentSubtotals = new Dictionary<string, long>(),
@ -395,9 +406,6 @@ namespace BTCPayServer.Services.Invoices
var cryptoSuffix = cryptoInfo.CryptoCode == "BTC" ? "" : "/" + cryptoInfo.CryptoCode;
cryptoInfo.PaymentUrls = new NBitpayClient.InvoicePaymentUrls()
{
BIP72 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}&r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}",
BIP72b = $"{scheme}:?r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}",
BIP73 = ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}"),
BIP21 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}",
};
}
@ -450,7 +458,6 @@ namespace BTCPayServer.Services.Invoices
dto.Token = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16)); //No idea what it is useful for
dto.Guid = Guid.NewGuid().ToString();
dto.ExceptionStatus = ExceptionStatus == null ? new JValue(false) : new JValue(ExceptionStatus);
return dto;
}
@ -529,24 +536,127 @@ namespace BTCPayServer.Services.Invoices
}
#pragma warning restore CS0618
}
public InvoiceState GetInvoiceState()
{
return new InvoiceState(Status, ExceptionStatus);
}
}
public enum InvoiceStatus
{
New,
Paid,
Expired,
Invalid,
Complete,
Confirmed
}
public enum InvoiceExceptionStatus
{
None,
PaidLate,
PaidPartial,
Marked,
Invalid,
PaidOver
}
public class InvoiceState
{
static Dictionary<string, InvoiceStatus> _StringToInvoiceStatus;
static Dictionary<InvoiceStatus, string> _InvoiceStatusToString;
static Dictionary<string, InvoiceExceptionStatus> _StringToExceptionStatus;
static Dictionary<InvoiceExceptionStatus, string> _ExceptionStatusToString;
static InvoiceState()
{
_StringToInvoiceStatus = new Dictionary<string, InvoiceStatus>();
_StringToInvoiceStatus.Add("paid", InvoiceStatus.Paid);
_StringToInvoiceStatus.Add("expired", InvoiceStatus.Expired);
_StringToInvoiceStatus.Add("invalid", InvoiceStatus.Invalid);
_StringToInvoiceStatus.Add("complete", InvoiceStatus.Complete);
_StringToInvoiceStatus.Add("new", InvoiceStatus.New);
_StringToInvoiceStatus.Add("confirmed", InvoiceStatus.Confirmed);
_InvoiceStatusToString = _StringToInvoiceStatus.ToDictionary(kv => kv.Value, kv => kv.Key);
_StringToExceptionStatus = new Dictionary<string, InvoiceExceptionStatus>();
_StringToExceptionStatus.Add(string.Empty, InvoiceExceptionStatus.None);
_StringToExceptionStatus.Add("paidPartial", InvoiceExceptionStatus.PaidPartial);
_StringToExceptionStatus.Add("paidLate", InvoiceExceptionStatus.PaidLate);
_StringToExceptionStatus.Add("paidOver", InvoiceExceptionStatus.PaidOver);
_StringToExceptionStatus.Add("marked", InvoiceExceptionStatus.Marked);
_ExceptionStatusToString = _StringToExceptionStatus.ToDictionary(kv => kv.Value, kv => kv.Key);
_StringToExceptionStatus.Add("false", InvoiceExceptionStatus.None);
}
public InvoiceState(string status, string exceptionStatus)
{
Status = _StringToInvoiceStatus[status];
ExceptionStatus = _StringToExceptionStatus[exceptionStatus ?? string.Empty];
}
public InvoiceState(InvoiceStatus status, InvoiceExceptionStatus exceptionStatus)
{
Status = status;
ExceptionStatus = exceptionStatus;
}
public InvoiceStatus Status { get; }
public InvoiceExceptionStatus ExceptionStatus { get; }
public static string ToString(InvoiceStatus status)
{
return _InvoiceStatusToString[status];
}
public static string ToString(InvoiceExceptionStatus exceptionStatus)
{
return _ExceptionStatusToString[exceptionStatus];
}
public bool CanMarkComplete()
{
return (Status == InvoiceStatus.Paid) ||
#pragma warning disable CA1305 // Specify IFormatProvider
((Status == InvoiceStatus.New || Status == InvoiceStatus.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidPartial) ||
((Status == InvoiceStatus.New || Status == InvoiceStatus.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidLate) ||
(Status != InvoiceStatus.Complete && ExceptionStatus == InvoiceExceptionStatus.Marked) ||
(Status == InvoiceStatus.Invalid);
#pragma warning restore CA1305 // Specify IFormatProvider
}
public bool CanMarkInvalid()
{
return (Status == InvoiceStatus.Paid) ||
(Status == InvoiceStatus.New) ||
#pragma warning disable CA1305 // Specify IFormatProvider
((Status == InvoiceStatus.New || Status == InvoiceStatus.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidPartial) ||
((Status == InvoiceStatus.New || Status == InvoiceStatus.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidLate) ||
(Status != InvoiceStatus.Invalid && ExceptionStatus == InvoiceExceptionStatus.Marked);
#pragma warning restore CA1305 // Specify IFormatProvider;
}
public override string ToString()
{
return ToString(Status) + (ExceptionStatus == InvoiceExceptionStatus.None ? string.Empty : $" ({ToString(ExceptionStatus)})");
}
}
public class PaymentMethodAccounting
{
/// <summary>
/// Total amount of this invoice
/// </summary>
/// <summary>Total amount of this invoice</summary>
public Money TotalDue { get; set; }
/// <summary>
/// Amount of crypto remaining to pay this invoice
/// </summary>
/// <summary>Amount of crypto remaining to pay this invoice</summary>
public Money Due { get; set; }
/// <summary>
/// Same as Due, can be negative
/// </summary>
/// <summary>Same as Due, can be negative</summary>
public Money DueUncapped { get; set; }
/// <summary>If DueUncapped is negative, that means user overpaid invoice</summary>
public Money OverpaidHelper
{
get { return DueUncapped > Money.Zero ? Money.Zero : -DueUncapped; }
}
/// <summary>
/// Total amount of the invoice paid after conversion to this crypto currency
/// </summary>

View File

@ -126,7 +126,9 @@ namespace BTCPayServer.Services.Invoices
Created = invoice.InvoiceTime,
Blob = ToBytes(invoice, null),
OrderId = invoice.OrderId,
Status = invoice.Status,
#pragma warning disable CS0618 // Type or member is obsolete
Status = invoice.StatusString,
#pragma warning restore CS0618 // Type or member is obsolete
ItemCode = invoice.ProductInformation.ItemCode,
CustomerEmail = invoice.RefundMail
});
@ -316,15 +318,15 @@ namespace BTCPayServer.Services.Invoices
});
}
public async Task UpdateInvoiceStatus(string invoiceId, string status, string exceptionStatus)
public async Task UpdateInvoiceStatus(string invoiceId, InvoiceState invoiceState)
{
using (var context = _ContextFactory.CreateContext())
{
var invoiceData = await context.FindAsync<Data.InvoiceData>(invoiceId).ConfigureAwait(false);
if (invoiceData == null)
return;
invoiceData.Status = status;
invoiceData.ExceptionStatus = exceptionStatus;
invoiceData.Status = InvoiceState.ToString(invoiceState.Status);
invoiceData.ExceptionStatus = InvoiceState.ToString(invoiceState.ExceptionStatus);
await context.SaveChangesAsync().ConfigureAwait(false);
}
}
@ -334,13 +336,26 @@ namespace BTCPayServer.Services.Invoices
using (var context = _ContextFactory.CreateContext())
{
var invoiceData = await context.FindAsync<Data.InvoiceData>(invoiceId).ConfigureAwait(false);
if (invoiceData?.Status != "paid")
if (invoiceData == null || !invoiceData.GetInvoiceState().CanMarkInvalid())
return;
invoiceData.Status = "invalid";
invoiceData.ExceptionStatus = "marked";
await context.SaveChangesAsync().ConfigureAwait(false);
}
}
public async Task<InvoiceEntity> GetInvoice(string storeId, string id, bool inludeAddressData = false)
public async Task UpdatePaidInvoiceToComplete(string invoiceId)
{
using (var context = _ContextFactory.CreateContext())
{
var invoiceData = await context.FindAsync<Data.InvoiceData>(invoiceId).ConfigureAwait(false);
if (invoiceData == null || !invoiceData.GetInvoiceState().CanMarkComplete())
return;
invoiceData.Status = "complete";
invoiceData.ExceptionStatus = "marked";
await context.SaveChangesAsync().ConfigureAwait(false);
}
}
public async Task<InvoiceEntity> GetInvoice(string id, bool inludeAddressData = false)
{
using (var context = _ContextFactory.CreateContext())
{
@ -353,9 +368,6 @@ namespace BTCPayServer.Services.Invoices
query = query.Include(o => o.HistoricalAddressInvoices).Include(o => o.AddressInvoices);
query = query.Where(i => i.Id == id);
if (storeId != null)
query = query.Where(i => i.StoreDataId == storeId);
var invoice = await query.FirstOrDefaultAsync().ConfigureAwait(false);
if (invoice == null)
return null;
@ -375,8 +387,9 @@ namespace BTCPayServer.Services.Invoices
return paymentEntity;
}).ToList();
#pragma warning restore CS0618
entity.ExceptionStatus = invoice.ExceptionStatus;
entity.Status = invoice.Status;
var state = invoice.GetInvoiceState();
entity.ExceptionStatus = state.ExceptionStatus;
entity.Status = state.Status;
entity.RefundMail = invoice.CustomerEmail;
entity.Refundable = invoice.RefundAddresses.Count != 0;
if (invoice.HistoricalAddressInvoices != null)

View File

@ -50,7 +50,7 @@ namespace BTCPayServer.Services
_languages = result.ToArray();
}
private static DirectoryInfo TryGetSolutionDirectoryInfo(string currentPath = null)
public static DirectoryInfo TryGetSolutionDirectoryInfo(string currentPath = null)
{
var directory = new DirectoryInfo(
currentPath ?? Directory.GetCurrentDirectory());

View File

@ -39,7 +39,7 @@ namespace BTCPayServer.Services
public class LightningConfigurations
{
public List<LightningConfiguration> Configurations { get; set; } = new List<LightningConfiguration>();
public List<object> Configurations { get; set; } = new List<object>();
}
public class LightningConfiguration
{
@ -51,5 +51,16 @@ namespace BTCPayServer.Services
public bool SSL { get; set; }
public string CertificateThumbprint { get; set; }
public string Macaroon { get; set; }
public string RestrictedMacaroon { get; set; }
}
public class LNDRestConfiguration
{
public string ChainType { get; set; }
public string Type { get; set; }
public string CryptoCode { get; set; }
public string Uri { get; set; }
public string Macaroon { get; set; }
public string CertificateThumbprint { get; set; }
public string RestrictedMacaroon { get; set; }
}
}

View File

@ -61,7 +61,15 @@ namespace BTCPayServer.Services.Rates
currencyInfo.CurrencySymbol = currency;
return currencyInfo;
}
public NumberFormatInfo GetNumberFormatInfo(string currency)
{
var curr = GetCurrencyProvider(currency);
if (curr is CultureInfo cu)
return cu.NumberFormat;
if (curr is NumberFormatInfo ni)
return ni;
return null;
}
public IFormatProvider GetCurrencyProvider(string currency)
{
lock (_CurrencyProviders)

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
@ -9,6 +10,8 @@ namespace BTCPayServer.Services
public class ThemeSettings
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[MaxLength(500)]
[Display(Name = "Custom bootstrap CSS file")]
public string BootstrapCssUri { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]

View File

@ -122,7 +122,7 @@ namespace BTCPayServer.Services.Wallets
UTXOChanges result = null;
try
{
result = await _Client.GetUTXOsAsync(strategy, null, false, cancellation).ConfigureAwait(false);
result = await _Client.GetUTXOsAsync(strategy, cancellation).ConfigureAwait(false);
}
catch
{
@ -153,7 +153,7 @@ namespace BTCPayServer.Services.Wallets
public Task<GetTransactionsResponse> FetchTransactions(DerivationStrategyBase derivationStrategyBase)
{
return _Client.GetTransactionsAsync(derivationStrategyBase, null, false);
return _Client.GetTransactionsAsync(derivationStrategyBase);
}
public Task<BroadcastResult[]> BroadcastTransactionsAsync(List<Transaction> transactions)

View File

@ -29,10 +29,35 @@
<input asp-for="Currency" class="form-control" />
<span asp-validation-for="Currency" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="EnableShoppingCart"></label>
<input asp-for="EnableShoppingCart" type="checkbox" class="form-check" />
</div>
<div class="form-group">
<label asp-for="ShowCustomAmount"></label>
<input asp-for="ShowCustomAmount" type="checkbox" class="form-check" />
</div>
<div class="form-group">
<label asp-for="ButtonText" class="control-label"></label>*
<input asp-for="ButtonText" class="form-control" />
<span asp-validation-for="ButtonText" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CustomButtonText" class="control-label"></label>*
<input asp-for="CustomButtonText" class="form-control" />
<span asp-validation-for="CustomButtonText" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CustomTipText" class="control-label"></label>*
<input asp-for="CustomTipText" class="form-control" />
<span asp-validation-for="CustomTipText" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CustomCSSLink" class="control-label"></label>
<a href="https://docs.btcpayserver.org/development/theme#bootstrap-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="CustomCSSLink" class="form-control" />
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Template" class="control-label"></label>*
<textarea asp-for="Template" rows="20" cols="40" class="form-control"></textarea>
@ -41,12 +66,12 @@
<div class="form-group">
<h5>Host button externally</h5>
<p>You can host point of sale buttons in an external website with the following code.</p>
@if(Model.Example1 != null)
@if (Model.Example1 != null)
{
<span>For anything with a custom amount</span>
<pre><code class="html">@Model.Example1</code></pre>
}
@if(Model.Example2 != null)
@if (Model.Example2 != null)
{
<span>For a specific item of your template</span>
<pre><code class="html">@Model.Example2</code></pre>
@ -63,7 +88,7 @@
</p>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" />
<input type="submit" class="btn btn-primary" value="Save Settings" />
</div>
</form>
<a asp-action="ListApps">Back to the app list</a>

View File

@ -1,4 +1,5 @@
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@addTagHelper *, Meziantou.AspNetCore.BundleTagHelpers
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@model BTCPayServer.Models.AppViewModels.ViewPointOfSaleViewModel
@{
@ -14,43 +15,133 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href="@this.Context.Request.GetAbsoluteUri(themeManager.BootstrapUri)" rel="stylesheet" />
@if (Model.CustomCSSLink != null)
{
<link href="@Model.CustomCSSLink" rel="stylesheet" />
}
<link href="~/vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
@if (Model.EnableShoppingCart)
{
<script type="text/javascript">
var srvModel = @Html.Raw(Json.Serialize(Model));
</script>
<bundle name="wwwroot/bundles/cart-bundle.min.js" />
}
</head>
<body class="h-100">
@if (Model.EnableShoppingCart)
{
<div id="cartModal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Shopping cart</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<table id="js-cart-list" class="table mt-2 mb-3">
<thead class="thead-dark">
<tr>
<th colspan="2">Product</th>
<th class="text-right" width="80">Quantity</th>
<th class="text-right" width="25%">Price</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<form method="post" asp-antiforgery="false" data-buy>
<input id="js-cart-amount" class="form-control" type="hidden" name="amount">
<button id="js-cart-pay" class="btn btn-primary" type="submit"><b>@Model.CustomButtonText</b></button>
</form>
</div>
</div>
</div>
</div>
}
<div class="container d-flex h-100">
<div class="justify-content-center align-self-center text-center mx-auto" style="margin: auto;">
<div class="justify-content-center align-self-center text-center mx-auto px-2 py-3 w-100" style="margin: auto;">
<h1 class="mb-4">@Model.Title</h1>
<form method="post" asp-antiforgery="false">
<div class="row">
@for (int i = 0; i < Model.Items.Length; i++)
{
var className = (Model.Items.Length - i) > (Model.Items.Length % 3) ? "col-sm-4 mb-3" : "col align-self-center";
var item = Model.Items[i];
<div class="@className">
<h3>@item.Title</h3>
<button type="submit" name="choiceKey" class="btn btn-primary" value="@item.Id">Buy for @item.Price.Formatted</button>
@if (Model.EnableShoppingCart)
{
<a id="js-cart" class="btn btn-warning text-white text-right" href="#" data-toggle="modal" data-target="#cartModal"><i class="fa fa-shopping-basket"></i>&nbsp; <span class="badge badge-light badge-pill"><span id="js-cart-items">0</span></span></a>
}
<div class="row">
@for (int i = 0; i < Model.Items.Length; i++)
{
var className = (Model.Items.Length - i) > (Model.Items.Length % 4) ? "col-sm-6 col-lg-3" : "col-md align-self-start";
var item = Model.Items[i];
var image = item.Image;
var description = item.Description;
<div class="@className my-3 px-2">
<div class="card" data-id="@i">
@if (!String.IsNullOrWhiteSpace(image))
{
<img class="card-img-top" src="@image" alt="Card image cap">
}
<div class="card-body">
<h5 class="card-title">@item.Title</h5>
@if (!String.IsNullOrWhiteSpace(description))
{
<p class="card-text">@description</p>
}
@if (item.Custom && !Model.EnableShoppingCart)
{
<form method="post" asp-antiforgery="false" data-buy>
<input type="hidden" name="choicekey" value="@item.Id" />
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">@Model.CurrencySymbol</span>
</div>
<input class="form-control" type="number" min="@item.Price.Value" step="@Model.Step" name="amount"
value="@item.Price.Value" placeholder="Amount">
<div class="input-group-append">
<button class="btn btn-primary" type="submit">@Model.CustomButtonText</button>
</div>
</div>
</form>
}
else
{
<form method="post" asp-antiforgery="false">
<button type="submit" name="choiceKey" class="js-add-cart btn btn-primary" value="@item.Id">
@String.Format(Model.ButtonText, @item.Price.Formatted)</button>
</form>
}
</div>
</div>
}
</div>
</form>
</div>
}
</div>
@if (Model.ShowCustomAmount)
{
<div class="row mt-4">
<div class="col-sm-3">&nbsp;</div>
<div class="col-sm-6">
<form method="post" asp-antiforgery="false" data-buy>
<div class="input-group">
<input class="form-control" type="number" min="0" step="@Model.Step" name="amount" placeholder="amount"><div class="input-group-append">
<button class="btn btn-primary" type="submit">Pay</button>
</div>
<div class="row mt-2 mb-4">
<div class="col-lg-4 offset-lg-4 col-md-6 offset-md-3 px-2">
<div class="card">
<div class="card-body">
<h5 class="card-title">Custom Amount</h5>
<p class="card-text">Create invoice to pay custom amount</p>
<form method="post" asp-antiforgery="false" data-buy>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">@Model.CurrencySymbol</span>
</div>
<input class="form-control" type="number" min="0" step="@Model.Step" name="amount" placeholder="Amount">
<div class="input-group-append"><button class="btn btn-primary" type="submit">@Model.CustomButtonText</button></div>
</div>
</form>
</div>
</form>
</div>
</div>
<div class="col-sm-3">&nbsp;</div>
</div>
}
</div>
</div>
<script src="~/vendor/jquery/jquery.js"></script>
<script src="~/vendor/bootstrap4/js/bootstrap.js"></script>
</body>
</html>

View File

@ -0,0 +1,61 @@
@model BitpayTranslatorViewModel
<section>
<div class="container">
@if (Model.Address != null)
{
<div class="row">
<div class="col-lg-12 text-center">
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<p>You need to pay <b>@Model.Amount</b> to <b>@Model.Address</b></p>
<div id="qrCode"></div>
<div id="qrCodeData" data-url="@Html.Raw(Model.BitcoinUri)" style="margin-bottom:20px;"></div>
<p>
<a class="btn btn-primary" href="@Model.BitcoinUri">
<span>Open in wallet</span>
</a>
</p>
</div>
</div>
</div>
}
<div class="row">
<div class="col-lg-12 text-center">
<h2 class="section-heading">The Bitpay Translator</h2>
<hr class="primary">
<p>Bitpay is using deprecated standard in their invoices which multiple wallet do not support, use this transform their invoices to regular address/amount.</p>
</div>
</div>
<div class="row">
<div class="col-lg-4 text-center">&nbsp;</div>
<div class="col-lg-4 text-center">
<form method="post">
<div class="form-group">
<label asp-for="BitpayLink" class="control-label"></label>*
<input asp-for="BitpayLink" class="form-control" />
<span asp-validation-for="BitpayLink" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-secondary" title="Continue">Translate to address</button>
</div>
</form>
</div>
<div class="col-lg-4 text-center">&nbsp;</div>
</div>
</div>
</section>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript" src="~/js/qrcode.min.js"></script>
<script type="text/javascript">
new QRCode(document.getElementById("qrCode"),
{
text: "@Html.Raw(Model.BitcoinUri)",
width: 150,
height: 150
});
$("#qrCode > img").css({ "margin": "auto" });
</script>
}

View File

@ -296,8 +296,8 @@
</option>
</select>
</div>
<a v-on:click="openDialog($event)" :href="url" class="changelly-component-button">
<img src="https://changelly.com/pay_button.png" alt="Changelly" v-show="url"/>
<a v-on:click="openDialog($event)" :href="url" class="btn btn-primary retry-button changelly-component-button" v-show="url">
Pay with Changelly
</a>
<button class="retry-button" v-if="calculateError" v-on:click="retry('calculateAmount')">
{{$t("ConversionTab_CalculateAmount_Error")}}
@ -329,7 +329,7 @@
</div>
</div>
<div class="success-message">{{$t("This invoice has been paid")}}</div>
<a class="action-button" :href="srvModel.merchantRefLink" v-show="srvModel.merchantRefLink && !isModal">
<a class="action-button" :href="srvModel.merchantRefLink" v-show="!isModal">
<span>{{$t("Return to StoreName", srvModel)}}</span>
</a>
<button class="action-button close-action" v-show="isModal">
@ -376,7 +376,7 @@
{{srvModel.orderId}}
</div>
</div>
<a class="action-button" :href="srvModel.merchantRefLink" v-show="srvModel.merchantRefLink && !isModal">
<a class="action-button" :href="srvModel.merchantRefLink" v-show="!isModal">
<span>{{$t("Return to StoreName", srvModel)}}</span>
</a>
<button class="action-button close-action" v-show="isModal">

View File

@ -13,6 +13,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<META NAME="robots" CONTENT="noindex,nofollow">
<title>@Model.HtmlTitle</title>
<bundle name="wwwroot/bundles/checkout-bundle.min.css" />
@ -176,7 +177,7 @@
lndModel: null,
scanDisplayQr: "",
expiringSoon: false,
isModal: '@(Model.IsModal ? "true" : "false")'
isModal: srvModel.isModal
}
});
</script>

View File

@ -2,6 +2,14 @@
@{
ViewData["Title"] = "Create an invoice";
}
<script>
$(function() {
$("#create-invoice-form").on("submit",
function() {
$(this).find("input[type='submit']").prop('disabled', true);
});
})
</script>
<section>
<div class="container">
<div class="row">
@ -12,7 +20,7 @@
</div>
<div class="row">
<div class="col-lg-12">
<form asp-action="CreateInvoice" method="post">
<form asp-action="CreateInvoice" method="post" id="create-invoice-form">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Amount" class="control-label"></label>*

View File

@ -2,6 +2,9 @@
@{
ViewData["Title"] = "Invoice " + Model.Id;
}
@section HeaderContent{
<META NAME="robots" CONTENT="noindex,nofollow">
}
<style type="text/css">
.linethrough {
@ -39,110 +42,130 @@
</div>
</div>
<div class="row">
<div class="col-md-6">
<h3>Information</h3>
<table class="table table-sm table-responsive-md">
<tr>
<th>Store</th>
<td><a href="@Model.StoreLink">@Model.StoreName</a></td>
</tr>
<tr>
<th>Id</th>
<td>@Model.Id</td>
</tr>
<tr>
<th>State</th>
<td>@Model.State</td>
</tr>
<tr>
<th>Created date</th>
<td>@Model.CreatedDate.ToBrowserDate()</td>
</tr>
<tr>
<th>Expiration date</th>
<td>@Model.ExpirationDate.ToBrowserDate()</td>
</tr>
<tr>
<th>Monitoring date</th>
<td>@Model.MonitoringDate.ToBrowserDate()</td>
</tr>
<tr>
<th>Transaction speed</th>
<td>@Model.TransactionSpeed</td>
</tr>
<tr>
<th>Refund email</th>
<td><a href="mailto:@Model.RefundEmail">@Model.RefundEmail</a></td>
</tr>
<tr>
<th>Order Id</th>
<td>@Model.OrderId</td>
</tr>
<tr>
<th>Total fiat due</th>
<td>@Model.Fiat</td>
</tr>
<tr>
<th>Notification Email</th>
<td>@Model.NotificationEmail</td>
</tr>
<tr>
<th>Notification Url</th>
<td>@Model.NotificationUrl</td>
</tr>
<tr>
<th>Redirect Url</th>
<td><a href="@Model.RedirectUrl">@Model.RedirectUrl</a></td>
</tr>
</table>
</div>
<div class="col-md-6">
<h3>Buyer information</h3>
<table class="table table-sm table-responsive-md">
<tr>
<th>Name</th>
<td>@Model.BuyerInformation.BuyerName</td>
</tr>
<tr>
<th>Email</th>
<td><a href="mailto:@Model.BuyerInformation.BuyerEmail">@Model.BuyerInformation.BuyerEmail</a></td>
</tr>
<tr>
<th>Phone</th>
<td>@Model.BuyerInformation.BuyerPhone</td>
</tr>
<tr>
<th>Address 1</th>
<td>@Model.BuyerInformation.BuyerAddress1</td>
</tr>
<tr>
<th>Address 2</th>
<td>@Model.BuyerInformation.BuyerAddress2</td>
</tr>
<tr>
<th>City</th>
<td>@Model.BuyerInformation.BuyerCity</td>
</tr>
<tr>
<th>State</th>
<td>@Model.BuyerInformation.BuyerState</td>
</tr>
<tr>
<th>Country</th>
<td>@Model.BuyerInformation.BuyerCountry</td>
</tr>
<tr>
<th>Zip</th>
<td>@Model.BuyerInformation.BuyerZip</td>
</tr>
</table>
@if (Model.PosData.Count == 0)
{
<h3>Product information</h3>
<table class="table table-sm table-responsive-md">
<tr>
<th>Item code</th>
<td>@Model.ProductInformation.ItemCode</td>
</tr>
<tr>
<th>Item Description</th>
<td>@Model.ProductInformation.ItemDesc</td>
</tr>
<tr>
<th>Price</th>
<td>@Model.ProductInformation.Price @Model.ProductInformation.Currency</td>
</tr>
</table>
}
</div>
</div>
@if (Model.PosData.Count != 0)
{
<div class="row">
<div class="col-md-6">
<h3>Information</h3>
<table class="table table-sm table-responsive-md">
<tr>
<th>Store</th>
<td><a href="@Model.StoreLink">@Model.StoreName</a></td>
</tr>
<tr>
<th>Id</th>
<td>@Model.Id</td>
</tr>
<tr>
<th>Created date</th>
<td>@Model.CreatedDate.ToBrowserDate()</td>
</tr>
<tr>
<th>Expiration date</th>
<td>@Model.ExpirationDate.ToBrowserDate()</td>
</tr>
<tr>
<th>Monitoring date</th>
<td>@Model.MonitoringDate.ToBrowserDate()</td>
</tr>
<tr>
<th>Transaction speed</th>
<td>@Model.TransactionSpeed</td>
</tr>
<tr>
<th>Status</th>
<td>@Model.Status</td>
</tr>
<tr>
<th>Status Exception</th>
<td>@Model.StatusException</td>
</tr>
<tr>
<th>Refund email</th>
<td><a href="mailto:@Model.RefundEmail">@Model.RefundEmail</a></td>
</tr>
<tr>
<th>Order Id</th>
<td>@Model.OrderId</td>
</tr>
<tr>
<th>Total fiat due</th>
<td>@Model.Fiat</td>
</tr>
<tr>
<th>Notification Email</th>
<td>@Model.NotificationEmail</td>
</tr>
<tr>
<th>Notification Url</th>
<td>@Model.NotificationUrl</td>
</tr>
<tr>
<th>Redirect Url</th>
<td><a href="@Model.RedirectUrl">@Model.RedirectUrl</a></td>
</tr>
</table>
</div>
<div class="col-md-6">
<h3>Buyer information</h3>
<table class="table table-sm table-responsive-md">
<tr>
<th>Name</th>
<td>@Model.BuyerInformation.BuyerName</td>
</tr>
<tr>
<th>Email</th>
<td><a href="mailto:@Model.BuyerInformation.BuyerEmail">@Model.BuyerInformation.BuyerEmail</a></td>
</tr>
<tr>
<th>Phone</th>
<td>@Model.BuyerInformation.BuyerPhone</td>
</tr>
<tr>
<th>Address 1</th>
<td>@Model.BuyerInformation.BuyerAddress1</td>
</tr>
<tr>
<th>Address 2</th>
<td>@Model.BuyerInformation.BuyerAddress2</td>
</tr>
<tr>
<th>City</th>
<td>@Model.BuyerInformation.BuyerCity</td>
</tr>
<tr>
<th>State</th>
<td>@Model.BuyerInformation.BuyerState</td>
</tr>
<tr>
<th>Country</th>
<td>@Model.BuyerInformation.BuyerCountry</td>
</tr>
<tr>
<th>Zip</th>
<td>@Model.BuyerInformation.BuyerZip</td>
</tr>
</table>
<h3>Product information</h3>
<table class="table table-sm table-responsive-md">
<tr>
@ -159,8 +182,31 @@
</tr>
</table>
</div>
<div class="col-md-6">
<h3>Point of Sale Data</h3>
<table class="table table-sm table-responsive-md">
@foreach (var posDataItem in Model.PosData)
{
<tr>
@if (!string.IsNullOrEmpty(posDataItem.Key))
{
<th>@posDataItem.Key</th>
<td>@posDataItem.Value</td>
}
else
{
<td colspan="2">@posDataItem.Value</td>
}
</tr>
}
</table>
</div>
</div>
<div class="row">
}
<div class="row">
<div class="col-md-12">
<h3>Paid summary</h3>
<table class="table table-sm table-responsive-md">
@ -171,7 +217,7 @@
<th class="text-right">Rate</th>
<th class="text-right">Paid</th>
<th class="text-right">Due</th>
@if (Model.StatusException == "paidOver")
@if (Model.StatusException == InvoiceExceptionStatus.PaidOver)
{
<th class="text-right">Overpaid</th>
}
@ -186,7 +232,7 @@
<td class="text-right">@payment.Rate</td>
<td class="text-right">@payment.Paid</td>
<td class="text-right">@payment.Due</td>
@if (Model.StatusException == "paidOver")
@if (Model.StatusException == InvoiceExceptionStatus.PaidOver)
{
<td class="text-right">@payment.Overpaid</td>
}
@ -255,29 +301,6 @@
</div>
</div>
}
<div class="row">
<div class="col-md-12">
<h3>Addresses</h3>
<table class="table table-sm table-responsive-md">
<thead class="thead-inverse">
<tr>
<th class="firstCol">Payment method</th>
<th>Address</th>
</tr>
</thead>
<tbody>
@foreach (var address in Model.Addresses)
{
var current = address.Current ? "font-weight-bold" : "";
<tr>
<td>@address.PaymentMethod</td>
<td class="smMaxWidth text-truncate @current">@address.Destination</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-12">

View File

@ -6,10 +6,6 @@
@section HeadScripts {
<script src="~/modal/btcpay.js"></script>
<script type="text/javascript">
btcpay.setApiUrlPrefix("@rootUrl");
</script>
}
<section>
@ -45,11 +41,19 @@
</div>
<div class="row no-gutter" style="margin-bottom: 5px;">
<div class="col-lg-4">
<div class="col-lg-6">
<a asp-action="CreateInvoice" class="btn btn-primary" role="button"><span class="fa fa-plus"></span> Create a new invoice</a>
<a class="btn btn-primary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Export
</a>
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
<a asp-action="Export" asp-route-format="csv" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item" target="_blank">CSV</a>
<a asp-action="Export" asp-route-format="json" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item" target="_blank">JSON</a>
</div>
</div>
<div class="col-lg-8">
<div class="col-lg-6">
<div class="form-group">
<form asp-action="SearchInvoice" method="post" style="float:right;">
<div class="input-group">
@ -95,23 +99,7 @@
}
</td>
<td>@invoice.InvoiceId</td>
@if (invoice.Status == "paid")
{
<td>
<div class="btn-group">
<a class="dropdown-toggle dropdown-toggle-split" style="cursor: pointer;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@invoice.Status <span class="sr-only">Toggle Dropdown</span>
</a>
<div class="dropdown-menu pull-right">
<button class="dropdown-item small" data-toggle="modal" data-target="#myModal" onclick="$('#invoiceId').val('@invoice.InvoiceId')">Make Invalid</button>
</div>
</div>
</td>
}
else
{
<td>@invoice.Status</td>
}
<td>@invoice.Status</td>
<td style="text-align:right">@invoice.AmountCurrency</td>
<td style="text-align:right">
@if (invoice.ShowCheckout)
@ -119,9 +107,32 @@
<span>
<a asp-action="Checkout" asp-route-invoiceId="@invoice.InvoiceId">Checkout</a>
<a href="javascript:btcpay.showInvoice('@invoice.InvoiceId')">[^]</a>
-
@if (!invoice.CanMarkStatus)
{
<span>-</span>
}
</span>
}
@if (invoice.CanMarkStatus)
{
<a class="dropdown-toggle dropdown-toggle-split" href="#" style="cursor: pointer;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Change status <span class="sr-only">Toggle Dropdown</span>
</a>
<div class="dropdown-menu pull-right">
@if (invoice.CanMarkInvalid)
{
<form method="get" asp-action="ChangeInvoiceState" asp-route-invoiceId="@invoice.InvoiceId" asp-route-newState="invalid">
<button class="dropdown-item small">Mark as invalid <span class="fa fa-times"></span></button>
</form>
}
@if (invoice.CanMarkComplete)
{
<form method="get" asp-action="ChangeInvoiceState" asp-route-invoiceId="@invoice.InvoiceId" asp-route-newState="complete">
<button class="dropdown-item small">Mark as complete <span class="fa fa-check-circle"></span></button>
</form>
}
</div>
}
<a asp-action="Invoice" asp-route-invoiceId="@invoice.InvoiceId">Details</a>
</td>
</tr>
@ -149,28 +160,3 @@
</div>
</section>
<!-- Modal -->
<div id="myModal" class="modal fade" role="dialog">
<form method="post" action="/invoices/invalidatepaid">
<input id="invoiceId" name="invoiceId" type="hidden" />
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Set Invoice status to Invalid</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>Are you sure you want to invalidate this transaction? This action is NOT undoable!</p>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-danger">Yes, make invoice Invalid</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</form>
</div>

View File

@ -0,0 +1 @@
@using BTCPayServer.Services.Invoices

View File

@ -32,6 +32,8 @@
<div class="row">
<div class="col-md-6">
<form method="post">
<input asp-for="AuthenticatorUri" type="hidden" />
<input asp-for="SharedKey" type="hidden" />
<div class="form-group">
<label asp-for="Code" class="control-label">Verification Code</label>
<input asp-for="Code" class="form-control" autocomplete="off" />

View File

@ -1,41 +0,0 @@
@model LndRestServicesViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h4>LND REST</h4>
<partial name="_StatusMessage" for="@TempData["StatusMessage"]" />
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<p>BTCPay exposes LND Rest services for outside consumption. See connection information below</p>
</div>
<div class="form-group">
<label asp-for="BaseApiUrl">Base API Url</label>
<input asp-for="BaseApiUrl" readonly class="form-control" />
</div>
@if (Model.Macaroon != null)
{
<div class="form-group">
<label asp-for="Macaroon"></label>
<input asp-for="Macaroon" readonly class="form-control" />
</div>
}
@if (Model.CertificateThumbprint != null)
{
<div class="form-group">
<label asp-for="CertificateThumbprint"></label>
<input asp-for="CertificateThumbprint" readonly class="form-control" />
</div>
}
</div>
</div>

View File

@ -4,7 +4,7 @@
}
<h4>LND GRPC</h4>
<h4>LND @Model.ConnectionType</h4>
<partial name="_StatusMessage" for="@TempData["StatusMessage"]" />
<div class="row">
<div class="col-md-6">
@ -17,14 +17,14 @@
<div class="col-md-8">
<div class="form-group">
<p>
<span>BTCPay exposes gRPC services for outside consumption, you will find connection information here.<br /></span>
<span>BTCPay exposes LND's @Model.ConnectionType service for outside consumption, you will find connection information here.<br /></span>
</p>
</div>
<div class="form-group">
<h5>QR Code connection</h5>
<p>
<span>You can use this QR Code to connect your Zap wallet to your LND instance.<br /></span>
<span>You can use this QR Code to connect external software to your LND instance.<br /></span>
<span>This QR Code is only valid for 10 minutes</span>
</p>
</div>
@ -61,22 +61,39 @@
<p>Alternatively, you can see the settings by clicking <a href="#details" data-toggle="collapse">here</a></p>
</div>
<div id="details" class="collapse">
<div class="form-group">
<label asp-for="Host"></label>
<input asp-for="Host" readonly class="form-control" />
</div>
<div class="form-group">
<label asp-for="SSL"></label>
<input asp-for="SSL" disabled type="checkbox" class="form-check-inline" />
</div>
@if(Model.Macaroon != null)
@if (Model.Uri == null)
{
<div class="form-group">
<label asp-for="Host"></label>
<input asp-for="Host" readonly class="form-control" />
</div>
<div class="form-group">
<label asp-for="SSL"></label>
<input asp-for="SSL" disabled type="checkbox" class="form-check-inline" />
</div>
}
else
{
<div class="form-group">
<label asp-for="Uri"></label>
<input asp-for="Uri" readonly class="form-control" />
</div>
}
@if (Model.Macaroon != null)
{
<div class="form-group">
<label asp-for="Macaroon"></label>
<input asp-for="Macaroon" readonly class="form-control" />
</div>
}
@if(Model.CertificateThumbprint != null)
@if (Model.RestrictedMacaroon != null)
{
<div class="form-group">
<label asp-for="RestrictedMacaroon"></label>
<input asp-for="RestrictedMacaroon" readonly class="form-control" />
</div>
}
@if (Model.CertificateThumbprint != null)
{
<div class="form-group">
<label asp-for="CertificateThumbprint"></label>

View File

@ -19,7 +19,7 @@
{
<a asp-action="LogsView" asp-route-offset="@(Model.LogFileOffset - 5)"><<</a>
}
Showing @Model.LogFileOffset - (@Model.LogFileOffset+@Model.LogFiles.Count) of @Model.LogFileCount
Showing @Model.LogFileOffset - (@(Model.LogFileOffset+Model.LogFiles.Count)) of @Model.LogFileCount
@if ((Model.LogFileOffset+ Model.LogFiles.Count) < Model.LogFileCount)
{
<a asp-action="LogsView" asp-route-offset="@(Model.LogFileOffset + Model.LogFiles.Count)">>></a>

View File

@ -15,8 +15,9 @@
<div class="row">
<div class="col-md-8">
<h4>Crypto services</h4>
<div class="form-group">
<span>You can get access here to LND (gRPC, Rest) or SSH services exposed by your server</span>
<span>You can get access here to LND (gRPC, Rest) services exposed by your server</span>
</div>
<div class="form-group">
@ -37,31 +38,54 @@
<td style="text-align:right">
@if (lnd.Type == BTCPayServer.Configuration.External.LndTypes.gRPC)
{
<a asp-action="LNDGRPCServices" asp-route-cryptoCode="@lnd.Crypto" asp-route-index="@lnd.Index">See information</a>
<a asp-action="LNDServices" asp-route-cryptoCode="@lnd.Crypto" asp-route-index="@lnd.Index">See information</a>
}
else if (lnd.Type == BTCPayServer.Configuration.External.LndTypes.Rest)
{
<a asp-action="LndRestServices" asp-route-cryptoCode="@lnd.Crypto" asp-route-index="@lnd.Index">See information</a>
<a asp-action="LNDServices" asp-route-cryptoCode="@lnd.Crypto" asp-route-index="@lnd.Index">See information</a>
}
</td>
</tr>
}
@if (Model.HasSSH)
{
<tr>
<td>None</td>
<td>SSH</td>
<td style="text-align:right">
<a asp-action="SSHService">See information</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
@if (Model.ExternalServices.Count != 0)
{
<div class="row">
<div class="col-md-8">
<h4>Other services</h4>
<div class="form-group">
<span>Other external services</span>
</div>
<div class="form-group">
<table class="table table-sm table-responsive-md">
<thead>
<tr>
<th>Name</th>
<th style="text-align:right">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var s in Model.ExternalServices)
{
<tr>
<td>@s.Name</td>
<td style="text-align:right">
<a href="@s.Link" target="_blank">See information</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -16,6 +16,7 @@
<form method="post">
<div class="form-group">
<label asp-for="BootstrapCssUri"></label>
<a href="https://docs.btcpayserver.org/development/theme#bootstrap-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="BootstrapCssUri" class="form-control" />
<span asp-validation-for="BootstrapCssUri" class="text-danger"></span>
<p class="form-text text-muted">

View File

@ -28,6 +28,7 @@
<bundle name="wwwroot/bundles/main-bundle.min.js" />
@RenderSection("HeadScripts", required: false)
@RenderSection("HeaderContent", false)
</head>
<body id="page-top">

View File

@ -15,7 +15,7 @@
<div class="row">
<div class="col-md-8">
<form method="post">
@if(!Model.Confirmation)
@if (!Model.Confirmation)
{
<div class="form-group">
<h5>Derivation Scheme</h5>
@ -26,13 +26,19 @@
<label asp-for="DerivationScheme"></label>
<input asp-for="DerivationScheme" class="form-control" />
<span asp-validation-for="DerivationScheme" class="text-danger"></span>
<div id="ledger-loading" class="form-text text-muted">
Checking if a ledger wallet is connected...
</div>
<div id="ledger-validate" class="form-text text-muted" style="display: none;">
Please validate access on your screen...
</div>
<p id="no-ledger-info" class="form-text text-muted" style="display: none;">
No ledger wallet detected. If you own one, use chrome, open the app, and refresh this page.
</p>
<div id="ledger-info" class="form-text text-muted" style="display: none;">
<span>A ledger wallet is detected, which account do you want to use?</span>
<ul>
@for(int i = 0; i < 4; i++)
@for (int i = 0; i < 4; i++)
{
<li><a class="ledger-info-recommended" data-ledgeraccount="@i" href="#">Account @i (<span>@Model.RootKeyPath.Derive(i, true)</span>)</a></li>
}
@ -100,7 +106,7 @@
</tr>
</thead>
<tbody>
@foreach(var sample in Model.AddressSamples)
@foreach (var sample in Model.AddressSamples)
{
<tr>
<td>@sample.KeyPath</td>

View File

@ -7,6 +7,24 @@
<h4>@ViewData["Title"]</h4>
<partial name="_StatusMessage" for="StatusMessage" />
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<p>
<span>Before you proceed, please understand that Lightning Network is still in the experimental stage. Do not put the money you can't afford to lose. There is a high risk of you losing the money.</span>
</p>
<p>
Take time to familiarize yourself with the risk. There's no backup for LND or c-lightning keys in BTCPay. Your keys are in a hot-wallet. This means :
</p>
<ul>
<li>Most of BTCPay Server deployments run on a pruned node, which, while working, is not officially supported by lightning network implementations.</li>
<li>If you erase your BTCPay Server virtual machine - you lose all the funds.</li>
<li>If your server gets hacked - a hacker can take all of your funds by accessing your keys.</li>
<li>If there is a bug in a lightning network implementation - you might lose all the funds.</li>
<li>You approve being #reckless and being the sole responsible party for your loss.</li>
<li>You approve to keep on your lightning node only what you can afford to lose.</li>
</ul>
</div>
<div class="row">
<div class="col-md-10">
<form method="post">
@ -54,7 +72,7 @@
<label asp-for="ConnectionString"></label>
<input id="lightningurl" asp-for="ConnectionString" class="form-control" />
<span asp-validation-for="ConnectionString" class="text-danger"></span>
@if(Model.InternalLightningNode != null)
@if (Model.InternalLightningNode != null)
{
<p class="form-text text-muted">
You can use the internal lightning node by <a href="#" onclick="$('#lightningurl').val('@Model.InternalLightningNode'); return false;">clicking here</a>

View File

@ -44,29 +44,35 @@
</div>
<div class="form-group">
<label asp-for="NetworkFee"></label>
<a href="https://docs.btcpayserver.org/faq-and-common-issues/faq-stores#add-network-fee-to-invoice-vary-with-mining-fees" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="NetworkFee" type="checkbox" class="form-check" />
</div>
<div class="form-group">
<label asp-for="AnyoneCanCreateInvoice"></label>
<a href="https://docs.btcpayserver.org/faq-and-common-issues/faq-stores#allow-anyone-to-create-invoice" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="AnyoneCanCreateInvoice" type="checkbox" class="form-check" />
</div>
<div class="form-group">
<label asp-for="InvoiceExpiration"></label>
<a href="https://docs.btcpayserver.org/faq-and-common-issues/faq-stores#invoice-expires-if-the-full-amount-has-not-been-paid-after-minutes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="InvoiceExpiration" class="form-control" />
<span asp-validation-for="InvoiceExpiration" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MonitoringExpiration"></label>
<a href="https://docs.btcpayserver.org/faq-and-common-issues/faq-stores#payment-invalid-if-transactions-fails-to-confirm-minutes-after-invoice-expiration" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="MonitoringExpiration" class="form-control" />
<span asp-validation-for="MonitoringExpiration" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PaymentTolerance"></label>
<a href="https://docs.btcpayserver.org/faq-and-common-issues/faq-stores#consider-the-invoice-paid-even-if-the-paid-amount-is-less-than-expected" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="PaymentTolerance" class="form-control" />
<span asp-validation-for="PaymentTolerance" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SpeedPolicy"></label>
<a href="https://docs.btcpayserver.org/faq-and-common-issues/faq-stores#consider-the-invoice-confirmed-when-the-payment-transaction" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<select asp-for="SpeedPolicy" class="form-control">
<option value="0">Is unconfirmed</option>
<option value="1">Has at least 1 confirmation</option>

View File

@ -52,5 +52,14 @@
"wwwroot/vendor/vex/js/vex.combined.min.js",
"wwwroot/checkout/**/*.js"
]
},
{
"outputFileName": "wwwroot/bundles/cart-bundle.min.js",
"inputFiles": [
"wwwroot/vendor/jquery/jquery.js",
"wwwroot/vendor/bootstrap4/js/bootstrap.js",
"wwwroot/cart/js/cart.js",
"wwwroot/cart/js/cart.jquery.js"
]
}
]

View File

@ -0,0 +1,57 @@
$.fn.addAnimate = function(completeCallback) {
var documentHeight = $(document).height(),
itemPos = $(this).offset(),
itemY = itemPos.top,
cartPos = $('#js-cart').find('.badge').position();
tempItem = '<span id="js-cart-temp-item" class="badge badge-primary text-white badge-pill " style="' +
'position: absolute;' +
'top: ' + itemPos.top + 'px;' +
'left: ' + (itemPos.left + 50) + 'px;">'+
'<i class="fa fa-shopping-basket"></i></span>';
// Make animation speed look constant regardless of how far the object is from the cart
var animationSpeed = (Math.log(itemY) * (documentHeight / Math.log2(documentHeight - itemY))) / 2;
// Add the cart item badge and animate it
$('body').after(tempItem);
$('#js-cart-temp-item').animate({
easing: 'swing',
top: cartPos.top,
left: cartPos.left
}, animationSpeed, function() {
$(this).remove();
completeCallback && completeCallback();
});
};
$(document).ready(function(){
var cart = new Cart();
$('.js-add-cart').click(function(event){
event.preventDefault();
var $btn = $(event.target),
id = $btn.closest('.card').data('id'),
item = srvModel.items[id];
// Animate adding and then add then save
$(this).addAnimate(function(){
cart.addItem({
id: id,
title: item.title,
price: item.price,
image: typeof item.image != 'underfined' ? item.image : null
});
});
});
// Destroy the cart when the "pay button is clicked"
$('#js-cart-pay').click(function(){
cart.destroy();
});
// Repopulate cart items in the modal when it opens
$('#cartModal').on('show.bs.modal', function () {
cart.listItems();
});
});

View File

@ -0,0 +1,305 @@
function Cart() {
this.items = 0;
this.totalAmount = 0;
this.content = [];
this.tip = 0;
this.loadLocalStorage();
this.itemsCount();
this.listItems();
this.updateAmount();
}
Cart.prototype.addItem = function(item) {
// Increment the existing item count
var result = this.content.filter(function(obj){
if (obj.id === item.id){
obj.count++;
}
return obj.id === item.id
});
// Add new item because it doesn't exist yet
if (!result.length) {
this.content.push({id: item.id, title: item.title, price: item.price, count: 1, image: item.image})
}
this.items++;
this.saveLocalStorage();
this.itemsCount();
this.updateTotal();
this.updateAmount();
}
Cart.prototype.decrementItem = function(id) {
var self = this;
// Decrement the existing item count
this.content.filter(function(obj, index, arr){
if (obj.id === id)
{
obj.count--;
// It's the last item with the same ID, remove it
if (obj.count === 0) {
self.removeItem(id, index, arr);
}
}
});
this.items--;
this.saveLocalStorage();
this.itemsCount();
this.updateTotal();
this.updateAmount();
if (this.items === 0) {
this.emptyList();
}
}
Cart.prototype.removeItemAll = function(id) {
var self = this;
this.content.filter(function(obj, index, arr){
if (obj.id === id)
{
self.removeItem(id, index, arr);
for (var i = 0; i < obj.count; i++) {
self.items--;
}
}
});
this.saveLocalStorage();
this.itemsCount();
this.updateTotal();
this.updateAmount();
if (this.items === 0) {
this.emptyList();
}
}
Cart.prototype.removeItem = function(id, index, arr) {
// Remove from the array
arr.splice(index, 1);
// Remove from the DOM
$('#js-cart-list').find('tr').eq(index+1).remove();
}
Cart.prototype.setTip = function(tip) {
return this.tip = tip;
}
Cart.prototype.itemsCount = function() {
$('#js-cart-items').text(this.items);
}
Cart.prototype.getTotal = function(plain) {
this.totalAmount = 0;
// Always calculate the total amount based on the cart content
for (var key in this.content) {
if (this.content.hasOwnProperty(key) && typeof this.content[key] != 'undefined') {
var price = this.toCents(this.content[key].price.value);
this.totalAmount += (this.content[key].count * price);
}
}
this.totalAmount += this.toCents(this.tip);
return this.fromCents(this.totalAmount);
}
Cart.prototype.updateTotal = function() {
$('#js-cart-total').text(this.formatCurrency(this.getTotal(), srvModel.currencyCode, srvModel.currencySymbol));
}
Cart.prototype.updateAmount = function() {
$('#js-cart-amount').val(this.getTotal());
}
Cart.prototype.escape = function(input) {
return ('' + input) /* Forces the conversion to string. */
.replace(/&/g, '&amp;') /* This MUST be the 1st replacement. */
.replace(/'/g, '&apos;') /* The 4 other predefined entities, required. */
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
;
}
Cart.prototype.listItems = function() {
var $table = $('#js-cart-list').find('tbody'),
self = this,
list = [],
tableTemplate = '';
if (this.content.length > 0) {
// Prepare the list of items in the cart
for (var key in this.content) {
var item = this.content[key],
id = this.escape(item.id),
title = this.escape(item.title),
image = this.escape(item.image),
count = this.escape(item.count),
price = this.escape(item.price.formatted),
total = this.escape(this.formatCurrency(this.getTotal(), srvModel.currencyCode, srvModel.currencySymbol)),
step = this.escape(srvModel.step),
tip = this.escape(this.tip || ''),
customTipText = this.escape(srvModel.customTipText);
tableTemplate = '<tr data-id="' + id + '">' +
(image !== null ? '<td class="align-middle pr-0" width="60"><img src="' + image + '" width="100%"></td>' : '') +
'<td class="align-middle pr-0"><b>' + title + '</b></td>' +
'<td class="align-middle pr-0" align="right"><div class="input-group">' +
' <input class="js-cart-item-count form-control form-control-sm pull-left" type="number" min="0" step="1" name="count" placeholder="Qty" value="' + count + '" data-prev="' + count + '">' +
' <div class="input-group-append"><a class="js-cart-item-remove btn btn-danger btn-sm" href="#"><i class="fa fa-remove"></i></a></div>' +
'</div></td>' +
'<td class="align-middle" align="right">' + price + '</td>' +
'</tr>';
list.push($(tableTemplate));
}
tableTemplate = '<tr><td colspan="4"><div class="row"><div class="col-sm-7 py-2">' + customTipText + '</div><div class="col-sm-5">' +
'<div class="input-group">' +
'<div class="input-group-prepend">' +
'<span class="input-group-text"><i class="fa fa-money"></i></span>' +
'</div>' +
'<input class="js-cart-tip form-control" type="number" min="0" step="' + step + '" value="' + tip + '" name="tip" placeholder="Amount">' +
'</div>' +
'</div></div></td></tr>';
list.push($(tableTemplate));
tableTemplate = '<tr class="bg-light h4"><td colspan="1">Total</td><td colspan="3" align="right"><span id="js-cart-total">' + total + '</span></td></tr>';
list.push($(tableTemplate));
// Add the list to DOM
$table.html(list);
// Update the cart when number of items is changed
$('.js-cart-item-count').off().on('input', function(event){
var _this = this,
id = $(this).closest('tr').data('id'),
count = parseInt($(this).val()),
prevCount = parseInt($(this).data('prev')),
increased = count > prevCount;
// User hasn't inputed any number so stop here
if (isNaN(count)) {
return false;
}
$(this).data('prev', count);
var item = self.content.filter(function(obj){
return obj.id === id
});
// Must be in the loop because user may change the count manually by more than 1
for (var i = 0; i < Math.abs(count - prevCount); i++) {
if (increased) {
self.addItem({
id: id,
title: item.title,
price: item.price,
image: typeof item.image != 'underfined' ? item.image : null
});
} else {
self.decrementItem(id);
}
}
});
// Remove item from the cart
$('.js-cart-item-remove').off().on('click', function(event){
event.preventDefault();
var id = $(this).closest('tr').data('id');
self.removeItemAll(id);
});
// Change total when tip is changed
$('.js-cart-tip').off().on('input', function(event){
self.setTip($(this).val());
self.updateTotal();
self.updateAmount();
});
} else { // No item in the cart
self.emptyList();
}
}
Cart.prototype.emptyList = function() {
var $table = $('#js-cart-list').find('tbody');
$table.html('<tr><td colspan="4">The cart is empty.</td></tr>');
}
Cart.prototype.formatCurrency = function(amount, currency, symbol) {
var amt = '',
thousandsSep = '',
decimalSep = ''
prefix = '',
postfix = '';
if (srvModel.currencyInfo.prefixed) {
prefix = srvModel.currencyInfo.currencySymbol;
}
else {
postfix = srvModel.currencyInfo.currencySymbol;
}
thousandsSep = srvModel.currencyInfo.thousandSeparator;
decimalSep = srvModel.currencyInfo.decimalSeparator;
amt = amount.toFixed(srvModel.currencyInfo.divisibility);
// Add currency sign and thousands separator
var splittedAmount = amt.split('.');
amt = (splittedAmount[0] + '.').replace(/(\d)(?=(\d{3})+\.)/g, '$1' + thousandsSep);
amt = amt.substr(0, amt.length - 1);
if(splittedAmount.length == 2)
amt = amt + '.' + splittedAmount[1];
if (srvModel.currencyInfo.divisibility !== 0) {
amt[amt.length - srvModel.currencyInfo.divisibility - 1] = decimalSep;
}
amt = prefix + amt + postfix;
return amt;
}
Cart.prototype.toCents = function(num) {
return num * Math.pow(10, srvModel.currencyInfo.divisibility);
}
Cart.prototype.fromCents = function(num) {
return num / Math.pow(10, srvModel.currencyInfo.divisibility);
}
Cart.prototype.getStorageKey = function () { return ('cart' + srvModel.appId + srvModel.currencyCode); }
Cart.prototype.saveLocalStorage = function() {
localStorage.setItem(this.getStorageKey(), JSON.stringify(this.content));
}
Cart.prototype.loadLocalStorage = function() {
this.content = $.parseJSON(localStorage.getItem(this.getStorageKey())) || [];
// Get number of cart items
for (var key in this.content) {
if (this.content.hasOwnProperty(key) && typeof this.content[key] != 'undefined' && this.content[key] != null) {
this.items += this.content[key].count;
}
}
}
Cart.prototype.destroy = function() {
localStorage.removeItem(this.getStorageKey());
this.content = [];
this.items = 0;
this.totalAmount = 0;
this.tip = 0;
}

View File

@ -20,22 +20,32 @@
function WriteAlert(type, message) {
}
function showFeedback(id) {
$("#ledger-loading").css("display", id === "ledger-loading" ? "block" : "none");
$("#no-ledger-info").css("display", id === "no-ledger-info" ? "block" : "none");
$("#ledger-validate").css("display", id === "ledger-validate" ? "block" : "none");
$("#ledger-info").css("display", id === "ledger-info" ? "block" : "none");
}
function Write(prefix, type, message) {
if (type === "error") {
$("#no-ledger-info").css("display", "block");
$("#ledger-in fo").css("display", "none");
showFeedback("no-ledger-info");
}
}
$(".ledger-info-recommended").on("click", function (elem) {
elem.preventDefault();
showFeedback("ledger-validate");
var account = elem.currentTarget.getAttribute("data-ledgeraccount");
var cryptoCode = GetSelectedCryptoCode();
bridge.sendCommand("getxpub", "cryptoCode=" + cryptoCode + "&account=" + account)
.then(function (result) {
if (cryptoCode !== GetSelectedCryptoCode())
return;
showFeedback("ledger-info");
$("#DerivationScheme").val(result.extPubKey);
$("#DerivationSchemeFormat").val("BTCPay");
})
@ -60,8 +70,7 @@
}
else {
Write('check', 'success', 'This store is configured to use your ledger');
$("#no-ledger-info").css("display", "none");
$("#ledger-info").css("display", "block");
showFeedback("ledger-info");
}
});
};

View File

@ -1,44 +1,47 @@
{
"code": "cs-CZ",
"currentLanguage": "Česky",
"lang": "Jazyk",
"Awaiting Payment...": "Očekávám platbu...",
"Pay with": "Zaplatit pomocí",
"Contact and Refund Email": "Kontaktní email",
"Contact_Body": "Prosímte poskytněte vaši emailovou adresu. Kontaktujeme vás v případě, že se objeví problému s vaší platbou.",
"Your email": "Váš email",
"Continue": "Pokračovat",
"Please enter a valid email address": "Prosíme vložte platnou emailovou adresu",
"Order Amount": "Cena objednávky",
"Network Cost": "Síťové náklady",
"Already Paid": "Již zaplaceno",
"Due": "Zbývá",
"Scan": "Skenovat",
"Copy": "Kopírovat",
"Conversion": "Konverze",
"Open in wallet": "Otevřít v peněžence",
"CompletePay_Body": "K dokončení platby, prosíme pošlete {{btcDue}} {{cryptoCode}} na adresu níže.",
"Amount": "Částka",
"Address": "Adresa",
"Copied": "Zkopírováno",
"ConversionTab_BodyTop": "Můžete zaplatit {{btcDue}} {{cryptoCode}} i pomocí altcoinů které přímo nepodporuje obchodník.",
"ConversionTab_BodyDesc": "Tato služba je poskytována třetí stranou. Prosíme mějte na paměti, že nemáme žádnou kontrolu nad tím, jak poskytovatelé budou nakládat s vašimi prostředky. Faktura bude označena jako zaplacena, pouze když jsou prostředky obdrženy v {{cryptoCode}} Blockchainu.",
"Shapeshift_Button_Text": "Zaplatit pomocí Altcoinů",
"ConversionTab_Lightning": "Pro platby Lightning Network nejsou dostupní žádní poskytovatelé konverzí.",
"Invoice expiring soon...": "Faktura brzy vyprší...",
"Invoice expired": "Faktura vypršela",
"What happened?": "Co se stalo?",
"InvoiceExpired_Body_1": "Tato faktura již vypršela. Faktura je platná pouze {{maxTimeMinutes}} minut. \nMůžete se vrátit do {{storeName}}, pokud chcete svojí objednávku založit znovu.",
"InvoiceExpired_Body_2": "Pokud jste se pokoušeli poslat platbu, nebyla zatím zaznamenána v Bitcoinové síti. Zatím jsme neobdrželi vaše prostředky.",
"InvoiceExpired_Body_3": "Pokud nebude transakce přijata Bitcoinovou sítí, vaše prostředky bude opět použitelné ve vaší peněžence. V závislosti na vaší peněžence toto může trvat 48-72 hodin.",
"Invoice ID": "ID Faktury",
"Order ID": "ID Objednávky",
"Return to StoreName": "Vrátit se na {{storeName}}",
"This invoice has been paid": "Faktura byla zaplacena",
"This invoice has been archived": "Tato faktura byla archivována",
"Archived_Body": "Prosíme kontaktujte prodejce pro informace o objednávce a případnou pomoc",
"BOLT 11 Invoice": "BOLT 11 Faktura",
"Node Info": "Info o uzlu",
"txCount": "{{count}} transakce",
"txCount_plural": "{{count}} transakcí"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "cs-CZ",
"currentLanguage": "Česky",
"lang": "Jazyk",
"Awaiting Payment...": "Očekávám platbu...",
"Pay with": "Zaplatit pomocí",
"Contact and Refund Email": "Kontaktní email",
"Contact_Body": "Prosímte poskytněte vaši emailovou adresu. Kontaktujeme vás v případě, že se objeví problému s vaší platbou.",
"Your email": "Váš email",
"Continue": "Pokračovat",
"Please enter a valid email address": "Prosíme vložte platnou emailovou adresu",
"Order Amount": "Cena objednávky",
"Network Cost": "Síťové náklady",
"Already Paid": "Již zaplaceno",
"Due": "Zbývá",
"Scan": "Skenovat",
"Copy": "Kopírovat",
"Conversion": "Konverze",
"Open in wallet": "Otevřít v peněžence",
"CompletePay_Body": "K dokončení platby, prosíme pošlete {{btcDue}} {{cryptoCode}} na adresu níže.",
"Amount": "Částka",
"Address": "Adresa",
"Copied": "Zkopírováno",
"ConversionTab_BodyTop": "Můžete zaplatit {{btcDue}} {{cryptoCode}} i pomocí altcoinů které přímo nepodporuje obchodník.",
"ConversionTab_BodyDesc": "Tato služba je poskytována třetí stranou. Prosíme mějte na paměti, že nemáme žádnou kontrolu nad tím, jak poskytovatelé budou nakládat s vašimi prostředky. Faktura bude označena jako zaplacena, pouze když jsou prostředky obdrženy v {{cryptoCode}} Blockchainu.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Pro platby Lightning Network nejsou dostupní žádní poskytovatelé konverzí.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Faktura brzy vyprší...",
"Invoice expired": "Faktura vypršela",
"What happened?": "Co se stalo?",
"InvoiceExpired_Body_1": "Tato faktura již vypršela. Faktura je platná pouze {{maxTimeMinutes}} minut. \nMůžete se vrátit do {{storeName}}, pokud chcete svojí objednávku založit znovu.",
"InvoiceExpired_Body_2": "Pokud jste se pokoušeli poslat platbu, nebyla zatím zaznamenána v Bitcoinové síti. Zatím jsme neobdrželi vaše prostředky.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "ID Faktury",
"Order ID": "ID Objednávky",
"Return to StoreName": "Vrátit se na {{storeName}}",
"This invoice has been paid": "Faktura byla zaplacena",
"This invoice has been archived": "Tato faktura byla archivována",
"Archived_Body": "Prosíme kontaktujte prodejce pro informace o objednávce a případnou pomoc",
"BOLT 11 Invoice": "BOLT 11 Faktura",
"Node Info": "Info o uzlu",
"txCount": "{{count}} transakce",
"txCount_plural": "{{count}} transakcí"
}

View File

@ -1,44 +1,47 @@
{
"code": "de-DE",
"currentLanguage": "Deutsch",
"lang": "Sprache",
"Awaiting Payment...": "Warten auf Zahlung...",
"Pay with": "Bezahlen mit",
"Contact and Refund Email": "Kontakt und Rückerstattungs Email",
"Contact_Body": "Bitte geben Sie unten eine E-Mail-Adresse an. Wir werden Sie unter dieser Adresse kontaktieren, falls ein Problem mit Ihrer Zahlung vorliegt.",
"Your email": "Ihre Email-Adresse",
"Continue": "Fortsetzen",
"Please enter a valid email address": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
"Order Amount": "Bestellbetrag",
"Network Cost": "Netzwerkkosten",
"Already Paid": "Bereits bezahlt",
"Due": "Fällig",
"Scan": "Scan",
"Copy": "Kopieren",
"Conversion": "Umrechnung",
"Open in wallet": "In der Wallet öffnen",
"CompletePay_Body": "Um Ihre Zahlung abzuschließen, senden Sie bitte {{btcDue}} {{cryptoCode}} an die unten angegebene Adresse.",
"Amount": "Menge",
"Address": "Adresse",
"Copied": "Kopiert",
"ConversionTab_BodyTop": "Sie können {{btcDue}} {{cryptoCode}} mit Altcoins bezahlen, die nicht direkt vom Händler unterstützt werden.",
"ConversionTab_BodyDesc": "Dieser Service wird von Drittanbietern bereitgestellt. Bitte beachten Sie, dass wir keine Kontrolle darüber haben, wie die Anbieter Ihre Gelder weiterleiten. Die Rechnung wird erst als bezahlt markiert, wenn das Geld in {{cryptoCode}} Blockchain eingegangen ist.",
"Shapeshift_Button_Text": "Bezahlen mit Altcoins",
"ConversionTab_Lightning": "Für Lightning Network-Zahlungen sind keine Umrechnungsanbieter verfügbar.",
"Invoice expiring soon...": "Die Rechnung läuft bald ab...",
"Invoice expired": "Die Rechnung ist abgelaufen",
"What happened?": "Was ist passiert?",
"InvoiceExpired_Body_1": "Diese Rechnung ist abgelaufen. Eine Rechnung ist nur für {{maxTimeMinutes}} Minuten gültig. \nSie können zu {{storeName}} zurückkehren, wenn Sie Ihre Zahlung erneut senden möchten.",
"InvoiceExpired_Body_2": "Wenn Sie versucht haben, eine Zahlung zu senden, wurde sie vom Netzwerk noch nicht akzeptiert. Wir haben Ihre Gelder noch nicht erhalten.",
"InvoiceExpired_Body_3": "Wenn die Transaktion vom Netzwerk nicht akzeptiert wird, ist das Geld wieder in Ihrer Wallet verfügbar. Abhängig von Ihrer Wallet, kann dies 48-72 Stunden dauern.",
"Invoice ID": "Rechnungs ID",
"Order ID": "Auftrag ID",
"Return to StoreName": "Zurück zu {{storeName}}",
"This invoice has been paid": "Diese Rechnung wurde bezahlt",
"This invoice has been archived": "Diese Rechnung wurde archiviert",
"Archived_Body": "Bitte kontaktieren Sie den Shop für Bestellinformationen oder Hilfe",
"BOLT 11 Invoice": "BOLT 11 Rechnung",
"Node Info": "Netzwerkknoten Info",
"txCount": "{{count}} transaktion",
"txCount_plural": "{{count}} transaktionen"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "de-DE",
"currentLanguage": "Deutsch",
"lang": "Sprache",
"Awaiting Payment...": "Warten auf Zahlung...",
"Pay with": "Bezahlen mit",
"Contact and Refund Email": "Kontakt und Rückerstattungs Email",
"Contact_Body": "Bitte geben Sie unten eine E-Mail-Adresse an. Wir werden Sie unter dieser Adresse kontaktieren, falls ein Problem mit Ihrer Zahlung vorliegt.",
"Your email": "Ihre Email-Adresse",
"Continue": "Fortsetzen",
"Please enter a valid email address": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
"Order Amount": "Bestellbetrag",
"Network Cost": "Netzwerkkosten",
"Already Paid": "Bereits bezahlt",
"Due": "Fällig",
"Scan": "Scan",
"Copy": "Kopieren",
"Conversion": "Umrechnung",
"Open in wallet": "In der Wallet öffnen",
"CompletePay_Body": "Um Ihre Zahlung abzuschließen, senden Sie bitte {{btcDue}} {{cryptoCode}} an die unten angegebene Adresse.",
"Amount": "Menge",
"Address": "Adresse",
"Copied": "Kopiert",
"ConversionTab_BodyTop": "Sie können {{btcDue}} {{cryptoCode}} mit Altcoins bezahlen, die nicht direkt vom Händler unterstützt werden.",
"ConversionTab_BodyDesc": "Dieser Service wird von Drittanbietern bereitgestellt. Bitte beachten Sie, dass wir keine Kontrolle darüber haben, wie die Anbieter Ihre Gelder weiterleiten. Die Rechnung wird erst als bezahlt markiert, wenn das Geld in {{cryptoCode}} Blockchain eingegangen ist.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Für Lightning Network-Zahlungen sind keine Umrechnungsanbieter verfügbar.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Die Rechnung läuft bald ab...",
"Invoice expired": "Die Rechnung ist abgelaufen",
"What happened?": "Was ist passiert?",
"InvoiceExpired_Body_1": "Diese Rechnung ist abgelaufen. Eine Rechnung ist nur für {{maxTimeMinutes}} Minuten gültig. \nSie können zu {{storeName}} zurückkehren, wenn Sie Ihre Zahlung erneut senden möchten.",
"InvoiceExpired_Body_2": "Wenn Sie versucht haben, eine Zahlung zu senden, wurde sie vom Netzwerk noch nicht akzeptiert. Wir haben Ihre Gelder noch nicht erhalten.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Rechnungs ID",
"Order ID": "Auftrag ID",
"Return to StoreName": "Zurück zu {{storeName}}",
"This invoice has been paid": "Diese Rechnung wurde bezahlt",
"This invoice has been archived": "Diese Rechnung wurde archiviert",
"Archived_Body": "Bitte kontaktieren Sie den Shop für Bestellinformationen oder Hilfe",
"BOLT 11 Invoice": "BOLT 11 Rechnung",
"Node Info": "Netzwerkknoten Info",
"txCount": "{{count}} transaktion",
"txCount_plural": "{{count}} transaktionen"
}

View File

@ -1,46 +1,47 @@
{
"code": "en",
"currentLanguage": "English",
"lang": "Language",
"Awaiting Payment...": "Awaiting Payment...",
"Pay with": "Pay with",
"Contact and Refund Email": "Contact & Refund Email",
"Contact_Body": "Please provide an email address below. Well contact you at this address if there is an issue with your payment.",
"Your email": "Your email",
"Continue": "Continue",
"Please enter a valid email address": "Please enter a valid email address",
"Order Amount": "Order Amount",
"Network Cost": "Network Cost",
"Already Paid": "Already Paid",
"Due": "Due",
"Scan": "Scan",
"Copy": "Copy",
"Conversion": "Conversion",
"Open in wallet": "Open in wallet",
"CompletePay_Body": "To complete your payment, please send {{btcDue}} {{cryptoCode}} to the address below.",
"Amount": "Amount",
"Address": "Address",
"Copied": "Copied",
"ConversionTab_BodyTop": "You can pay {{btcDue}} {{cryptoCode}} using altcoins other than the ones merchant directly supports.",
"ConversionTab_BodyDesc": "This service is provided by 3rd party. Please keep in mind that we have no control over how providers will forward your funds. Invoice will only be marked paid once funds are received on {{cryptoCode}} Blockchain.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "No conversion providers available for Lightning Network payments.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Invoice expiring soon...",
"Invoice expired": "Invoice expired",
"What happened?": "What happened?",
"InvoiceExpired_Body_1": "This invoice has expired. An invoice is only valid for {{maxTimeMinutes}} minutes. \\nYou can return to {{storeName}} if you would like to submit your payment again.",
"InvoiceExpired_Body_2": "If you tried to send a payment, it has not yet been accepted by the network. We have not yet received your funds.",
"InvoiceExpired_Body_3": "If the transaction is not accepted by the network, the funds will be spendable again in your wallet. Depending on your wallet, this may take 48-72 hours.",
"Invoice ID": "Invoice ID",
"Order ID": "Order ID",
"Return to StoreName": "Return to {{storeName}}",
"This invoice has been paid": "This invoice has been paid",
"This invoice has been archived": "This invoice has been archived",
"Archived_Body": "Please contact the store for order information or assistance",
"BOLT 11 Invoice": "BOLT 11 Invoice",
"Node Info": "Node Info",
"txCount": "{{count}} transaction",
"txCount_plural": "{{count}} transactions"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "en",
"currentLanguage": "English",
"lang": "Language",
"Awaiting Payment...": "Awaiting Payment...",
"Pay with": "Pay with",
"Contact and Refund Email": "Contact & Refund Email",
"Contact_Body": "Please provide an email address below. Well contact you at this address if there is an issue with your payment.",
"Your email": "Your email",
"Continue": "Continue",
"Please enter a valid email address": "Please enter a valid email address",
"Order Amount": "Order Amount",
"Network Cost": "Network Cost",
"Already Paid": "Already Paid",
"Due": "Due",
"Scan": "Scan",
"Copy": "Copy",
"Conversion": "Conversion",
"Open in wallet": "Open in wallet",
"CompletePay_Body": "To complete your payment, please send {{btcDue}} {{cryptoCode}} to the address below.",
"Amount": "Amount",
"Address": "Address",
"Copied": "Copied",
"ConversionTab_BodyTop": "You can pay {{btcDue}} {{cryptoCode}} using altcoins other than the ones merchant directly supports.",
"ConversionTab_BodyDesc": "This service is provided by 3rd party. Please keep in mind that we have no control over how providers will forward your funds. Invoice will only be marked paid once funds are received on {{cryptoCode}} Blockchain.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "No conversion providers available for Lightning Network payments.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Invoice expiring soon...",
"Invoice expired": "Invoice expired",
"What happened?": "What happened?",
"InvoiceExpired_Body_1": "This invoice has expired. An invoice is only valid for {{maxTimeMinutes}} minutes. \nYou can return to {{storeName}} if you would like to submit your payment again.",
"InvoiceExpired_Body_2": "If you tried to send a payment, it has not yet been accepted by the network. We have not yet received your funds.",
"InvoiceExpired_Body_3": "If we receive it at a later point, we will either process your order or contact you to make refund arrangements...",
"Invoice ID": "Invoice ID",
"Order ID": "Order ID",
"Return to StoreName": "Return to {{storeName}}",
"This invoice has been paid": "This invoice has been paid",
"This invoice has been archived": "This invoice has been archived",
"Archived_Body": "Please contact the store for order information or assistance",
"BOLT 11 Invoice": "BOLT 11 Invoice",
"Node Info": "Node Info",
"txCount": "{{count}} transaction",
"txCount_plural": "{{count}} transactions"
}

View File

@ -1,44 +1,47 @@
{
"code": "es-ES",
"currentLanguage": "Spanish",
"lang": "Lenguaje",
"Awaiting Payment...": "En espera de pago...",
"Pay with": "Pagar con",
"Contact and Refund Email": "Contacto y correo electrónico de reembolso",
"Contact_Body": "Por favor indica una dirección de correo electrónico a continuación. Nos pondremos en contacto contigo en esta dirección si hay algún problema con tu pago.",
"Your email": "Tu correo electrónico",
"Continue": "Continuar",
"Please enter a valid email address": "Por favor ingresa un correo electrónico válido",
"Order Amount": "Total del pedido",
"Network Cost": "Costo de la red",
"Already Paid": "Ya has pagado",
"Due": "Aún debes",
"Scan": "Escanear",
"Copy": "Copiar",
"Conversion": "Conversión",
"Open in wallet": "Abrir en la billetera",
"CompletePay_Body": "Para completar tu pago, envía {{btcDue}} {{cryptoCode}} a la siguiente dirección:",
"Amount": "Cantidad",
"Address": "Dirección",
"Copied": "Copiado",
"ConversionTab_BodyTop": "Puedes pagar {{btcDue}} {{cryptoCode}} usando Altcoins que este comercio no soporta directamente.",
"ConversionTab_BodyDesc": "Este servicio es provisto por terceros. Ten en cuenta que no tenemos control sobre cómo estos terceros envían los fondos. La factura solo se marcará como pagada una vez se reciban los fondos en la cadena de bloques de {{cryptoCode}} .",
"Shapeshift_Button_Text": "Pagar con Altcoins",
"ConversionTab_Lightning": "No hay proveedores de conversión disponibles para los pagos de Lightning Network.",
"Invoice expiring soon...": "La factura expira pronto...",
"Invoice expired": "La factura expiró",
"What happened?": "¿Qué sucedió?",
"InvoiceExpired_Body_1": "Esta factura ha expirado. Una factura solo es válida por {{maxTimeMinutes}} minutos. Puedes regresar a {{storeName}} si deseas volver a enviar tu pago.",
"InvoiceExpired_Body_2": "Si intentaste enviar un pago, aún no ha sido aceptado por la red de Bitcoin. Todavía no hemos recibido tus fondos.",
"InvoiceExpired_Body_3": "Si la transacción no es aceptada por la red de Bitcoin, los fondos se podrán gastar nuevamente en tu billetera. Dependiendo de tu billetera, esto puede tomar 48-72 horas.",
"Invoice ID": "ID de la factura",
"Order ID": "ID del pedido",
"Return to StoreName": "Regresar a {{storeName}}",
"This invoice has been paid": "Esta factura ha sido pagada",
"This invoice has been archived": "Esta factura ha sido archivada",
"Archived_Body": "Por favor, comunícate con la tienda para obtener información de tu pedido o asistencia",
"BOLT 11 Invoice": "Factura BOLT 11",
"Node Info": "Información del nodo",
"txCount": "{{count}} transacción",
"txCount_plural": "{{count}} transacciones"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "es-ES",
"currentLanguage": "Español",
"lang": "Lenguaje",
"Awaiting Payment...": "En espera de pago...",
"Pay with": "Pagar con",
"Contact and Refund Email": "Contacto y correo electrónico de reembolso",
"Contact_Body": "Por favor indica una dirección de correo electrónico a continuación. Nos pondremos en contacto contigo en esta dirección si hay algún problema con tu pago.",
"Your email": "Tu correo electrónico",
"Continue": "Continuar",
"Please enter a valid email address": "Por favor ingresa un correo electrónico válido",
"Order Amount": "Total del pedido",
"Network Cost": "Costo de la red",
"Already Paid": "Ya has pagado",
"Due": "Aún debes",
"Scan": "Escanear",
"Copy": "Copiar",
"Conversion": "Conversión",
"Open in wallet": "Abrir en la billetera",
"CompletePay_Body": "Para completar tu pago, envía {{btcDue}} {{cryptoCode}} a la siguiente dirección:",
"Amount": "Cantidad",
"Address": "Dirección",
"Copied": "Copiado",
"ConversionTab_BodyTop": "Puedes pagar {{btcDue}} {{cryptoCode}} usando Altcoins que este comercio no soporta directamente.",
"ConversionTab_BodyDesc": "Este servicio es provisto por terceros. Ten en cuenta que no tenemos control sobre cómo estos terceros envían los fondos. La factura solo se marcará como pagada una vez se reciban los fondos en la cadena de bloques de {{cryptoCode}} .",
"ConversionTab_CalculateAmount_Error": "Reintentar",
"ConversionTab_LoadCurrencies_Error": "Reintentar",
"ConversionTab_Lightning": "No hay proveedores de conversión disponibles para los pagos de Lightning Network.",
"ConversionTab_CurrencyList_Select_Option": "Por favor selecciona el tipo de moneda que de desea cambiar",
"Invoice expiring soon...": "La factura expira pronto...",
"Invoice expired": "La factura expiró",
"What happened?": "¿Qué sucedió?",
"InvoiceExpired_Body_1": "Esta factura ha expirado. Una factura solo es válida por {{maxTimeMinutes}} minutos. \nPuedes regresar a {{storeName}} si deseas volver a enviar tu pago.",
"InvoiceExpired_Body_2": "Si intentaste enviar un pago, aún no ha sido aceptado por la red de Bitcoin. Todavía no hemos recibido tus fondos.",
"InvoiceExpired_Body_3": "Si recibimos el pago despues, procesaremos tu orden o te contactaremos para un reembolso...",
"Invoice ID": "ID de la factura",
"Order ID": "ID del pedido",
"Return to StoreName": "Regresar a {{storeName}}",
"This invoice has been paid": "Esta factura ha sido pagada",
"This invoice has been archived": "Esta factura ha sido archivada",
"Archived_Body": "Por favor, comunícate con la tienda para obtener información de tu pedido o asistencia",
"BOLT 11 Invoice": "Factura BOLT 11",
"Node Info": "Información del nodo",
"txCount": "{{count}} transacción",
"txCount_plural": "{{count}} transacciones"
}

View File

@ -1,44 +1,47 @@
{
"code": "fr-FR",
"currentLanguage": "Français",
"lang": "Langue",
"Awaiting Payment...": "En attente du paiement...",
"Pay with": "Payer avec",
"Contact and Refund Email": "Adresse de contact et de remboursement",
"Contact_Body": "Merci de renseigner l'adresse email ci-dessous. Nous vous contacterons à cette adresse si il y a un problème avec votre paiement.",
"Your email": "Votre email",
"Continue": "Continuer",
"Please enter a valid email address": "Merci de saisir une addrese email valide",
"Order Amount": "Montant de la commande",
"Network Cost": "Coût réseau",
"Already Paid": "Déjà payé",
"Due": "Reste à payer",
"Scan": "Scanner",
"Copy": "Copier",
"Conversion": "Convertir",
"Open in wallet": "Ouvrir le portefeuille",
"CompletePay_Body": "Pour terminer le paiement, merci d'envoyer {{btcDue}} {{cryptoCode}} à l'adresse ci-dessous.",
"Amount": "Montant",
"Address": "Adresse",
"Copied": "Copié",
"ConversionTab_BodyTop": "Vous pouvez payer {{btcDue}} {{cryptoCode}} en utilisant d'autres crypto-monnaies alternatives non supportées directement par le marchand.",
"ConversionTab_BodyDesc": "Ce service est fourni par un tiers. Nous n'avons aucun contrôle sur la façon dont seront traités vos fonds. La facture sera considérée payée seulement quand les fonds seront reçus sur la blockchain {{ cryptoCode }}.",
"Shapeshift_Button_Text": "Payer en altcoins",
"ConversionTab_Lightning": "Le service de conversion n'est pas disponible pour les paiements sur le Lightning Network.",
"Invoice expiring soon...": "La facture va bientôt expirer...",
"Invoice expired": "Facture expirée",
"What happened?": "Que s'est-il passé ?",
"InvoiceExpired_Body_1": "La facture a expiré. Une facture est valide seulement {{maxTimeMinutes}} minutes. \nSi vous voulez soumettre à nouveau votre paiement, vous pouvez revenir sur {{storeName}} .",
"InvoiceExpired_Body_2": "Si vous avez envoyé un paiement, ce dernier n'a pas encore été inscrit dans la blockchain. Nous n'avons pas reçu vos fonds.",
"InvoiceExpired_Body_3": "Si votre transaction n'est pas inscrite dans la blockchain, vos fonds reviendront dans votre portefeuille. Selon votre portefeuille, cela peut prendre entre 48 et 72 heures.",
"Invoice ID": "Numéro de facture",
"Order ID": "Numéro de commande",
"Return to StoreName": "Retourner sur {{storeName}}",
"This invoice has been paid": "Cette facture a été payée",
"This invoice has been archived": "Cette facture a été archivée",
"Archived_Body": "Merci de contacter le marchand pour obtenir de l'aide ou des informations sur cette commande.",
"BOLT 11 Invoice": "Facture BOLT 11",
"Node Info": "Informations sur le nœud",
"txCount": "{{count}} transaction",
"txCount_plural": "{{count}} transactions"
}
{
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "fr-FR",
"currentLanguage": "Français",
"lang": "Langue",
"Awaiting Payment...": "En attente du paiement...",
"Pay with": "Payer avec",
"Contact and Refund Email": "Adresse de contact et de remboursement",
"Contact_Body": "Merci de renseigner l'adresse email ci-dessous. Nous vous contacterons à cette adresse si il y a un problème avec votre paiement.",
"Your email": "Votre email",
"Continue": "Continuer",
"Please enter a valid email address": "Merci de saisir une addrese email valide",
"Order Amount": "Montant de la commande",
"Network Cost": "Coût réseau",
"Already Paid": "Déjà payé",
"Due": "Reste à payer",
"Scan": "Scanner",
"Copy": "Copier",
"Conversion": "Convertir",
"Open in wallet": "Ouvrir le portefeuille",
"CompletePay_Body": "Pour terminer le paiement, merci d'envoyer {{btcDue}} {{cryptoCode}} à l'adresse ci-dessous.",
"Amount": "Montant",
"Address": "Adresse",
"Copied": "Copié",
"ConversionTab_BodyTop": "Vous pouvez payer {{btcDue}} {{cryptoCode}} en utilisant d'autres crypto-monnaies alternatives non supportées directement par le marchand.",
"ConversionTab_BodyDesc": "Ce service est fourni par un tiers. Nous n'avons aucun contrôle sur la façon dont seront traités vos fonds. La facture sera considérée payée seulement quand les fonds seront reçus sur la blockchain {{ cryptoCode }}.",
"ConversionTab_CalculateAmount_Error": "Recommencer",
"ConversionTab_LoadCurrencies_Error": "Recommencer",
"ConversionTab_Lightning": "Le service de conversion n'est pas disponible pour les paiements sur le Lightning Network.",
"ConversionTab_CurrencyList_Select_Option": "Merci de sélectionner la monnaie que vous voulez convertir",
"Invoice expiring soon...": "La facture va bientôt expirer...",
"Invoice expired": "Facture expirée",
"What happened?": "Que s'est-il passé ?",
"InvoiceExpired_Body_1": "La facture a expiré. Une facture est valide seulement {{maxTimeMinutes}} minutes. \nSi vous voulez soumettre à nouveau votre paiement, vous pouvez revenir sur {{storeName}} .",
"InvoiceExpired_Body_2": "Si vous avez envoyé un paiement, ce dernier n'a pas encore été inscrit dans la blockchain. Nous n'avons pas reçu vos fonds.",
"InvoiceExpired_Body_3": "Si nous recevons votre paiement plus tard, nous traiterons votre commande ou nous vous contacterons pour le remboursement.",
"Invoice ID": "Numéro de facture",
"Order ID": "Numéro de commande",
"Return to StoreName": "Retourner sur {{storeName}}",
"This invoice has been paid": "Cette facture a été payée",
"This invoice has been archived": "Cette facture a été archivée",
"Archived_Body": "Merci de contacter le marchand pour obtenir de l'aide ou des informations sur cette commande.",
"BOLT 11 Invoice": "Facture BOLT 11",
"Node Info": "Informations sur le nœud",
"txCount": "{{count}} transaction",
"txCount_plural": "{{count}} transactions"
}

View File

@ -1,40 +1,47 @@
{
"code": "hr-HR",
"currentLanguage": "Croatian",
"lang": "Croatian",
"Awaiting Payment...": "Čekamo uplatu...",
"Pay with": "Plati sa",
"Contact and Refund Email": "E-mail za kontakt i povrat sredstava",
"Contact_Body": "Molimo upišite Vašu e-mail adresu. Kontaktirat ćemo Vas ukoliko bude potrebe.",
"Your email": "Vaš e-mail",
"Continue": "Dalje",
"Please enter a valid email address": "Molimo unesite ispravnu e-mail adresu",
"Order Amount": "Količina",
"Network Cost": "Trošak mreže",
"Already Paid": "Već plaćeno",
"Due": "Rok",
"Scan": "Skeniraj",
"Copy": "Kopiraj",
"Conversion": "Pretvori",
"Open in wallet": "Otvori u novčaniku",
"CompletePay_Body": "Kako bi završili uplatu pošaljite {{btcDue}} {{cryptoCode}} na navedenu adresu",
"Amount": "Iznos",
"Address": "Adresa",
"Copied": "Kopirano",
"ConversionTab_BodyTop": "Možete platiti {{btcDue}} {{cryptoCode}} pomoću altcoina koje prodavač ne podržava.",
"ConversionTab_BodyDesc": "Ovu usluga pruža treća strana. Vodite računa da nemamo kontroli nad načinom kako će Vam davatelji usluge proslijediti sredstva. Vodite računa da je račun plaćen tek kada su primljena sredstva na {{cryptoCode}} Blockchainu.",
"Shapeshift_Button_Text": "Plati s Alt-coinovima",
"ConversionTab_Lightning": "Ne postoji treća strana koja bi konvertirala Lightning Network uplate.",
"Invoice expiring soon...": "Račun uskoro ističe...",
"Invoice expired": "Račun je istekao",
"What happened?": "Što se dogodilo",
"InvoiceExpired_Body_1": "Račun je istekao i nije više valjan. Račun vrijedi samo {{maxTimeMinutes}} minuta. \nMožete se vratiti na {{storeName}}, gdje možete ponovo inicirati plaćanje.",
"InvoiceExpired_Body_2": "Ako ste pokušali poslati uplatu, ista nije registrirana na Blockchainu. Nismo još zaprimili Vašu uplatu.",
"InvoiceExpired_Body_3": "Ako poslana sredstva na budu potvrđena na Blockchainu, sredstva će biti ponovo dostupna u Vašem novčaniku.",
"Invoice ID": "Broj računa",
"Order ID": "Broj narudžbe",
"Return to StoreName": "Vrati se na {{storeName}}",
"This invoice has been paid": "Račun je plaćen",
"This invoice has been archived": "Račun je arhiviran.",
"Archived_Body": "Kontaktirajte dućan za detalje oko narudžbe ili pomoć."
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "hr-HR",
"currentLanguage": "Croatian",
"lang": "Croatian",
"Awaiting Payment...": "Čekamo uplatu...",
"Pay with": "Plati sa",
"Contact and Refund Email": "E-mail za kontakt i povrat sredstava",
"Contact_Body": "Molimo upišite Vašu e-mail adresu. Kontaktirat ćemo Vas ukoliko bude potrebe.",
"Your email": "Vaš e-mail",
"Continue": "Dalje",
"Please enter a valid email address": "Molimo unesite ispravnu e-mail adresu",
"Order Amount": "Količina",
"Network Cost": "Trošak mreže",
"Already Paid": "Već plaćeno",
"Due": "Rok",
"Scan": "Skeniraj",
"Copy": "Kopiraj",
"Conversion": "Pretvori",
"Open in wallet": "Otvori u novčaniku",
"CompletePay_Body": "Kako bi završili uplatu pošaljite {{btcDue}} {{cryptoCode}} na navedenu adresu",
"Amount": "Iznos",
"Address": "Adresa",
"Copied": "Kopirano",
"ConversionTab_BodyTop": "Možete platiti {{btcDue}} {{cryptoCode}} pomoću altcoina koje prodavač ne podržava.",
"ConversionTab_BodyDesc": "Ovu usluga pruža treća strana. Vodite računa da nemamo kontroli nad načinom kako će Vam davatelji usluge proslijediti sredstva. Vodite računa da je račun plaćen tek kada su primljena sredstva na {{cryptoCode}} Blockchainu.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Ne postoji treća strana koja bi konvertirala Lightning Network uplate.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Račun uskoro ističe...",
"Invoice expired": "Račun je istekao",
"What happened?": "Što se dogodilo",
"InvoiceExpired_Body_1": "Račun je istekao i nije više valjan. Račun vrijedi samo {{maxTimeMinutes}} minuta. \nMožete se vratiti na {{storeName}}, gdje možete ponovo inicirati plaćanje.",
"InvoiceExpired_Body_2": "Ako ste pokušali poslati uplatu, ista nije registrirana na Blockchainu. Nismo još zaprimili Vašu uplatu.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Broj računa",
"Order ID": "Broj narudžbe",
"Return to StoreName": "Vrati se na {{storeName}}",
"This invoice has been paid": "Račun je plaćen",
"This invoice has been archived": "Račun je arhiviran.",
"Archived_Body": "Kontaktirajte dućan za detalje oko narudžbe ili pomoć.",
"BOLT 11 Invoice": "BOLT 11 Invoice",
"Node Info": "Node Info",
"txCount": "{{count}} transaction",
"txCount_plural": "{{count}} transactions"
}

View File

@ -1,44 +1,47 @@
{
"code": "is-IS",
"currentLanguage": "Íslenska",
"lang": "Tungumál",
"Awaiting Payment...": "Bíð eftir greiðslu...",
"Pay with": "Borga með",
"Contact and Refund Email": "Netfang",
"Contact_Body": "Við munum hafa samband við þig á þessu netfangi ef það er vandamál með greiðsluna þína.",
"Your email": "Þitt netfang",
"Continue": "Áfram",
"Please enter a valid email address": "Þú verður að nota gilt netfang",
"Order Amount": "Upphæð",
"Network Cost": "Auka gjöld",
"Already Paid": "Nú þegar greitt",
"Due": "Gjalddagi",
"Scan": "Skanna",
"Copy": "Afrita",
"Conversion": "Umbreyting",
"Open in wallet": "Opna með veski",
"CompletePay_Body": "Til að klára greiðsluna skaltu senda {{btcDue}} {{cryptoCode}} á lykilinn fyrir neðan.",
"Amount": "Magn",
"Address": "Lykill",
"Copied": "Afritað",
"ConversionTab_BodyTop": "Þú getur borgað {{btcDue}} {{cryptoCode}} með altcoins.",
"ConversionTab_BodyDesc": essi þjónusta er veitt af þriðja aðila. Mundu að við höfum ekki stjórn á því hvað þeir gera við peningana. Reikningurinn verður aðeins móttekinn þegar {{cryptoCode}} greiðslan hefur verið staðfest á netinu.",
"Shapeshift_Button_Text": "Borga með Altcoins",
"ConversionTab_Lightning": "Engir viðskiptaveitendur eru í boði fyrir Lightning Network greiðslur.",
"Invoice expiring soon...": "Reikningurinn rennur út fljótlega...",
"Invoice expired": "Reikningurinn er útrunnin",
"What happened?": "Hvað gerðist?",
"InvoiceExpired_Body_1": "Þessi reikningur er útrunnin. Reikningurinn er aðeins gildur í {{maxTimeMinutes}} mínútur. \nÞú getur farið aftur á {{storeName}} ef þú vilt reyna aftur.",
"InvoiceExpired_Body_2": "Ef þú reyndir að senda greiðslu, þá hefur hún ekki verið samþykkt.",
"InvoiceExpired_Body_3": "Ef viðskiptin eru ekki samþykkt af netinu verða fjármunirnir aðgengilegar aftur í veskinu þínu. Það fer eftir veskinu þínu og getur tekið 48-72 klukkustundir.",
"Invoice ID": "Innheimtu ID",
"Order ID": "Pöntun ID",
"Return to StoreName": "Fara aftur á {{storeName}}",
"This invoice has been paid": "Þetta hefur verið greitt",
"This invoice has been archived": "Þessi reikningur hefur verið gerður ógildur",
"Archived_Body": "Vinsamlegast hafðu samband fyrir upplýsingar eða aðstoð.",
"BOLT 11 Invoice": "BOLT 11 Reikningur",
"Node Info": "Nótu upplýsingar",
"txCount": "{{count}} reikningur",
"txCount_plural": "{{count}} reikningar"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "is-IS",
"currentLanguage": "Íslenska",
"lang": "Tungumál",
"Awaiting Payment...": "Bíð eftir greiðslu...",
"Pay with": "Borga með",
"Contact and Refund Email": "Netfang",
"Contact_Body": "Við munum hafa samband við þig á þessu netfangi ef það er vandamál með greiðsluna þína.",
"Your email": "Þitt netfang",
"Continue": "Áfram",
"Please enter a valid email address": "Þú verður að nota gilt netfang",
"Order Amount": "Upphæð",
"Network Cost": "Auka gjöld",
"Already Paid": "Nú þegar greitt",
"Due": "Gjalddagi",
"Scan": "Skanna",
"Copy": "Afrita",
"Conversion": "Umbreyting",
"Open in wallet": "Opna með veski",
"CompletePay_Body": "Til að klára greiðsluna skaltu senda {{btcDue}} {{cryptoCode}} á lykilinn fyrir neðan.",
"Amount": "Magn",
"Address": "Lykill",
"Copied": "Afritað",
"ConversionTab_BodyTop": ú getur borgað {{btcDue}} {{cryptoCode}} með altcoins.",
"ConversionTab_BodyDesc": "Þessi þjónusta er veitt af þriðja aðila. Mundu að við höfum ekki stjórn á því hvað þeir gera við peningana. Reikningurinn verður aðeins móttekinn þegar {{cryptoCode}} greiðslan hefur verið staðfest á netinu.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Engir viðskiptaveitendur eru í boði fyrir Lightning Network greiðslur.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Reikningurinn rennur út fljótlega...",
"Invoice expired": "Reikningurinn er útrunnin",
"What happened?": "Hvað gerðist?",
"InvoiceExpired_Body_1": "Þessi reikningur er útrunnin. Reikningurinn er aðeins gildur í {{maxTimeMinutes}} mínútur. \nÞú getur farið aftur á {{storeName}} ef þú vilt reyna aftur.",
"InvoiceExpired_Body_2": "Ef þú reyndir að senda greiðslu, þá hefur hún ekki verið samþykkt.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Innheimtu ID",
"Order ID": "Pöntun ID",
"Return to StoreName": "Fara aftur á {{storeName}}",
"This invoice has been paid": "Þetta hefur verið greitt",
"This invoice has been archived": "Þessi reikningur hefur verið gerður ógildur",
"Archived_Body": "Vinsamlegast hafðu samband fyrir upplýsingar eða aðstoð.",
"BOLT 11 Invoice": "BOLT 11 Reikningur",
"Node Info": "Nótu upplýsingar",
"txCount": "{{count}} reikningur",
"txCount_plural": "{{count}} reikningar"
}

View File

@ -1,44 +1,47 @@
{
"code": "it-IT",
"currentLanguage": "Italiano",
"lang": "Lingua",
"Awaiting Payment...": "In attesa del Pagamento...",
"Pay with": "Paga con",
"Contact and Refund Email": "Email di Contatto e Rimborso",
"Contact_Body": "Inserisci un indirizzo email qui sotto. Ti contatteremo a questo indirizzo in caso di problemi con il pagamento.",
"Your email": "La tua email",
"Continue": "Continua",
"Please enter a valid email address": "Inserisci un indirizzo email valido",
"Order Amount": "Importo dell'Ordine",
"Network Cost": "Costi di Rete",
"Already Paid": "Già pagato",
"Due": "Dovuto",
"Scan": "Scansiona",
"Copy": "Copia",
"Conversion": "Conversione",
"Open in wallet": "Apri nel portafoglio",
"CompletePay_Body": "Per completare il pagamento, inviare {{btcDue}} {{cryptoCode}} all'indirizzo riportato di seguito.",
"Amount": "Importo",
"Address": "Indirizzo",
"Copied": "Copiato",
"ConversionTab_BodyTop": "Puoi pagare {{btcDue}} {{cryptoCode}} usando altcoin diverse da quelle che il commerciante supporta direttamente.",
"ConversionTab_BodyDesc": "Questo servizio è fornito da terze parti. Si prega di tenere presente che non abbiamo alcun controllo su come i fornitori inoltreranno i fondi. La fattura verrà contrassegnata solo dopo aver ricevuto i fondi su {{cryptoCode}} Blockchain.",
"Shapeshift_Button_Text": "Paga con Altcoin",
"ConversionTab_Lightning": "Nessun fornitore di conversione disponibile per i pagamenti Lightning Network.",
"Invoice expiring soon...": "Fattura in scadenza a breve...",
"Invoice expired": "Fattura scaduta",
"What happened?": "Cosa è successo?",
"InvoiceExpired_Body_1": "Questa fattura è scaduta. Una fattura è valida solo per {{maxTime minuti}} minuti. \nPuoi tornare a {{store name}} se desideri inviare nuovamente il pagamento.",
"InvoiceExpired_Body_2": "Se hai provato a inviare un pagamento, non è ancora stato accettato dalla rete. Non abbiamo ancora ricevuto i tuoi fondi.",
"InvoiceExpired_Body_3": "Se la transazione non viene accettata dalla rete, i fondi saranno nuovamente spendibili nel tuo portafoglio. A seconda del portafoglio, potrebbero essere necessarie 48-72 ore.",
"Invoice ID": "Numero della Fattura",
"Order ID": "Numero dell'Ordine",
"Return to StoreName": "Ritorna a {{storeName}}",
"This invoice has been paid": "La fattura è stata pagata",
"This invoice has been archived": "TQuesta fattura è stata pagata",
"Archived_Body": "Contatta il negozio per informazioni sull'ordine o per assistenza",
"BOLT 11 Invoice": "Fattura BOLT 11",
"Node Info": "Informazioni sul Nodo",
"txCount": "{{count}} transazione",
"txCount_plural": "{{count}} transazioni"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "it-IT",
"currentLanguage": "Italiano",
"lang": "Lingua",
"Awaiting Payment...": "In attesa del Pagamento...",
"Pay with": "Paga con",
"Contact and Refund Email": "Email di Contatto e Rimborso",
"Contact_Body": "Inserisci un indirizzo email qui sotto. Ti contatteremo a questo indirizzo in caso di problemi con il pagamento.",
"Your email": "La tua email",
"Continue": "Continua",
"Please enter a valid email address": "Inserisci un indirizzo email valido",
"Order Amount": "Importo dell'Ordine",
"Network Cost": "Costi di Rete",
"Already Paid": "Già pagato",
"Due": "Dovuto",
"Scan": "Scansiona",
"Copy": "Copia",
"Conversion": "Conversione",
"Open in wallet": "Apri nel portafoglio",
"CompletePay_Body": "Per completare il pagamento, inviare {{btcDue}} {{cryptoCode}} all'indirizzo riportato di seguito.",
"Amount": "Importo",
"Address": "Indirizzo",
"Copied": "Copiato",
"ConversionTab_BodyTop": "Puoi pagare {{btcDue}} {{cryptoCode}} usando altcoin diverse da quelle che il commerciante supporta direttamente.",
"ConversionTab_BodyDesc": "Questo servizio è fornito da terze parti. Si prega di tenere presente che non abbiamo alcun controllo su come i fornitori inoltreranno i fondi. La fattura verrà contrassegnata solo dopo aver ricevuto i fondi su {{cryptoCode}} Blockchain.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Nessun fornitore di conversione disponibile per i pagamenti Lightning Network.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Fattura in scadenza a breve...",
"Invoice expired": "Fattura scaduta",
"What happened?": "Cosa è successo?",
"InvoiceExpired_Body_1": "Questa fattura è scaduta. Una fattura è valida solo per {{maxTime minuti}} minuti. \nPuoi tornare a {{store name}} se desideri inviare nuovamente il pagamento.",
"InvoiceExpired_Body_2": "Se hai provato a inviare un pagamento, non è ancora stato accettato dalla rete. Non abbiamo ancora ricevuto i tuoi fondi.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Numero della Fattura",
"Order ID": "Numero dell'Ordine",
"Return to StoreName": "Ritorna a {{storeName}}",
"This invoice has been paid": "La fattura è stata pagata",
"This invoice has been archived": "TQuesta fattura è stata pagata",
"Archived_Body": "Contatta il negozio per informazioni sull'ordine o per assistenza",
"BOLT 11 Invoice": "Fattura BOLT 11",
"Node Info": "Informazioni sul Nodo",
"txCount": "{{count}} transazione",
"txCount_plural": "{{count}} transazioni"
}

View File

@ -1,44 +1,47 @@
{
"code": "ja-JP",
"currentLanguage": "日本語",
"lang": "Language",
"Awaiting Payment...": "お支払いをお待ちしております…",
"Pay with": "お支払い方法",
"Contact and Refund Email": "問題発生時の連絡先",
"Contact_Body": "決済においては、何か問題発生したらこちらのメールアドレスに対してご連絡差し上げることもございますので、ご記入ください",
"Your email": "ご自分のメールアドレス",
"Continue": "続ける",
"Please enter a valid email address": "正常なメールアドレスをご記入ください",
"Order Amount": "注文金額",
"Network Cost": "ネットワーク手数料",
"Already Paid": "支払い済み金額",
"Due": "払い金額",
"Scan": "スキャン",
"Copy": "コピー",
"Conversion": "変換",
"Open in wallet": "ウォレットで開く",
"CompletePay_Body": "決済をするために、下記のアドレスに {{btcDue}} {{cryptoCode}} をお送りください",
"Amount": "金額",
"Address": "アドレス",
"Copied": "コピーしました",
"ConversionTab_BodyTop": "代わりに、お店が受け付けていなくても {{btcDue}} {{cryptoCode}} での支払いもできます。",
"ConversionTab_BodyDesc": "ただし、この変換は第三者サービスによるものですので、お店が受け付けている通貨で着金するまでの間の処理に関しては何の保証もいたしません。変換後に受付中の通貨 ({{cryptoCode}}) がお店に着金してから支払い済みとなりますのでご了承ください。",
"Shapeshift_Button_Text": "他の仮想通貨で支払う",
"ConversionTab_Lightning": "ライトニングのペイメントでは現在変換サービスが存在しないためご利用いただけません。ご了承ください。",
"Invoice expiring soon...": "お支払いの期限が迫っています...",
"Invoice expired": "お支払いの期限が切れました",
"What happened?": "え!?ナニコレ!?",
"InvoiceExpired_Body_1": "当件のお支払いの有効期限が過ぎてしまいました。最大 {{maxTimeMinutes}} 分以内に支払うことが義務付けられています。 \nまだお支払いのご希望の場合 {{storeName}} に一旦戻っていただき、もう一度お支払いの手続きを最初からやり直してみてください。",
"InvoiceExpired_Body_2": "送金手続きを完了したつもりでも、ネットワークにて取り込まれて処理されるまでは決済となりません。現時点ではまだ着金しておりません。",
"InvoiceExpired_Body_3": "ネットワークにて取り込まれなかった送金はいずれ送金元のウォレットに戻りますが、ウォレットソフトによっては2〜3日かかる場合もございますのでご了承ください。",
"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": "お支払いコード",
"Node Info": "接続情報",
"txCount": "取引 {{count}} 個",
"txCount_plural": "取引 {{count}} 個"
}
{
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "ja-JP",
"currentLanguage": "日本語",
"lang": "Language",
"Awaiting Payment...": "お支払いをお待ちしております…",
"Pay with": "お支払い方法",
"Contact and Refund Email": "問題発生時の連絡先",
"Contact_Body": "決済においては、何か問題が発生したらこちらのメールアドレスに対してご連絡差し上げることもございますので、ご記入ください",
"Your email": "ご自分のメールアドレス",
"Continue": "続ける",
"Please enter a valid email address": "正常なメールアドレスをご記入ください",
"Order Amount": "注文金額",
"Network Cost": "ネットワーク手数料",
"Already Paid": "払い済み金額",
"Due": "未払い金額",
"Scan": "スキャン",
"Copy": "コピー",
"Conversion": "変換",
"Open in wallet": "ウォレットで開く",
"CompletePay_Body": "決済をするために、下記のアドレスに {{btcDue}} {{cryptoCode}} をお送りください",
"Amount": "金額",
"Address": "アドレス",
"Copied": "コピーしました",
"ConversionTab_BodyTop": "代わりに、お店が受け付けていなくても {{btcDue}} {{cryptoCode}} での支払いもできます。",
"ConversionTab_BodyDesc": "ただし、この変換は第三者サービスによるものですので、お店が受け付けている通貨で着金するまでの間の処理に関しては何の保証もいたしません。変換後に受付中の通貨 ({{cryptoCode}}) がお店に着金してから支払い済みとなりますのでご了承ください。",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "ライトニングのペイメントでは現在変換サービスが存在しないためご利用いただけません。ご了承ください。",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "お支払いの期限が迫っています...",
"Invoice expired": "お支払いの期限が切れました",
"What happened?": "え!?ナニコレ!?",
"InvoiceExpired_Body_1": "当件のお支払いの有効期限が過ぎてしまいました。最大 {{maxTimeMinutes}} 分以内に支払うことが義務付けられています。 \nまだお支払いのご希望の場合 {{storeName}} に一旦戻っていただき、もう一度お支払いの手続きを最初からやり直してみてください。",
"InvoiceExpired_Body_2": "送金手続きを完了したつもりでも、ネットワークにて取り込まれて処理されるまでは決済となりません。現時点ではまだ着金しておりません。",
"InvoiceExpired_Body_3": "",
"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": "お支払いコード",
"Node Info": "接続情報",
"txCount": "取引 {{count}} 個",
"txCount_plural": "取引 {{count}} 個"
}

View File

@ -1,44 +1,47 @@
{
"code": "kk-KZ",
"currentLanguage": "Қазақша",
"lang": "Тіл",
"Awaiting Payment...": "Күтіп тұрған төлем…",
"Pay with": "Төлеу",
"Contact and Refund Email": "Байланыс және ақша қайтару үшін арналған электрондық пошта",
"Contact_Body": "Электрондық пошта мекенжайыңызды төменде көрсетуіңізді сұраймыз. Төлеміңіз туралы мәселе болса, біз сізге осы мекенжайыңыз арқылы хабарласамыз.",
"Your email": "Сіздің электрондық пошта мекенжайыңыз",
"Continue": "Жалғастыру",
"Please enter a valid email address": "Қолданыстағы электрондық пошта мекенжайыңызды еңгізуін сұраймыз",
"Order Amount": "Тапсырыс саны",
"Network Cost": "Желі бағасы",
"Already Paid": "Төленген",
"Due": "Жалпы сома",
"Scan": "Сканерлеу",
"Copy": "Көшіру",
"Conversion": "Айырбастау",
"Open in wallet": "Әмиянда ашу",
"CompletePay_Body": "Төлеміңізді аяқтау үшін төмендегі мекенжайға {{btcDue}} {{cryptoCode}} жіберуіңізді сұраймыз",
"Amount": "Сан",
"Address": "Мекенжай",
"Copied": "Көшірілді",
"ConversionTab_BodyTop": "Сатушы тікелей қолдау көрсетуден тыс кезде сіз altcoins көмегімен {{btcDue}} {{cryptoCode}} төлеуіңізге болады.",
"ConversionTab_BodyDesc": "Бұл қызмет үшінші тараптан қамтамасыз етіледі. Сіздің ақшаңызды провайдерлер сізге қалай жеткізетінін біз бақылауға алмайтынымызды есте сақтауыңызды сұраймыз. Шот тек қана {{cryptoCode}} Blockchain жүйесі қаражаттырылған соң көрсетіледі.",
"Shapeshift_Button_Text": "Төлеу Altcoins",
"ConversionTab_Lightning": "Lightning Төлемдерді айырбастау жеткізушілер байланыстан тыс жерде",
"Invoice expiring soon...": "Шот-фактура жақын арада аяқталады…",
"Invoice expired": "Шот-фактураның мерзімі аяқталды",
"What happened?": "Не жағдай болды?",
"InvoiceExpired_Body_1": "Бұл шот-фактураның мерзімі аяқталды. Шот-фактура {{maxTimeMinutes}} минуттарға жарамды. \nТөлеміңізді қайтадан жібергіңіз келсе {{storeName}} ға оралуға болады.",
"InvoiceExpired_Body_2": "Егер сіз төлемді жіберуге тырыссқан болсаңыз, ол әлі желімен қабылданған жоқ. Біз сіздің қаражатыңызды әлі алған жоқпыз.",
"InvoiceExpired_Body_3": "Егер транзакция желі арқылы қабылданбаса, қаражат әмияныңызға қайтадан қайтарылады. Бұл сіздің әмияныңызға байланысты 48-72 сағат алуы мүмкін.",
"Invoice ID": "Шот анықтамасы",
"Order 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 анықтамасы",
"txCount": "{{count}} Транзакция",
"txCount_plural": "{{count}} Транзакциялар"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "kk-KZ",
"currentLanguage": "Қазақша",
"lang": "Тіл",
"Awaiting Payment...": "Күтіп тұрған төлем…",
"Pay with": "Төлеу",
"Contact and Refund Email": "Байланыс және ақша қайтару үшін арналған электрондық пошта",
"Contact_Body": лектрондық пошта мекенжайыңызды төменде көрсетуіңізді сұраймыз. Төлеміңіз туралы мәселе болса, біз сізге осы мекенжайыңыз арқылы хабарласамыз.",
"Your email": "Сіздің электрондық пошта мекенжайыңыз",
"Continue": "Жалғастыру",
"Please enter a valid email address": "Қолданыстағы электрондық пошта мекенжайыңызды еңгізуін сұраймыз",
"Order Amount": "Тапсырыс саны",
"Network Cost": "Желі бағасы",
"Already Paid": "Төленген",
"Due": "Жалпы сома",
"Scan": "Сканерлеу",
"Copy": "Көшіру",
"Conversion": "Айырбастау",
"Open in wallet": "Әмиянда ашу",
"CompletePay_Body": "Төлеміңізді аяқтау үшін төмендегі мекенжайға {{btcDue}} {{cryptoCode}} жіберуіңізді сұраймыз",
"Amount": "Сан",
"Address": "Мекенжай",
"Copied": "Көшірілді",
"ConversionTab_BodyTop": "Сатушы тікелей қолдау көрсетуден тыс кезде сіз altcoins көмегімен {{btcDue}} {{cryptoCode}} төлеуіңізге болады.",
"ConversionTab_BodyDesc": "Бұл қызмет үшінші тараптан қамтамасыз етіледі. Сіздің ақшаңызды провайдерлер сізге қалай жеткізетінін біз бақылауға алмайтынымызды есте сақтауыңызды сұраймыз. Шот тек қана {{cryptoCode}} Blockchain жүйесі қаражаттырылған соң көрсетіледі.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Lightning Төлемдерді айырбастау жеткізушілер байланыстан тыс жерде",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Шот-фактура жақын арада аяқталады",
"Invoice expired": "Шот-фактураның мерзімі аяқталды",
"What happened?": "Не жағдай болды?",
"InvoiceExpired_Body_1": "Бұл шот-фактураның мерзімі аяқталды. Шот-фактура {{maxTimeMinutes}} минуттарға жарамды. \nТөлеміңізді қайтадан жібергіңіз келсе {{storeName}} ға оралуға болады.",
"InvoiceExpired_Body_2": "Егер сіз төлемді жіберуге тырыссқан болсаңыз, ол әлі желімен қабылданған жоқ. Біз сіздің қаражатыңызды әлі алған жоқпыз.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Шот анықтамасы",
"Order 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 анықтамасы",
"txCount": "{{count}} Транзакция",
"txCount_plural": "{{count}} Транзакциялар"
}

View File

@ -1,44 +1,47 @@
{
"code": "nl-NL",
"currentLanguage": "Dutch",
"lang": "Taal",
"Awaiting Payment...": "Wachtende op de betaling...",
"Pay with": "Betalen met",
"Contact and Refund Email": "Email adres voor opvolging en terugbetaling",
"Contact_Body": "Bedankt om je email adres in te vullen voor een mogelijke opvolging. We contacteren je indien er een probleem optreedt.",
"Your email": "Je email adres",
"Continue": "Verdergaan",
"Please enter a valid email address": "Vul een geldig email adres in",
"Order Amount": "Bedrag van je bestelling",
"Network Cost": "Netwerk kosten",
"Already Paid": "Reeds betaald",
"Due": "Verschuldigd",
"Scan": "Scannen",
"Copy": "Kopiëren",
"Conversion": "Omzetting",
"Open in wallet": "Wallet openen",
"CompletePay_Body": "Om de betaling te vervoledigen, bedankt om {{btcDue}} {{cryptoCode}} naar het hieronder vemelde adres op te sturen.",
"Amount": "Bedrag",
"Address": "Adres",
"Copied": "Gekopieerd",
"ConversionTab_BodyTop": "Je kan altcoins gebruiken die niet ondersteund zijn door de verkoper, om {{btcDue}} {{cryptoCode}} te betalen.",
"ConversionTab_BodyDesc": "Deze dienst wordt door een externe partij geleverd. Bijgevolg, hebben we geen zicht over jouw fondsen. De factuur wordt pas als betaald beschouwd, wanneer de fondsen door de blockchain aanvaard zijn {{ cryptoCode }}.",
"Shapeshift_Button_Text": "Betalen met altcoins",
"ConversionTab_Lightning": "Geen leverancier beschikbaar voor de betalingen op het Lightning Network",
"Invoice expiring soon...": "De factuur verloopt binnenkort...",
"Invoice expired": "Vervallen factuur",
"What happened?": "Wat gebeurde er?",
"InvoiceExpired_Body_1": "De factuur is vervallen. Een factuur is alleen geldig voor {{maxTimeMinutes}} minuten. \nJe kan terug komen naar {{storeName}} als je de betaling opnieuw wilt proberen",
"InvoiceExpired_Body_2": "Als je een betaling uitvoerde, dan werd dit nog niet bevestigd door het netwerk. We hebben je fondsen nog niet ontvangen.",
"InvoiceExpired_Body_3": "Als de transactie niet geaccepteerd werd door het netwerk, zullen je fondsen terug in wallet verschijnen. Afhankelijk van je wallet, kan dit 48 to 72 uren duren.",
"Invoice ID": "Factuurnummer",
"Order ID": "Bestllingsnummer",
"Return to StoreName": "Terug naar {{storeName}}",
"This invoice has been paid": "Deze factuur is betaald",
"This invoice has been archived": "Deze factuur is gearchiveerd",
"Archived_Body": "Bedankt om de winkel te contacteren voor bijstand met of informatie over deze bestelling",
"BOLT 11 Invoice": "BOLT 11 Factuur",
"Node Info": "Node Info",
"txCount": "{{count}} transactie",
"txCount_plural": "{{count}} transacties"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "nl-NL",
"currentLanguage": "Dutch",
"lang": "Taal",
"Awaiting Payment...": "Wachtende op de betaling...",
"Pay with": "Betalen met",
"Contact and Refund Email": "Email adres voor opvolging en terugbetaling",
"Contact_Body": "Bedankt om je email adres in te vullen voor een mogelijke opvolging. We contacteren je indien er een probleem optreedt.",
"Your email": "Je email adres",
"Continue": "Verdergaan",
"Please enter a valid email address": "Vul een geldig email adres in",
"Order Amount": "Bedrag van je bestelling",
"Network Cost": "Netwerk kosten",
"Already Paid": "Reeds betaald",
"Due": "Verschuldigd",
"Scan": "Scannen",
"Copy": "Kopiëren",
"Conversion": "Omzetting",
"Open in wallet": "Wallet openen",
"CompletePay_Body": "Om de betaling te vervoledigen, bedankt om {{btcDue}} {{cryptoCode}} naar het hieronder vemelde adres op te sturen.",
"Amount": "Bedrag",
"Address": "Adres",
"Copied": "Gekopieerd",
"ConversionTab_BodyTop": "Je kan altcoins gebruiken die niet ondersteund zijn door de verkoper, om {{btcDue}} {{cryptoCode}} te betalen.",
"ConversionTab_BodyDesc": "Deze dienst wordt door een externe partij geleverd. Bijgevolg, hebben we geen zicht over jouw fondsen. De factuur wordt pas als betaald beschouwd, wanneer de fondsen door de blockchain aanvaard zijn {{ cryptoCode }}.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Geen leverancier beschikbaar voor de betalingen op het Lightning Network",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "De factuur verloopt binnenkort...",
"Invoice expired": "Vervallen factuur",
"What happened?": "Wat gebeurde er?",
"InvoiceExpired_Body_1": "De factuur is vervallen. Een factuur is alleen geldig voor {{maxTimeMinutes}} minuten. \nJe kan terug komen naar {{storeName}} als je de betaling opnieuw wilt proberen",
"InvoiceExpired_Body_2": "Als je een betaling uitvoerde, dan werd dit nog niet bevestigd door het netwerk. We hebben je fondsen nog niet ontvangen.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Factuurnummer",
"Order ID": "Bestllingsnummer",
"Return to StoreName": "Terug naar {{storeName}}",
"This invoice has been paid": "Deze factuur is betaald",
"This invoice has been archived": "Deze factuur is gearchiveerd",
"Archived_Body": "Bedankt om de winkel te contacteren voor bijstand met of informatie over deze bestelling",
"BOLT 11 Invoice": "BOLT 11 Factuur",
"Node Info": "Node Info",
"txCount": "{{count}} transactie",
"txCount_plural": "{{count}} transacties"
}

View File

@ -1,45 +1,47 @@
{
"code": "np-NP",
"currentLanguage": "नेपाली",
"lang": "भाषा",
"Awaiting Payment...": "भुक्तानी पर्खँदै...",
"Pay with": "तिर्नुहोस्",
"Contact and Refund Email": "सम्पर्क र फिर्ता इमेल",
"Contact_Body": "कृपया तल ईमेल ठेगाना प्रदान गर्नुहोस्। तपाईंको भुक्तानीको साथमा समस्या छ भने हामी यो ठेगानामा सम्पर्क गर्नेछौं",
"Your email": "तिम्रो इमेल",
"Continue": "जारी राख्नुहोस्",
"Please enter a valid email address": "कृपया वैध ईमेल ठेगाना प्रविष्ट गर्नुहोस्",
"Order Amount": "अर्डर रकम",
"Network Cost": "नेटवर्क लागत",
"Already Paid": "तिरिसकेको",
"Due": "बाँकी",
"Scan": "स्क्यान गर्नुहोस्",
"Copy": "कापी",
"Conversion": "रूपान्तरण",
"Open in wallet": "वालेटमा खोल्नुहोस्",
"CompletePay_Body": "आफ्नो भुक्तानी पूरा गर्न, कृपया तल ठेगानामा {{btcDue}} {{cryptoCode}} पठाउनुहोस्",
"Amount": "राशि",
"Address": "ठेगाना",
"Copied": "प्रतिलिपि गरियो",
"ConversionTab_BodyTop": "तपाईं एक व्यापारी को सीधा समर्थन भन्दा Altcoins अन्य को उपयोग को {{btcDue}} {{cryptoCode}} भुगतान गर्न सक्छन्",
"ConversionTab_BodyDesc": "यो सेवा तेस्रो पार्टी द्वारा प्रदान गरिएको छ। कृपया ध्यान राख्नुहोस् कि हामीसँग कुनै नियन्त्रण छैन कि कसरी प्रदायकहरूले तपाईंको रकम अगाडि बढ्नेछन्। रकम प्राप्त {{cryptoCode}} Blockchain भएको बेलामा इनभ्वाइस मात्र भुक्तानी चिन्ह लगाइनेछ",
"Shapeshift_Button_Text": "तिर्नुहोस् Altcoins",
"ConversionTab_Lightning": "Lightning Network भुक्तानीको लागि कुनै परिवर्तन प्रदायकहरू उपलब्ध छैनन्",
"Invoice expiring soon...": "चलानीको म्याद सकियो",
"Invoice expired": "इनभ्वाइस समाप्त भयो",
"What happened?": "के भयो",
"InvoiceExpired_Body_1": "यो चलानी समाप्त भएको छ। इनभ्वाइस {{maxTimeMinutes}} मिनेटको लागि मात्र वैध छ। \nतपाइँ आफ्नो भुक्तानी पुन: पेश गर्न चाहानुहुन्छ यदि तपाइँ {{ storeName }} भुक्तानी पठाउन प्रयास गर्नुभयो भने।",
"InvoiceExpired_Body_2": "भने यो अझै नेटवर्कद्वारा स्वीकार गरिएको छैन। हामीले अझै सम्म तपाईंको रकम प्राप्त गरेका छैनौ।",
"InvoiceExpired_Body_3": "यदि लेनदेन नेटवर्क द्वारा स्वीकार गरेन भने, रकम तपाईंको वालेटमा फेरि खर्चयोग्य हुनेछ। तपाईंको वालेटको आधारमा, यो 48-72 घण्टा लाग्न सक्छ",
"Invoice ID": "चलानी आईडी",
"Order ID": "अर्डर आईडी",
"Return to StoreName": "यसलाई फर्काउनुहोस् {{storeName}}",
"This invoice has been paid": "इनभ्वाइस भुक्तानी गरिएको छ",
"This invoice has been archived": "इनभ्वाइस संग्रहीत गरिएको छ",
"Archived_Body": "कृपया स्टोर जानकारी वा सहयोगको लागि स्टोरलाई सम्पर्क गर्नुहोस्",
"BOLT 11 Invoice": "BOLT 11 इनभ्वाइस",
"Node Info": "नोड जानकारी",
"txCount": "{{count}} लेनदेन",
"txCount_plural": "{{count}} लेनदेन"
}
{
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "np-NP",
"currentLanguage": "नेपाली",
"lang": "भाषा",
"Awaiting Payment...": "भुक्तानी पर्खँदै...",
"Pay with": "तिर्नुहोस्",
"Contact and Refund Email": "सम्पर्क र फिर्ता इमेल",
"Contact_Body": "कृपया तल ईमेल ठेगाना प्रदान गर्नुहोस्। तपाईंको भुक्तानीको साथमा समस्या छ भने हामी यो ठेगानामा सम्पर्क गर्नेछौं",
"Your email": "तिम्रो इमेल",
"Continue": "जारी राख्नुहोस्",
"Please enter a valid email address": "कृपया वैध ईमेल ठेगाना प्रविष्ट गर्नुहोस्",
"Order Amount": "अर्डर रकम",
"Network Cost": "नेटवर्क लागत",
"Already Paid": "तिरिसकेको",
"Due": "बाँकी",
"Scan": "स्क्यान गर्नुहोस्",
"Copy": "कापी",
"Conversion": "रूपान्तरण",
"Open in wallet": "वालेटमा खोल्नुहोस्",
"CompletePay_Body": "आफ्नो भुक्तानी पूरा गर्न, कृपया तल ठेगानामा {{btcDue}} {{cryptoCode}} पठाउनुहोस्",
"Amount": "राशि",
"Address": "ठेगाना",
"Copied": "प्रतिलिपि गरियो",
"ConversionTab_BodyTop": "तपाईं एक व्यापारी को सीधा समर्थन भन्दा Altcoins अन्य को उपयोग को {{btcDue}} {{cryptoCode}} भुगतान गर्न सक्छन्",
"ConversionTab_BodyDesc": "यो सेवा तेस्रो पार्टी द्वारा प्रदान गरिएको छ। कृपया ध्यान राख्नुहोस् कि हामीसँग कुनै नियन्त्रण छैन कि कसरी प्रदायकहरूले तपाईंको रकम अगाडि बढ्नेछन्। रकम प्राप्त {{cryptoCode}} Blockchain भएको बेलामा इनभ्वाइस मात्र भुक्तानी चिन्ह लगाइनेछ",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Lightning Network भुक्तानीको लागि कुनै परिवर्तन प्रदायकहरू उपलब्ध छैनन्",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "चलानीको म्याद सकियो",
"Invoice expired": "इनभ्वाइस समाप्त भयो",
"What happened?": "के भयो",
"InvoiceExpired_Body_1": "यो चलानी समाप्त भएको छ। इनभ्वाइस {{maxTimeMinutes}} मिनेटको लागि मात्र वैध छ। \nतपाइँ आफ्नो भुक्तानी पुन: पेश गर्न चाहानुहुन्छ यदि तपाइँ {{ storeName }} भुक्तानी पठाउन प्रयास गर्नुभयो भने।",
"InvoiceExpired_Body_2": "भने यो अझै नेटवर्कद्वारा स्वीकार गरिएको छैन। हामीले अझै सम्म तपाईंको रकम प्राप्त गरेका छैनौ।",
"InvoiceExpired_Body_3": "",
"Invoice ID": "चलानी आईडी",
"Order ID": "अर्डर आईडी",
"Return to StoreName": "यसलाई फर्काउनुहोस् {{storeName}}",
"This invoice has been paid": "इनभ्वाइस भुक्तानी गरिएको छ",
"This invoice has been archived": "इनभ्वाइस संग्रहीत गरिएको छ",
"Archived_Body": "कृपया स्टोर जानकारी वा सहयोगको लागि स्टोरलाई सम्पर्क गर्नुहोस्",
"BOLT 11 Invoice": "BOLT 11 इनभ्वाइस",
"Node Info": "नोड जानकारी",
"txCount": "{{count}} लेनदेन",
"txCount_plural": "{{count}} लेनदेन"
}

View File

@ -0,0 +1,47 @@
{
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "pl",
"currentLanguage": "Polski",
"lang": "Język",
"Awaiting Payment...": "Oczekiwanie na płatność...",
"Pay with": "Płać z",
"Contact and Refund Email": "Email do kontaktu reklamacji",
"Contact_Body": "Proszę podać adres email poniżej. Jeżeli będzie problem z płatnością użyjemy go do kontaktu",
"Your email": "Twój email",
"Continue": "Kontynuacja",
"Please enter a valid email address": "Proszę podać prawidłowy adres email",
"Order Amount": "Kwota zamówienia",
"Network Cost": "Koszt sieci",
"Already Paid": "Już zapłacone",
"Due": "Z powodu",
"Scan": "Skan",
"Copy": "Kopia",
"Conversion": "Konwersja",
"Open in wallet": "Otwórz w porfelu",
"CompletePay_Body": "Aby dokonać płatności proszę wysłać {{btcDue}} {{cryptoCode}} na adres podany niżej.",
"Amount": "Kwota",
"Address": "Adres",
"Copied": "Skopiowano",
"ConversionTab_BodyTop": "Możesz zapłacić {{btcDue}} {{cryptoCode}} używając altcoina innego niż ten, który akceptuje bezpośrednio sprzedawca.",
"ConversionTab_BodyDesc": "Ten serwis prowadzony jest przez 3-cią stronę. Proszę pamiętać, że nie mamy kontroli nad tym jak dostawcy przekażą twoje fundusze. Płatność będzie uznana za opłaconą, tylko gdy otrzymane zostaną poprzez {{cryptoCode}}",
"ConversionTab_CalculateAmount_Error": "Ponów",
"ConversionTab_LoadCurrencies_Error": "Ponów",
"ConversionTab_Lightning": "Brak dostawcy kowertującego dla płatności Lightning Network",
"ConversionTab_CurrencyList_Select_Option": "Wybierz walutę która będzie konwertowana",
"Invoice expiring soon...": "Płatność wkrótce wygaśnie...",
"Invoice expired": "Płatność wygasła",
"What happened?": "Co się stało?",
"InvoiceExpired_Body_1": "Płatność wygasła. Płatność jest prawidłowa przez {{maxTimeMinutes}} minut. Jeśli chcesz wysłaść płatność ponownie wróć do {{storeName}}.",
"InvoiceExpired_Body_2": "Jeżeli próbowałeś wysłać płatność to nie została ona zaakcepowana przez sieć. Nie otrzymaliśmy jeszcze twoich funduszy.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Płatność ID",
"Order ID": "Zamówienie ID",
"Return to StoreName": "Wróć do {{storeName}}",
"This invoice has been paid": "Płatność wykonana",
"This invoice has been archived": "Płatność została zarchiwizowana",
"Archived_Body": "Proszę skontaktować się ze sprzedawcą w celu uzyskania informacji lub wsparcia",
"BOLT 11 Invoice": "Płatność BOLT 11",
"Node Info": "Informacja o węźle",
"txCount": "{{count}} transakcja",
"txCount_plural": "{{count}} transakcji"
}

View File

@ -1,44 +1,47 @@
{
"code": "pt-BR",
"currentLanguage": "Portuguese (Brazil)",
"lang": "Idioma",
"Awaiting Payment...": "Aguardando o pagamento...",
"Pay with": "Pague com",
"Contact and Refund Email": "E-mail de contato e reembolso",
"Contact_Body": "Por favor, informe um e-mail abaixo. Nós entraremos em contato se algum problema ocorrer com o seu pagamento.",
"Your email": "Seu e-mail",
"Continue": "Continuar",
"Please enter a valid email address": "Por favor, informe um e-mail válido",
"Order Amount": "Valor do pedido",
"Network Cost": "Custo da rede",
"Already Paid": "Já foi pago",
"Due": "Devido",
"Scan": "Escanear",
"Copy": "Copiar",
"Conversion": "Conversão",
"Open in wallet": "Abrir na carteira",
"CompletePay_Body": "Para completar seu pagamento, por favor envie {{btcDue}} {{cryptoCode}} para o endereço abaixo.",
"Amount": "Quantia",
"Address": "Endereço",
"Copied": "Copiado",
"ConversionTab_BodyTop": "Você pode pagar {{btcDue}} {{cryptoCode}} utilizando outras altcoins além das que a loja aceita diretamente.",
"ConversionTab_BodyDesc": "Esse serviço é oferecido por terceiros. Por favor, tenha em mente que não temos nenhum controle sobre como seus fundos serão utilizados. A fatura apenas será marcada como paga quando os fundos forem recebidos na Blockchain {{cryptoCode}}.",
"Shapeshift_Button_Text": "Pagar com Altcoins",
"ConversionTab_Lightning": "Não há provedores de conversão disponíveis para pagamentos via Lightning Network.",
"Invoice expiring soon...": "A fatura está expirando...",
"Invoice expired": "Fatura expirada",
"What happened?": "O que aconteceu?",
"InvoiceExpired_Body_1": "Essa fatura expirou. Uma fatura é válida por apenas {{maxTimeMinutes}} minutos. \nVocê pode voltar para {{storeName}} se quiser enviar o seu pagamento novamente.",
"InvoiceExpired_Body_2": "Se você tentou enviar um pagamento, ele ainda não foi aceito pela rede Bitcoin. Nós ainda não recebemos o valor enviado.",
"InvoiceExpired_Body_3": "Se a transação não for aceita pela rede Bitcoin, o valor voltará para sua carteira. Dependendo da sua carteira, isso pode demorar de 48 a 72 horas.",
"Invoice ID": "Nº da Fatura",
"Order ID": "Nº do Pedido",
"Return to StoreName": "Voltar para {{storeName}}",
"This invoice has been paid": "Essa fatura foi paga",
"This invoice has been archived": "Essa fatura foi arquivada",
"Archived_Body": "Por favor, entre em contato com o estabelecimento para informações e suporte",
"BOLT 11 Invoice": "Fatura BOLT 11",
"Node Info": "Informação de nó",
"txCount": "{{count}} transação",
"txCount_plural": "{{count}} transações"
}
{
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "pt-BR",
"currentLanguage": "Portuguese (Brazil)",
"lang": "Idioma",
"Awaiting Payment...": "Aguardando o pagamento...",
"Pay with": "Pague com",
"Contact and Refund Email": "E-mail de contato e reembolso",
"Contact_Body": "Por favor, informe um e-mail abaixo. Nós entraremos em contato se algum problema ocorrer com o seu pagamento.",
"Your email": "Seu e-mail",
"Continue": "Continuar",
"Please enter a valid email address": "Por favor, informe um e-mail válido",
"Order Amount": "Valor do pedido",
"Network Cost": "Custo da rede",
"Already Paid": "Já foi pago",
"Due": "Devido",
"Scan": "Escanear",
"Copy": "Copiar",
"Conversion": "Conversão",
"Open in wallet": "Abrir na carteira",
"CompletePay_Body": "Para completar seu pagamento, por favor envie {{btcDue}} {{cryptoCode}} para o endereço abaixo.",
"Amount": "Quantia",
"Address": "Endereço",
"Copied": "Copiado",
"ConversionTab_BodyTop": "Você pode pagar {{btcDue}} {{cryptoCode}} utilizando outras altcoins além das que a loja aceita diretamente.",
"ConversionTab_BodyDesc": "Esse serviço é oferecido por terceiros. Por favor, tenha em mente que não temos nenhum controle sobre como seus fundos serão utilizados. A fatura apenas será marcada como paga quando os fundos forem recebidos na Blockchain {{cryptoCode}}.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Não há provedores de conversão disponíveis para pagamentos via Lightning Network.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "A fatura está expirando...",
"Invoice expired": "Fatura expirada",
"What happened?": "O que aconteceu?",
"InvoiceExpired_Body_1": "Essa fatura expirou. Uma fatura é válida por apenas {{maxTimeMinutes}} minutos. \nVocê pode voltar para {{storeName}} se quiser enviar o seu pagamento novamente.",
"InvoiceExpired_Body_2": "Se você tentou enviar um pagamento, ele ainda não foi aceito pela rede Bitcoin. Nós ainda não recebemos o valor enviado.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Nº da Fatura",
"Order ID": "Nº do Pedido",
"Return to StoreName": "Voltar para {{storeName}}",
"This invoice has been paid": "Essa fatura foi paga",
"This invoice has been archived": "Essa fatura foi arquivada",
"Archived_Body": "Por favor, entre em contato com o estabelecimento para informações e suporte",
"BOLT 11 Invoice": "Fatura BOLT 11",
"Node Info": "Informação de nó",
"txCount": "{{count}} transação",
"txCount_plural": "{{count}} transações"
}

View File

@ -1,44 +1,47 @@
{
"code": "pt-PT",
"currentLanguage": "Portuguese",
"lang": "Idioma",
"Awaiting Payment...": "A Aguardar Pagamento...",
"Pay with": "Pague com",
"Contact and Refund Email": "E-mail de Contacto e Reembolso",
"Contact_Body": "Por favor indique um e-mail abaixo. Entraremos em contacto para este endereço se ocorrer algum problema com o seu pagamento.",
"Your email": "O seu e-mail",
"Continue": "Continuar",
"Please enter a valid email address": "Por favor introduza um e-mail válido",
"Order Amount": "Valor da Encomenda",
"Network Cost": "Custo da Rede",
"Already Paid": "Já Pago",
"Due": "Devido",
"Scan": "Digitalizar",
"Copy": "Copiar",
"Conversion": "Conversão",
"Open in wallet": "Abrir na carteira",
"CompletePay_Body": "Para completar o seu pagamento, por favor envie {{btcDue}} {{cryptoCode}} para o endereço abaixo.",
"Amount": "Quantia",
"Address": "Endereço",
"Copied": "Copiado",
"ConversionTab_BodyTop": "Pode pagar {{btcDue}} {{cryptoCode}} utilizando outras altcoins além das que a loja aceita diretamente.",
"ConversionTab_BodyDesc": "Este serviço é oferecido por terceiros. Por favor tenha em mente que não temos qualquer controlo sobre como os seus fundos serão utilizados. A fatura será marcada como paga apenas quando os fundos forem recebidos na Blockchain {{cryptoCode}}.",
"Shapeshift_Button_Text": "Pagar com Altcoins",
"ConversionTab_Lightning": "Não há fornecedores de conversão disponíveis para pagamentos via Lightning Network.",
"Invoice expiring soon...": "A fatura está a expirar...",
"Invoice expired": "Fatura expirada",
"What happened?": "O que aconteceu?",
"InvoiceExpired_Body_1": "Esta fatura expirou. Uma fatura é válida durante {{maxTimeMinutes}} minutos. \nPode voltar para {{storeName}} se quiser enviar o seu pagamento novamente.",
"InvoiceExpired_Body_2": "Se tentou enviar um pagamento, ele ainda não foi aceite pela rede Bitcoin. Nós ainda não recebemos o valor enviado.",
"InvoiceExpired_Body_3": "Se a transação não for aceite pela rede Bitcoin, o valor voltará para sua carteira. Dependendo da sua carteira, isto pode demorar entre 48 e 72 horas.",
"Invoice ID": "Nº da Fatura",
"Order ID": "Nº da Encomenda",
"Return to StoreName": "Voltar para {{storeName}}",
"This invoice has been paid": "Esta fatura foi paga",
"This invoice has been archived": "Esta fatura foi arquivada",
"Archived_Body": "Por favor, entre em contacto com o vendedor para informações e suporte",
"BOLT 11 Invoice": "Fatura BOLT 11",
"Node Info": "Informação do Nó",
"txCount": "{{count}} transação",
"txCount_plural": "{{count}} transações"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "pt-PT",
"currentLanguage": "Portuguese",
"lang": "Idioma",
"Awaiting Payment...": "A Aguardar Pagamento...",
"Pay with": "Pague com",
"Contact and Refund Email": "E-mail de Contacto e Reembolso",
"Contact_Body": "Por favor indique um e-mail abaixo. Entraremos em contacto para este endereço se ocorrer algum problema com o seu pagamento.",
"Your email": "O seu e-mail",
"Continue": "Continuar",
"Please enter a valid email address": "Por favor introduza um e-mail válido",
"Order Amount": "Valor da Encomenda",
"Network Cost": "Custo da Rede",
"Already Paid": "Já Pago",
"Due": "Devido",
"Scan": "Digitalizar",
"Copy": "Copiar",
"Conversion": "Conversão",
"Open in wallet": "Abrir na carteira",
"CompletePay_Body": "Para completar o seu pagamento, por favor envie {{btcDue}} {{cryptoCode}} para o endereço abaixo.",
"Amount": "Quantia",
"Address": "Endereço",
"Copied": "Copiado",
"ConversionTab_BodyTop": "Pode pagar {{btcDue}} {{cryptoCode}} utilizando outras altcoins além das que a loja aceita diretamente.",
"ConversionTab_BodyDesc": "Este serviço é oferecido por terceiros. Por favor tenha em mente que não temos qualquer controlo sobre como os seus fundos serão utilizados. A fatura será marcada como paga apenas quando os fundos forem recebidos na Blockchain {{cryptoCode}}.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Não há fornecedores de conversão disponíveis para pagamentos via Lightning Network.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "A fatura está a expirar...",
"Invoice expired": "Fatura expirada",
"What happened?": "O que aconteceu?",
"InvoiceExpired_Body_1": "Esta fatura expirou. Uma fatura é válida durante {{maxTimeMinutes}} minutos. \nPode voltar para {{storeName}} se quiser enviar o seu pagamento novamente.",
"InvoiceExpired_Body_2": "Se tentou enviar um pagamento, ele ainda não foi aceite pela rede Bitcoin. Nós ainda não recebemos o valor enviado.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Nº da Fatura",
"Order ID": "Nº da Encomenda",
"Return to StoreName": "Voltar para {{storeName}}",
"This invoice has been paid": "Esta fatura foi paga",
"This invoice has been archived": "Esta fatura foi arquivada",
"Archived_Body": "Por favor, entre em contacto com o vendedor para informações e suporte",
"BOLT 11 Invoice": "Fatura BOLT 11",
"Node Info": "Informação do Nó",
"txCount": "{{count}} transação",
"txCount_plural": "{{count}} transações"
}

View File

@ -1,44 +1,47 @@
{
"code": "ru-RU",
"currentLanguage": "русский",
"lang": "язык",
"Awaiting Payment...": "Ожидание оплаты...",
"Pay with": "заплатить",
"Contact and Refund Email": "Контактный адрес электронной почты",
"Contact_Body": "Укажите ниже адрес электронной почты. Мы свяжемся с вами по этому адресу, если у вас возникла проблема с оплатой.",
"Your email": "Ваш адрес электронной почты",
"Continue": "Продолжить",
"Please enter a valid email address": "Пожалуйста, введите действующий адрес электронной почты",
"Order Amount": "Сумма заказа",
"Network Cost": "Ценность сети",
"Already Paid": "Уже оплачено",
"Due": "является обязательной",
"Scan": "просканировать",
"Copy": "Скопировать",
"Conversion": "Конвертация",
"Open in wallet": "Открыть кошелек",
"CompletePay_Body": "Чтобы завершить оплату, отправьте {{btcDue}} {{cryptoCode}} по адресу, указанному ниже.",
"Amount": "Сумма",
"Address": "Адрес",
"Copied": "Скопировано",
"ConversionTab_BodyTop": "Вы можете заплатить {{btcDue}} {{cryptoCode}} используя Altcoins отличные от предпочитаемых продавцом.",
"ConversionTab_BodyDesc": "Эта услуга предоставляется третьим лицом. Пожалуйста, имейте в виду, что мы не контролируем, каким образом провайдеры переедут ваши фондовые средства. Счет будет закрыт только после {{cryptoCode}} Blockchain получения средств.",
"Shapeshift_Button_Text": "заплатить Altcoins",
"ConversionTab_Lightning": "Возможность конвертации Lightning Network платежей отсутствует.",
"Invoice expiring soon...": "Время выставленного счета вскоре истечёт...",
"Invoice expired": "Срок действия выставленного счета истек",
"What happened?": "Что случилось?",
"InvoiceExpired_Body_1": "Срок действия этого счета истек. Счет действителен только {{maxTimeMinutes}} минут. \nвы можете посетить {{storeName}} опять, если захотите оплатить.",
"InvoiceExpired_Body_2": "ваша оплата пока еще не принята системой. Мы еще не получили ваши фонды.",
"InvoiceExpired_Body_3": "если транзакция не пройдёт Ваши фонды будут возвращены в ваш кошелек.в зависимости от Вашего кошелек это может занять от 48 до 72 часов.",
"Invoice ID": "Порядковый номер выставленного счета",
"Order ID": "Порядковый номер заказа",
"Return to StoreName": "возвратить в {{storeName}}",
"This invoice has been paid": "Этот выставленный счет был оплачен",
"This invoice has been archived": "Этот счет был заархивирован.",
"Archived_Body": "ожалуйста, обратитесь в магазин для получения информации о заказе или помощи",
"BOLT 11 Invoice": "счет BOLT 11",
"Node Info": "Информация об устройстве",
"txCount": "{{count}} Сделка",
"txCount_plural": "{{count}} операции"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "ru-RU",
"currentLanguage": "русский",
"lang": "язык",
"Awaiting Payment...": "Ожидание оплаты...",
"Pay with": "заплатить",
"Contact and Refund Email": "Контактный адрес электронной почты",
"Contact_Body": "Укажите ниже адрес электронной почты. Мы свяжемся с вами по этому адресу, если у вас возникла проблема с оплатой.",
"Your email": "Ваш адрес электронной почты",
"Continue": "Продолжить",
"Please enter a valid email address": "Пожалуйста, введите действующий адрес электронной почты",
"Order Amount": "Сумма заказа",
"Network Cost": "Ценность сети",
"Already Paid": "Уже оплачено",
"Due": "является обязательной",
"Scan": "просканировать",
"Copy": "Скопировать",
"Conversion": "Конвертация",
"Open in wallet": "Открыть кошелек",
"CompletePay_Body": "Чтобы завершить оплату, отправьте {{btcDue}} {{cryptoCode}} по адресу, указанному ниже.",
"Amount": "Сумма",
"Address": "Адрес",
"Copied": "Скопировано",
"ConversionTab_BodyTop": "Вы можете заплатить {{btcDue}} {{cryptoCode}} используя Altcoins отличные от предпочитаемых продавцом.",
"ConversionTab_BodyDesc": "Эта услуга предоставляется третьим лицом. Пожалуйста, имейте в виду, что мы не контролируем, каким образом провайдеры переедут ваши фондовые средства. Счет будет закрыт только после {{cryptoCode}} Blockchain получения средств.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Возможность конвертации Lightning Network платежей отсутствует.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Время выставленного счета вскоре истечёт...",
"Invoice expired": "Срок действия выставленного счета истек",
"What happened?": "Что случилось?",
"InvoiceExpired_Body_1": "Срок действия этого счета истек. Счет действителен только {{maxTimeMinutes}} минут. \nвы можете посетить {{storeName}} опять, если захотите оплатить.",
"InvoiceExpired_Body_2": "ваша оплата пока еще не принята системой. Мы еще не получили ваши фонды.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Порядковый номер выставленного счета",
"Order ID": "Порядковый номер заказа",
"Return to StoreName": "возвратить в {{storeName}}",
"This invoice has been paid": "Этот выставленный счет был оплачен",
"This invoice has been archived": "Этот счет был заархивирован.",
"Archived_Body": "ожалуйста, обратитесь в магазин для получения информации о заказе или помощи",
"BOLT 11 Invoice": "счет BOLT 11",
"Node Info": "Информация об устройстве",
"txCount": "{{count}} Сделка",
"txCount_plural": "{{count}} операции"
}

View File

@ -0,0 +1,47 @@
{
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "sk-SK",
"currentLanguage": "Slovenčina",
"lang": "Jazyk",
"Awaiting Payment...": "Očakávam platbu...",
"Pay with": "Zaplatiť s",
"Contact and Refund Email": "E-mail na kontaktovanie a vrátenie platby",
"Contact_Body": "Nižšie zadajte e-mailovú adresu. Ak sa vyskytne problém s Vašou platbou, kontaktujeme Vás na tejto adrese.",
"Your email": "Váš email",
"Continue": "Pokračovať",
"Please enter a valid email address": "Prosím zadajte platnú emailovú adresu",
"Order Amount": "Cena objednávky",
"Network Cost": "Sieťové poplatky",
"Already Paid": "Už zaplatené",
"Due": "Zostáva",
"Scan": "Naskenovať",
"Copy": "Skopírovať",
"Conversion": "Konverzia",
"Open in wallet": "Otvoriť v peňaženke",
"CompletePay_Body": "Pre dokončenie platby, pošlite {{btcDue}} {{cryptoCode}} na nižšie uvedenú adresu.",
"Amount": "Čiaska",
"Address": "Adresa",
"Copied": "Skopírované",
"ConversionTab_BodyTop": "Čiastku {{btcDue}} {{cryptoCode}} môžete zaplatiť aj pomocou altcoinov, ktoré priamo obchodník nepodporuje.",
"ConversionTab_BodyDesc": "Táto služba je poskytovaná treťou stranou. Majte na pamäti, že nemáme žiadnu kontrolu nad tým, ako budú poskytovatelia nakladať s Vašimi prostriedkami. Faktúra bude označená ako zaplatená až po prijatí prostriedkov v {{cryptoCode}} blockchaine.",
"ConversionTab_CalculateAmount_Error": "Skúsiť znova",
"ConversionTab_LoadCurrencies_Error": "Skúsiť znova",
"ConversionTab_Lightning": "Pre platby v Lightning sieti nie sú k dispozícii žiadni poskytovatelia konverzií.",
"ConversionTab_CurrencyList_Select_Option": "Vyberte menu, z ktorej chcete konvertovať",
"Invoice expiring soon...": "Faktúra čoskoro vyprší...",
"Invoice expired": "Platnosť faktúry uplynula",
"What happened?": "Čo sa stalo?",
"InvoiceExpired_Body_1": "Platnosť tejto faktúry vypršala. Faktúra platí iba {{maxTimeMinutes}} minút. \nMôžete sa vrátiť na {{storeName}}, ak chcete platbu znova odoslať.",
"InvoiceExpired_Body_2": "Ak ste sa pokúsili odoslať platbu, sieť ju ešte neprijala. Zatia+l sme nedostali Vaše finančné prostriedky.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "ID faktúry",
"Order ID": "ID objednávky",
"Return to StoreName": "Vrátiť sa na {{storeName}}",
"This invoice has been paid": "Táto faktúra bola zaplatená",
"This invoice has been archived": "Táto faktúra bola archivovaná",
"Archived_Body": "Ak potrebujete informácie o objednávke alebo pomoc, obráťte sa na obchodníka",
"BOLT 11 Invoice": "Faktúra BOLT 11",
"Node Info": "Informácia o uzle",
"txCount": "{{count}} transakcia",
"txCount_plural": "{{count}} transakcií"
}

View File

@ -0,0 +1,47 @@
{
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "sr",
"currentLanguage": "Srpski",
"lang": "Jezik",
"Awaiting Payment...": "Čeka se uplata...",
"Pay with": "Plati sa",
"Contact and Refund Email": "E-mail za kontakt i povraćaj sredstava",
"Contact_Body": "Molimo upišite Vašu e-mail adresu. Kontaktiraćemo Vas ukoliko bude potrebe.",
"Your email": "Vaš email",
"Continue": "Nastavi",
"Please enter a valid email address": "Molimo unesite ispravnu email adresu",
"Order Amount": "Iznos narudžbine",
"Network Cost": "Trošak mreže",
"Already Paid": "Već plaćeno",
"Due": "Za uplatu",
"Scan": "Skeniraj",
"Copy": "Kopiraj",
"Conversion": "Konvertuj",
"Open in wallet": "Otvori u novčaniku",
"CompletePay_Body": "Da kompletirate vašu uplatu molimo pošaljite {{btcDue}} {{cryptoCode}} na adresu ispod.",
"Amount": "Iznos",
"Address": "Adresa",
"Copied": "Kopirano",
"ConversionTab_BodyTop": "Možete platiti {{btcDue}} {{cryptoCode}} pomoću altcoina koje prodavac ne podržava direktno.",
"ConversionTab_BodyDesc": "Ovu usluga pruža treća strana. Vodite računa da nemamo kontroliu nad načinom kako će Vam davatelji usluge proslijediti sredstva. Račun je plaćen tek kada su sredstva primljena na {{cryptoCode}} blokčejnu.",
"ConversionTab_CalculateAmount_Error": "Pokušaj ponovo",
"ConversionTab_LoadCurrencies_Error": "Pokušaj ponovo",
"ConversionTab_Lightning": "Ne postoji provajder koji će konvertovati Lightning Network uplate.",
"ConversionTab_CurrencyList_Select_Option": "Molimo odaberite valutu iz koje se konvertuje",
"Invoice expiring soon...": "Račun uskoro ističe...",
"Invoice expired": "Račun je istekao",
"What happened?": "Šta se desilo?",
"InvoiceExpired_Body_1": "Ovaj račun je istekao. Račun važi samo {{maxTimeMinutes}} minuta. \nMožete se vratiti na {{storeName}} ukoliko želite da ponovo inicirate plaćanje.",
"InvoiceExpired_Body_2": "Ako ste pokušali da pošaljete uplatu, još uvek nije prihvaćena na mreži. Nismo još zaprimili Vašu uplatu.",
"InvoiceExpired_Body_3": "Ukoliko vašu uplatu primimo kasnije, procesuiraćemo vašu narudžbinu ili ćemo vas kontaktirati za povraćaj novca...",
"Invoice ID": "Broj računa",
"Order ID": "Broj narudžbine",
"Return to StoreName": "Vrati se na {{storeName}}",
"This invoice has been paid": "Račun je plaćen.",
"This invoice has been archived": "Račun je arhiviran.",
"Archived_Body": "Molimo kontaktirajte prodaca za bili kakvu informaciju ili pomoć u vezi narudžbine.",
"BOLT 11 Invoice": "BOLT 11 Račun",
"Node Info": "Informacije Node-a",
"txCount": "{{count}} transakcija",
"txCount_plural": "{{count}} transakcije"
}

View File

@ -1,44 +1,47 @@
{
"code": "uk-UA",
"currentLanguage": "Українська",
"lang": "Мова",
"Awaiting Payment...": "Очікування платежу…",
"Pay with": "Оплатити",
"Contact and Refund Email": "Електронна адреса для зв’язку та повернення коштів",
"Contact_Body": "Будь ласка, надайте електронну адресу нижче. Ми зв’яжемось з вами за цією адресою, якщо з вашим платежем виникнуть проблеми.",
"Your email": "Ваша адреса",
"Continue": "Продовжити",
"Please enter a valid email address": "Будь ласка, введіть дійсну електронну адресу",
"Order Amount": "Обсяг замовлення",
"Network Cost": "Мережева вартість",
"Already Paid": "Вже сплачено",
"Due": "До сплати",
"Scan": "Сканувати",
"Copy": "Копіювати",
"Conversion": "Конверсія",
"Open in wallet": "Відкрити гаманець",
"CompletePay_Body": "Для завершення платежу, будь ласка, надішліть {{btcDue}} {{cryptoCode}} на адресу нижче.",
"Amount": "Сума",
"Address": "Адреса",
"Copied": "Скопійовано",
"ConversionTab_BodyTop": "Ви можете сплатити {{btcDue}} {{cryptoCode}} використовуючи альткоїни, що відрізняються від тих, що підтримує продавець.",
"ConversionTab_BodyDesc": "Послуга надається третьою особою. Будь ласка, майте на увазі, що ми не контролюємо те, як провайдери пересилатимуть ваші кошти. Рахунок буде позначений як сплачений тільки після надходження коштів на блокчейн {{cryptoCode}}.",
"Shapeshift_Button_Text": "Заплатити альткоїнами",
"ConversionTab_Lightning": "Для платежів з використанням Lightning Network немає провайдерів конверсії.",
"Invoice expiring soon...": "Строк дії рахунку скоро закінчується...",
"Invoice expired": "Строк дії рахунку закінчився",
"What happened?": "Що сталось?",
"InvoiceExpired_Body_1": "Строк дії цього рахунку закінчився. Рахунок дійсний лише {{maxTimeMinutes}} хвилин. \nВи можете повернутись до {{storeName}} якщо ви хочете ще раз надіслати ваш платіж.",
"InvoiceExpired_Body_2": "Якщо ви пробували надіслати платіж, він ще не був прийнятий мережею. Ми ще не отримали ваші кошти.",
"InvoiceExpired_Body_3": "Якщо транзакція не прийнята мережею, кошти будуть знову доступними у вашому гаманці. Залежно від вашого гаманця, це може зайняти приблизно 48-72 годин.",
"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": "Інформація щодо вузла",
"txCount": "транзакція {{count}}",
"txCount_plural": "транзакції {{count}}"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "uk-UA",
"currentLanguage": "Українська",
"lang": "Мова",
"Awaiting Payment...": "Очікування платежу…",
"Pay with": "Оплатити",
"Contact and Refund Email": "Електронна адреса для зв’язку та повернення коштів",
"Contact_Body": "Будь ласка, надайте електронну адресу нижче. Ми зв’яжемось з вами за цією адресою, якщо з вашим платежем виникнуть проблеми.",
"Your email": "Ваша адреса",
"Continue": "Продовжити",
"Please enter a valid email address": "Будь ласка, введіть дійсну електронну адресу",
"Order Amount": "Обсяг замовлення",
"Network Cost": "Мережева вартість",
"Already Paid": "Вже сплачено",
"Due": "До сплати",
"Scan": "Сканувати",
"Copy": "Копіювати",
"Conversion": "Конверсія",
"Open in wallet": "Відкрити гаманець",
"CompletePay_Body": "Для завершення платежу, будь ласка, надішліть {{btcDue}} {{cryptoCode}} на адресу нижче.",
"Amount": "Сума",
"Address": "Адреса",
"Copied": "Скопійовано",
"ConversionTab_BodyTop": "Ви можете сплатити {{btcDue}} {{cryptoCode}} використовуючи альткоїни, що відрізняються від тих, що підтримує продавець.",
"ConversionTab_BodyDesc": "Послуга надається третьою особою. Будь ласка, майте на увазі, що ми не контролюємо те, як провайдери пересилатимуть ваші кошти. Рахунок буде позначений як сплачений тільки після надходження коштів на блокчейн {{cryptoCode}}.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Для платежів з використанням Lightning Network немає провайдерів конверсії.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Строк дії рахунку скоро закінчується...",
"Invoice expired": "Строк дії рахунку закінчився",
"What happened?": "Що сталось?",
"InvoiceExpired_Body_1": "Строк дії цього рахунку закінчився. Рахунок дійсний лише {{maxTimeMinutes}} хвилин. \nВи можете повернутись до {{storeName}} якщо ви хочете ще раз надіслати ваш платіж.",
"InvoiceExpired_Body_2": "Якщо ви пробували надіслати платіж, він ще не був прийнятий мережею. Ми ще не отримали ваші кошти.",
"InvoiceExpired_Body_3": "",
"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": "Інформація щодо вузла",
"txCount": "транзакція {{count}}",
"txCount_plural": "транзакції {{count}}"
}

View File

@ -1,44 +1,47 @@
{
"code": "vi-VN",
"currentLanguage": "Tiếng Việt",
"lang": "Ngôn ngữ",
"Awaiting Payment...": "Đang chờ Thanh toán...",
"Pay with": "Thanh toán bằng",
"Contact and Refund Email": "Email để Liên lạc và Hoàn Tiền",
"Contact_Body": "Hãy cung cấp một địa chỉ email dưới đây. Chúng tôi sẽ liên lạc với bạn tại địa chỉ email này nếu có trục trặc trong thanh toán.",
"Your email": "Email của bạn",
"Continue": "Tiếp tục",
"Please enter a valid email address": "Hãy nhập một địa chỉ email hợp lệ",
"Order Amount": "Tổng giá trị Đơn hàng",
"Network Cost": "Chi phí Mạng lưới",
"Already Paid": "Số Tiền Đã Thanh toán",
"Due": "Số tiền chưa thanh toán",
"Scan": "Quét mã",
"Copy": "Sao chép",
"Conversion": "Chuyển đổi",
"Open in wallet": "Mở ví",
"CompletePay_Body": "Để hoàn tất việc thanh toán của bạn, hãy gửi {{btcDue}} {{cryptoCode}}tới địa chỉ sau.",
"Amount": "Số tiền",
"Address": "Địa chỉ",
"Copied": ã sao chép",
"ConversionTab_BodyTop": "Bạn có thể dùng Altcoin để trả cho {{btcDue}} {{cryptoCode}} chứ không phải dùng những loại tiền người bán chấp nhận trực tiếp.",
"ConversionTab_BodyDesc": "Dịch vụ này được cung cấp bởi một bên thứ ba. Xin vui lòng ghi nhớ, chúng tôi không kiểm soát phương thức các nhà cung cấp chuyển tiếp các nguồn tiền của bạn. Hóa đơn sẽ chỉ được đánh dấu đã thanh toán khi nào các nguồn tiền đã được nhận bởi Chuỗi khối Blockchain {{cryptoCode}}.",
"Shapeshift_Button_Text": "Thanh toán bằng Altcoins",
"ConversionTab_Lightning": "Không có nhà cung cấp nào có dịch vụ chuyển đổi cho các phương thức thanh toán Lightning Network.",
"Invoice expiring soon...": "Hóa đơn sắp quá hạn...",
"Invoice expired": "Hóa đơn đã quá hạn",
"What happened?": "Đã xảy ra chuyện gì?",
"InvoiceExpired_Body_1": "Hóa đơn này đã quá hạn. Một hóa đơn chỉ có giá trị trong {{maxTimeMinutes}} phút. \nNếu muốn đệ trình việc thanh toán một lần nữa, bạn có thể quay lại {{storeName}}.",
"InvoiceExpired_Body_2": "Nếu bạn vừa định gửi thanh toán, thì hệ thống vẫn chưa chấp nhận thanh toán của bạn. Chúng tôi vẫn chưa nhận được tiền của bạn.",
"InvoiceExpired_Body_3": "Nếu giao dịch không được hệ thống chấp nhận, các khoản tiền của bạn sẽ được phân bổ trở lại ví của bạn để bạn tiêu dùng. Tuỳ thuộc vào ví của bạn, việc này có thể mất 48-72 giờ.",
"Invoice ID": "Số Hóa đơn",
"Order ID": "Số Đơn hàng",
"Return to StoreName": "Quay lại {{storeName}}",
"This invoice has been paid": "Hóa đơn này đã được thanh toán",
"This invoice has been archived": "Hóa đơn này đã được lưu trữ",
"Archived_Body": "Hãy liên lạc với cửa hàng để biết thông tin về đơn hàng hoặc nếu cần trợ giúp",
"BOLT 11 Invoice": "Hóa đơn BOLT 11",
"Node Info": "Nút Thông tin",
"txCount": "{{count}} giao dịch",
"txCount_plural": "{{count}} các giao dịch"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "vi-VN",
"currentLanguage": "Tiếng Việt",
"lang": "Ngôn ngữ",
"Awaiting Payment...": "Đang chờ Thanh toán...",
"Pay with": "Thanh toán bằng",
"Contact and Refund Email": "Email để Liên lạc và Hoàn Tiền",
"Contact_Body": "Hãy cung cấp một địa chỉ email dưới đây. Chúng tôi sẽ liên lạc với bạn tại địa chỉ email này nếu có trục trặc trong thanh toán.",
"Your email": "Email của bạn",
"Continue": "Tiếp tục",
"Please enter a valid email address": "Hãy nhập một địa chỉ email hợp lệ",
"Order Amount": "Tổng giá trị Đơn hàng",
"Network Cost": "Chi phí Mạng lưới",
"Already Paid": "Số Tiền Đã Thanh toán",
"Due": "Số tiền chưa thanh toán",
"Scan": "Quét mã",
"Copy": "Sao chép",
"Conversion": "Chuyển đổi",
"Open in wallet": "Mở ví",
"CompletePay_Body": "Để hoàn tất việc thanh toán của bạn, hãy gửi {{btcDue}} {{cryptoCode}}tới địa chỉ sau.",
"Amount": "Số tiền",
"Address": ịa ch",
"Copied": "Đã sao chép",
"ConversionTab_BodyTop": "Bạn có thể dùng Altcoin để trả cho {{btcDue}} {{cryptoCode}} chứ không phải dùng những loại tiền người bán chấp nhận trực tiếp.",
"ConversionTab_BodyDesc": "Dịch vụ này được cung cấp bởi một bên thứ ba. Xin vui lòng ghi nhớ, chúng tôi không kiểm soát phương thức các nhà cung cấp chuyển tiếp các nguồn tiền của bạn. Hóa đơn sẽ chỉ được đánh dấu đã thanh toán khi nào các nguồn tiền đã được nhận bởi Chuỗi khối Blockchain {{cryptoCode}}.",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "Không có nhà cung cấp nào có dịch vụ chuyển đổi cho các phương thức thanh toán Lightning Network.",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "Hóa đơn sắp quá hạn...",
"Invoice expired": "Hóa đơn đã quá hạn",
"What happened?": "Đã xảy ra chuyện gì?",
"InvoiceExpired_Body_1": "Hóa đơn này đã quá hạn. Một hóa đơn chỉ có giá trị trong {{maxTimeMinutes}} phút. \nNếu muốn đệ trình việc thanh toán một lần nữa, bạn có thể quay lại {{storeName}}.",
"InvoiceExpired_Body_2": "Nếu bạn vừa định gửi thanh toán, thì hệ thống vẫn chưa chấp nhận thanh toán của bạn. Chúng tôi vẫn chưa nhận được tiền của bạn.",
"InvoiceExpired_Body_3": "",
"Invoice ID": "Số Hóa đơn",
"Order ID": "Số Đơn hàng",
"Return to StoreName": "Quay lại {{storeName}}",
"This invoice has been paid": "Hóa đơn này đã được thanh toán",
"This invoice has been archived": "Hóa đơn này đã được lưu trữ",
"Archived_Body": "Hãy liên lạc với cửa hàng để biết thông tin về đơn hàng hoặc nếu cần trợ giúp",
"BOLT 11 Invoice": "Hóa đơn BOLT 11",
"Node Info": "Nút Thông tin",
"txCount": "{{count}} giao dịch",
"txCount_plural": "{{count}} các giao dịch"
}

View File

@ -1,44 +1,47 @@
{
"code": "zh-SP",
"currentLanguage": "中文(简体)",
"lang": "语言",
"Awaiting Payment...": "等待付款中...",
"Pay with": "支付方式",
"Contact and Refund Email": "联络和退款邮箱",
"Contact_Body": "请输入您的邮箱地址。如果您的支付出现问题,我们将会通过此邮箱地址与您联系。",
"Your email": "您的邮箱",
"Continue": "继续",
"Please enter a valid email address": "请输入有效的邮箱地址",
"Order Amount": "订单总额",
"Network Cost": "网络费用",
"Already Paid": "已支付",
"Due": "支付",
"Scan": "扫一扫",
"Copy": "复制",
"Conversion": "兑换支付",
"Open in wallet": "在钱包中打开",
"CompletePay_Body": "请发送{{btcDue}} {{cryptoCode}}至下方地址以完成支付。",
"Amount": "金额",
"Address": "地址",
"Copied": "已复制",
"ConversionTab_BodyTop": "您也可以使用商家支持以外的其他altcoins支付{{btcDue}} {{cryptoCode}}。",
"ConversionTab_BodyDesc": "请您注意,这项服务由第三方应用提供,所以我们无法直接获悉和操控第三方应用的具体兑换过程。订单只有在支付款被{{cryptocode}}的区块链接收之后,才会显示已支付。",
"Shapeshift_Button_Text": "Altcoins支付",
"ConversionTab_Lightning": "闪电网络不支持其他altcoins兑换支付。",
"Invoice expiring soon...": "订单即将过期...",
"Invoice expired": "订单已过期 ",
"What happened?": "发生了什么?",
"InvoiceExpired_Body_1": "您的订单已过期。一个订单只在{{maxTimeMinutes}}分钟内有效。 \n如果您想要再次支付可返回{{storeName}}。",
"InvoiceExpired_Body_2": "如果您已支付,但付款没有被网络确认接收。这说明我方目前还未收到您的支付款。",
"InvoiceExpired_Body_3": "如果您的支付最终被网络拒绝支付款将被返回您的钱包。根据您使用的钱包不同可能需要48-72小时。",
"Invoice ID": "订单号",
"Order 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}}笔交易"
}
"NOTICE_WARN": "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/",
"code": "zh-SP",
"currentLanguage": "中文(简体)",
"lang": "语言",
"Awaiting Payment...": "等待付款中...",
"Pay with": "支付方式",
"Contact and Refund Email": "联络和退款邮箱",
"Contact_Body": "请输入您的邮箱地址。如果您的支付出现问题,我们将会通过此邮箱地址与您联系。",
"Your email": "您的邮箱",
"Continue": "继续",
"Please enter a valid email address": "请输入有效的邮箱地址",
"Order Amount": "订单总额",
"Network Cost": "网络费用",
"Already Paid": "支付",
"Due": "待支付",
"Scan": "扫一扫",
"Copy": "复制",
"Conversion": "兑换支付",
"Open in wallet": "在钱包中打开",
"CompletePay_Body": "请发送{{btcDue}} {{cryptoCode}}至下方地址以完成支付。",
"Amount": "金额",
"Address": "地址",
"Copied": "已复制",
"ConversionTab_BodyTop": "您也可以使用商家支持以外的其他altcoins支付{{btcDue}} {{cryptoCode}}。",
"ConversionTab_BodyDesc": "请您注意,这项服务由第三方应用提供,所以我们无法直接获悉和操控第三方应用的具体兑换过程。订单只有在支付款被{{cryptocode}}的区块链接收之后,才会显示已支付",
"ConversionTab_CalculateAmount_Error": "Retry",
"ConversionTab_LoadCurrencies_Error": "Retry",
"ConversionTab_Lightning": "闪电网络不支持其他altcoins兑换支付。",
"ConversionTab_CurrencyList_Select_Option": "Please select a currency to convert from",
"Invoice expiring soon...": "订单即将过期...",
"Invoice expired": "订单已过期 ",
"What happened?": "发生了什么?",
"InvoiceExpired_Body_1": "您的订单已过期。一个订单只在{{maxTimeMinutes}}分钟内有效。 \n如果您想要再次支付可返回{{storeName}}。",
"InvoiceExpired_Body_2": "如果您已支付,但付款没有被网络确认接收。这说明我方目前还未收到您的支付款。",
"InvoiceExpired_Body_3": "",
"Invoice ID": "订单",
"Order 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}}笔交易"
}

Some files were not shown because too many files have changed in this diff Show More