From e144d2479b54551413d6a3d61e62dedf3c444a09 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Tue, 27 Nov 2018 07:13:09 +0100 Subject: [PATCH] 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 --- BTCPayServer.Tests/UnitTest1.cs | 78 ++++- .../Controllers/InvoiceController.UI.cs | 44 ++- .../InvoicingModels/InvoiceDetailsModel.cs | 1 + BTCPayServer/Views/Invoice/Invoice.cshtml | 292 ++++++++++-------- 4 files changed, 278 insertions(+), 137 deletions(-) diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 5b4fc8451..42df7a557 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -43,6 +43,7 @@ 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; @@ -79,7 +80,7 @@ namespace BTCPayServer.Tests Assert.False(attribute.IsValid("http://")); Assert.False(attribute.IsValid("httpdsadsa.com")); } - + [Fact] [Trait("Fast", "Fast")] public void CanCalculateCryptoDue2() @@ -1505,6 +1506,81 @@ donation: Assert.Equal("donation", donationInvoice.ItemDesc); } } + + [Fact] + [Trait("Fast", "Fast")] + public void PosDataParser_ParsesCorrectly() + { + var testCases = + new List<(string input, Dictionary expectedOutput)>() + { + { (null, new Dictionary())}, + {("", new Dictionary())}, + {("{}", new Dictionary())}, + {("non-json-content", new Dictionary(){ {string.Empty, "non-json-content"}})}, + {("[1,2,3]", new Dictionary(){ {string.Empty, "[1,2,3]"}})}, + {("{ \"key\": \"value\"}", new Dictionary(){ {"key", "value"}})}, + {("{ \"key\": true}", new Dictionary(){ {"key", "True"}})}, + {("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}", + new Dictionary(){ {"key", "value"}, {"key2", "value,value2"}})}, + {("{ invalidjson file here}", new Dictionary(){ {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(null); + + var testCases = + new List<(string input, Dictionary expectedOutput)>() + { + { (null, new Dictionary())}, + {("", new Dictionary())}, + {("{}", new Dictionary())}, + {("non-json-content", new Dictionary(){ {string.Empty, "non-json-content"}})}, + {("[1,2,3]", new Dictionary(){ {string.Empty, "[1,2,3]"}})}, + {("{ \"key\": \"value\"}", new Dictionary(){ {"key", "value"}})}, + {("{ \"key\": true}", new Dictionary(){ {"key", "True"}})}, + {("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}", + new Dictionary(){ {"key", "value"}, {"key2", "value,value2"}})}, + {("{ invalidjson file here}", new Dictionary(){ {String.Empty, "{ invalidjson file here}"}})} + }; + + var tasks = new List(); + 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( + Assert.IsType(result).Model); + Assert.Equal(valueTuple.expectedOutput, viewModel.PosData); + })); + } + + await Task.WhenAll(tasks); + } + } + + [Fact] [Trait("Integration", "Integration")] diff --git a/BTCPayServer/Controllers/InvoiceController.UI.cs b/BTCPayServer/Controllers/InvoiceController.UI.cs index 820bc115f..f500bf914 100644 --- a/BTCPayServer/Controllers/InvoiceController.UI.cs +++ b/BTCPayServer/Controllers/InvoiceController.UI.cs @@ -14,13 +14,13 @@ using BTCPayServer.Payments.Changelly; using BTCPayServer.Payments.Lightning; using BTCPayServer.Security; using BTCPayServer.Services.Invoices; -using BTCPayServer.Services.Rates; 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 { @@ -41,7 +41,6 @@ namespace BTCPayServer.Controllers var dto = invoice.EntityToDTO(_NetworkProvider); var store = await _StoreRepository.FindStore(invoice.StoreId); - InvoiceDetailsModel model = new InvoiceDetailsModel() { StoreName = store.StoreName, @@ -64,7 +63,8 @@ namespace BTCPayServer.Controllers 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)) @@ -586,5 +586,43 @@ namespace BTCPayServer.Controllers { return _UserManager.GetUserId(User); } + + public class PosDataParser + { + public static Dictionary ParsePosData(string posData) + { + var result = new Dictionary(); + 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; + } + } + } } diff --git a/BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs b/BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs index 3a50aac9e..193d20157 100644 --- a/BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs +++ b/BTCPayServer/Models/InvoicingModels/InvoiceDetailsModel.cs @@ -143,5 +143,6 @@ namespace BTCPayServer.Models.InvoicingModels public DateTimeOffset MonitoringDate { get; internal set; } public List Events { get; internal set; } public string NotificationEmail { get; internal set; } + public Dictionary PosData { get; set; } } } diff --git a/BTCPayServer/Views/Invoice/Invoice.cshtml b/BTCPayServer/Views/Invoice/Invoice.cshtml index 63062685e..e7196bfdb 100644 --- a/BTCPayServer/Views/Invoice/Invoice.cshtml +++ b/BTCPayServer/Views/Invoice/Invoice.cshtml @@ -42,128 +42,177 @@ +
+
+

Information

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Store@Model.StoreName
Id@Model.Id
Created date@Model.CreatedDate.ToBrowserDate()
Expiration date@Model.ExpirationDate.ToBrowserDate()
Monitoring date@Model.MonitoringDate.ToBrowserDate()
Transaction speed@Model.TransactionSpeed
Status@Model.Status
Status Exception@Model.StatusException
Refund email@Model.RefundEmail
Order Id@Model.OrderId
Total fiat due@Model.Fiat
Notification Email@Model.NotificationEmail
Notification Url@Model.NotificationUrl
Redirect Url@Model.RedirectUrl
+
+ +
+

Buyer information

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name@Model.BuyerInformation.BuyerName
Email@Model.BuyerInformation.BuyerEmail
Phone@Model.BuyerInformation.BuyerPhone
Address 1@Model.BuyerInformation.BuyerAddress1
Address 2@Model.BuyerInformation.BuyerAddress2
City@Model.BuyerInformation.BuyerCity
State@Model.BuyerInformation.BuyerState
Country@Model.BuyerInformation.BuyerCountry
Zip@Model.BuyerInformation.BuyerZip
+ +

Product information

+ + + + + + + + + + + + + +
Item code@Model.ProductInformation.ItemCode
Item Description@Model.ProductInformation.ItemDesc
Price@Model.ProductInformation.Price @Model.ProductInformation.Currency
+
+
+
-

Information

+

Pos Data

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @foreach (var posDataItem in Model.PosData) + { + + @if (!string.IsNullOrEmpty(posDataItem.Key)) + { + + + + } + else + { + + + } + + } +
Store@Model.StoreName
Id@Model.Id
Created date@Model.CreatedDate.ToBrowserDate()
Expiration date@Model.ExpirationDate.ToBrowserDate()
Monitoring date@Model.MonitoringDate.ToBrowserDate()
Transaction speed@Model.TransactionSpeed
Status@Model.Status
Status Exception@Model.StatusException
Refund email@Model.RefundEmail
Order Id@Model.OrderId
Total fiat due@Model.Fiat
Notification Email@Model.NotificationEmail
Notification Url@Model.NotificationUrl
Redirect Url@Model.RedirectUrl
@posDataItem.Key@posDataItem.Value@posDataItem.Value
+
- +
-

Buyer information

+

Addresses

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name@Model.BuyerInformation.BuyerName
Email@Model.BuyerInformation.BuyerEmail
Phone@Model.BuyerInformation.BuyerPhone
Address 1@Model.BuyerInformation.BuyerAddress1
Address 2@Model.BuyerInformation.BuyerAddress2
City@Model.BuyerInformation.BuyerCity
State@Model.BuyerInformation.BuyerState
Country@Model.BuyerInformation.BuyerCountry
Zip@Model.BuyerInformation.BuyerZip
- -

Product information

- - - - - - - - - - - - + + + + + @foreach (var address in Model.Addresses) + { + var current = address.Current ? "font-weight-bold" : ""; + + + + + } +
Item code@Model.ProductInformation.ItemCode
Item Description@Model.ProductInformation.ItemDesc
Price@Model.ProductInformation.Price @Model.ProductInformation.CurrencyPayment methodAddress
@address.PaymentMethod@address.Destination
-
+ +

Paid summary

@@ -258,29 +307,6 @@ } -
-
-

Addresses

-
- - - - - - - - @foreach (var address in Model.Addresses) - { - var current = address.Current ? "font-weight-bold" : ""; - - - - - } - -
Payment methodAddress
@address.PaymentMethod@address.Destination
-
-