mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 14:22:40 +01:00
Enhance PosData Viewer & add cart to posdata in POS app (#559)
This commit is contained in:
parent
7ea665d884
commit
30bdfeee37
8 changed files with 96 additions and 50 deletions
|
@ -1629,18 +1629,16 @@ donation:
|
||||||
public void PosDataParser_ParsesCorrectly()
|
public void PosDataParser_ParsesCorrectly()
|
||||||
{
|
{
|
||||||
var testCases =
|
var testCases =
|
||||||
new List<(string input, Dictionary<string, string> expectedOutput)>()
|
new List<(string input, Dictionary<string, object> expectedOutput)>()
|
||||||
{
|
{
|
||||||
{ (null, new Dictionary<string, string>())},
|
{ (null, new Dictionary<string, object>())},
|
||||||
{("", new Dictionary<string, string>())},
|
{("", new Dictionary<string, object>())},
|
||||||
{("{}", new Dictionary<string, string>())},
|
{("{}", new Dictionary<string, object>())},
|
||||||
{("non-json-content", new Dictionary<string, string>(){ {string.Empty, "non-json-content"}})},
|
{("non-json-content", new Dictionary<string, object>(){ {string.Empty, "non-json-content"}})},
|
||||||
{("[1,2,3]", new Dictionary<string, string>(){ {string.Empty, "[1,2,3]"}})},
|
{("[1,2,3]", new Dictionary<string, object>(){ {string.Empty, "[1,2,3]"}})},
|
||||||
{("{ \"key\": \"value\"}", new Dictionary<string, string>(){ {"key", "value"}})},
|
{("{ \"key\": \"value\"}", new Dictionary<string, object>(){ {"key", "value"}})},
|
||||||
{("{ \"key\": true}", new Dictionary<string, string>(){ {"key", "True"}})},
|
{("{ \"key\": true}", new Dictionary<string, object>(){ {"key", "True"}})},
|
||||||
{("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}",
|
{("{ invalidjson file here}", new Dictionary<string, object>(){ {String.Empty, "{ invalidjson file here}"}})}
|
||||||
new Dictionary<string, string>(){ {"key", "value"}, {"key2", "value,value2"}})},
|
|
||||||
{("{ invalidjson file here}", new Dictionary<string, string>(){ {String.Empty, "{ invalidjson file here}"}})}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
testCases.ForEach(tuple =>
|
testCases.ForEach(tuple =>
|
||||||
|
@ -1663,18 +1661,16 @@ donation:
|
||||||
var controller = tester.PayTester.GetController<InvoiceController>(null);
|
var controller = tester.PayTester.GetController<InvoiceController>(null);
|
||||||
|
|
||||||
var testCases =
|
var testCases =
|
||||||
new List<(string input, Dictionary<string, string> expectedOutput)>()
|
new List<(string input, Dictionary<string, object> expectedOutput)>()
|
||||||
{
|
{
|
||||||
{ (null, new Dictionary<string, string>())},
|
{ (null, new Dictionary<string, object>())},
|
||||||
{("", new Dictionary<string, string>())},
|
{("", new Dictionary<string, object>())},
|
||||||
{("{}", new Dictionary<string, string>())},
|
{("{}", new Dictionary<string, object>())},
|
||||||
{("non-json-content", new Dictionary<string, string>(){ {string.Empty, "non-json-content"}})},
|
{("non-json-content", new Dictionary<string, object>(){ {string.Empty, "non-json-content"}})},
|
||||||
{("[1,2,3]", new Dictionary<string, string>(){ {string.Empty, "[1,2,3]"}})},
|
{("[1,2,3]", new Dictionary<string, object>(){ {string.Empty, "[1,2,3]"}})},
|
||||||
{("{ \"key\": \"value\"}", new Dictionary<string, string>(){ {"key", "value"}})},
|
{("{ \"key\": \"value\"}", new Dictionary<string, object>(){ {"key", "value"}})},
|
||||||
{("{ \"key\": true}", new Dictionary<string, string>(){ {"key", "True"}})},
|
{("{ \"key\": true}", new Dictionary<string, object>(){ {"key", "True"}})},
|
||||||
{("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}",
|
{("{ invalidjson file here}", new Dictionary<string, object>(){ {String.Empty, "{ invalidjson file here}"}})}
|
||||||
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>();
|
var tasks = new List<Task>();
|
||||||
|
|
|
@ -25,6 +25,7 @@ using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NBitpayClient;
|
using NBitpayClient;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using YamlDotNet.RepresentationModel;
|
using YamlDotNet.RepresentationModel;
|
||||||
using static BTCPayServer.Controllers.AppsController;
|
using static BTCPayServer.Controllers.AppsController;
|
||||||
|
|
||||||
|
@ -155,10 +156,11 @@ namespace BTCPayServer.Controllers
|
||||||
var store = await _AppsHelper.GetStore(app);
|
var store = await _AppsHelper.GetStore(app);
|
||||||
var title = settings.Title;
|
var title = settings.Title;
|
||||||
var price = request.Amount;
|
var price = request.Amount;
|
||||||
|
ViewPointOfSaleViewModel.Item choice = null;
|
||||||
if (!string.IsNullOrEmpty(request.ChoiceKey))
|
if (!string.IsNullOrEmpty(request.ChoiceKey))
|
||||||
{
|
{
|
||||||
var choices = _AppsHelper.Parse(settings.PerksTemplate, settings.TargetCurrency);
|
var choices = _AppsHelper.Parse(settings.PerksTemplate, settings.TargetCurrency);
|
||||||
var choice = choices.FirstOrDefault(c => c.Id == request.ChoiceKey);
|
choice = choices.FirstOrDefault(c => c.Id == request.ChoiceKey);
|
||||||
if (choice == null)
|
if (choice == null)
|
||||||
return NotFound("Incorrect option provided");
|
return NotFound("Incorrect option provided");
|
||||||
title = choice.Title;
|
title = choice.Title;
|
||||||
|
@ -211,7 +213,8 @@ namespace BTCPayServer.Controllers
|
||||||
string orderId,
|
string orderId,
|
||||||
string notificationUrl,
|
string notificationUrl,
|
||||||
string redirectUrl,
|
string redirectUrl,
|
||||||
string choiceKey)
|
string choiceKey,
|
||||||
|
string posData = null)
|
||||||
{
|
{
|
||||||
var app = await _AppsHelper.GetApp(appId, AppType.PointOfSale);
|
var app = await _AppsHelper.GetApp(appId, AppType.PointOfSale);
|
||||||
if (string.IsNullOrEmpty(choiceKey) && amount <= 0)
|
if (string.IsNullOrEmpty(choiceKey) && amount <= 0)
|
||||||
|
@ -227,10 +230,11 @@ namespace BTCPayServer.Controllers
|
||||||
}
|
}
|
||||||
string title = null;
|
string title = null;
|
||||||
var price = 0.0m;
|
var price = 0.0m;
|
||||||
|
ViewPointOfSaleViewModel.Item choice = null;
|
||||||
if (!string.IsNullOrEmpty(choiceKey))
|
if (!string.IsNullOrEmpty(choiceKey))
|
||||||
{
|
{
|
||||||
var choices = _AppsHelper.Parse(settings.Template, settings.Currency);
|
var choices = _AppsHelper.Parse(settings.Template, settings.Currency);
|
||||||
var choice = choices.FirstOrDefault(c => c.Id == choiceKey);
|
choice = choices.FirstOrDefault(c => c.Id == choiceKey);
|
||||||
if (choice == null)
|
if (choice == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
title = choice.Title;
|
title = choice.Title;
|
||||||
|
@ -249,7 +253,7 @@ namespace BTCPayServer.Controllers
|
||||||
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
|
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
|
||||||
var invoice = await _InvoiceController.CreateInvoiceCore(new NBitpayClient.Invoice()
|
var invoice = await _InvoiceController.CreateInvoiceCore(new NBitpayClient.Invoice()
|
||||||
{
|
{
|
||||||
ItemCode = choiceKey ?? string.Empty,
|
ItemCode = choice?.Id,
|
||||||
ItemDesc = title,
|
ItemDesc = title,
|
||||||
Currency = settings.Currency,
|
Currency = settings.Currency,
|
||||||
Price = price,
|
Price = price,
|
||||||
|
@ -258,6 +262,7 @@ namespace BTCPayServer.Controllers
|
||||||
NotificationURL = notificationUrl,
|
NotificationURL = notificationUrl,
|
||||||
RedirectURL = redirectUrl ?? Request.GetDisplayUrl(),
|
RedirectURL = redirectUrl ?? Request.GetDisplayUrl(),
|
||||||
FullNotifications = true,
|
FullNotifications = true,
|
||||||
|
PosData = string.IsNullOrEmpty(posData) ? null : posData
|
||||||
}, store, HttpContext.Request.GetAbsoluteRoot());
|
}, store, HttpContext.Request.GetAbsoluteRoot());
|
||||||
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
|
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ using BTCPayServer.Services.Invoices.Export;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.EntityFrameworkCore.Internal;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBitpayClient;
|
using NBitpayClient;
|
||||||
using NBXplorer;
|
using NBXplorer;
|
||||||
|
@ -679,9 +680,9 @@ namespace BTCPayServer.Controllers
|
||||||
|
|
||||||
public class PosDataParser
|
public class PosDataParser
|
||||||
{
|
{
|
||||||
public static Dictionary<string, string> ParsePosData(string posData)
|
public static Dictionary<string, object> ParsePosData(string posData)
|
||||||
{
|
{
|
||||||
var result = new Dictionary<string,string>();
|
var result = new Dictionary<string,object>();
|
||||||
if (string.IsNullOrEmpty(posData))
|
if (string.IsNullOrEmpty(posData))
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
|
@ -689,7 +690,6 @@ namespace BTCPayServer.Controllers
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
var jObject =JObject.Parse(posData);
|
var jObject =JObject.Parse(posData);
|
||||||
foreach (var item in jObject)
|
foreach (var item in jObject)
|
||||||
{
|
{
|
||||||
|
@ -697,7 +697,14 @@ namespace BTCPayServer.Controllers
|
||||||
switch (item.Value.Type)
|
switch (item.Value.Type)
|
||||||
{
|
{
|
||||||
case JTokenType.Array:
|
case JTokenType.Array:
|
||||||
result.Add(item.Key, string.Join(',', item.Value.AsEnumerable()));
|
var items = item.Value.AsEnumerable().ToList();
|
||||||
|
for (var i = 0; i < items.Count(); i++)
|
||||||
|
{
|
||||||
|
result.Add($"{item.Key}[{i}]", ParsePosData(items[i].ToString()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JTokenType.Object:
|
||||||
|
result.Add(item.Key, ParsePosData(item.Value.ToString()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
result.Add(item.Key, item.Value.ToString());
|
result.Add(item.Key, item.Value.ToString());
|
||||||
|
|
|
@ -143,6 +143,6 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||||
public DateTimeOffset MonitoringDate { get; internal set; }
|
public DateTimeOffset MonitoringDate { get; internal set; }
|
||||||
public List<Data.InvoiceEventData> Events { get; internal set; }
|
public List<Data.InvoiceEventData> Events { get; internal set; }
|
||||||
public string NotificationEmail { get; internal set; }
|
public string NotificationEmail { get; internal set; }
|
||||||
public Dictionary<string, string> PosData { get; set; }
|
public Dictionary<string, object> PosData { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,7 @@
|
||||||
<div class="modal-footer bg-light">
|
<div class="modal-footer bg-light">
|
||||||
<form method="post" asp-antiforgery="false" data-buy>
|
<form method="post" asp-antiforgery="false" data-buy>
|
||||||
<input id="js-cart-amount" class="form-control" type="hidden" name="amount">
|
<input id="js-cart-amount" class="form-control" type="hidden" name="amount">
|
||||||
|
<input id="js-cart-posdata" class="form-control" type="hidden" name="posdata">
|
||||||
<button id="js-cart-pay" class="btn btn-primary btn-lg" type="submit"><b>@Model.CustomButtonText</b></button>
|
<button id="js-cart-pay" class="btn btn-primary btn-lg" type="submit"><b>@Model.CustomButtonText</b></button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -192,24 +192,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h3>Point of Sale Data</h3>
|
<h3>Point of Sale Data</h3>
|
||||||
<table class="table table-sm table-responsive-md">
|
<partial name="PosData" model="@Model.PosData"></partial>
|
||||||
@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>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
37
BTCPayServer/Views/Invoice/PosData.cshtml
Normal file
37
BTCPayServer/Views/Invoice/PosData.cshtml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
@model Dictionary<string, object>
|
||||||
|
|
||||||
|
<table class="table table-sm table-responsive-md">
|
||||||
|
@foreach (var posDataItem in Model)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
@if (!string.IsNullOrEmpty(posDataItem.Key))
|
||||||
|
{
|
||||||
|
<th>@posDataItem.Key</th>
|
||||||
|
<td>
|
||||||
|
@if (posDataItem.Value is string)
|
||||||
|
{
|
||||||
|
@posDataItem.Value
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<partial name="PosData" model="@posDataItem.Value"/>
|
||||||
|
}
|
||||||
|
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<td colspan="2">
|
||||||
|
@if (posDataItem.Value is string)
|
||||||
|
{
|
||||||
|
@posDataItem.Value
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<partial name="PosData" model="@posDataItem.Value"/>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</table>
|
|
@ -21,6 +21,7 @@ function Cart() {
|
||||||
|
|
||||||
this.updateItemsCount();
|
this.updateItemsCount();
|
||||||
this.updateAmount();
|
this.updateAmount();
|
||||||
|
this.updatePosData();
|
||||||
}
|
}
|
||||||
|
|
||||||
Cart.prototype.setCustomAmount = function(amount) {
|
Cart.prototype.setCustomAmount = function(amount) {
|
||||||
|
@ -243,6 +244,7 @@ Cart.prototype.updateAll = function() {
|
||||||
this.updateSummaryTotal();
|
this.updateSummaryTotal();
|
||||||
this.updateTotal();
|
this.updateTotal();
|
||||||
this.updateAmount();
|
this.updateAmount();
|
||||||
|
this.updatePosData();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update number of cart items
|
// Update number of cart items
|
||||||
|
@ -290,6 +292,20 @@ Cart.prototype.updateTip = function(amount) {
|
||||||
Cart.prototype.updateAmount = function() {
|
Cart.prototype.updateAmount = function() {
|
||||||
$('#js-cart-amount').val(this.getTotal(true));
|
$('#js-cart-amount').val(this.getTotal(true));
|
||||||
}
|
}
|
||||||
|
Cart.prototype.updatePosData = function() {
|
||||||
|
|
||||||
|
var result = {
|
||||||
|
cart: this.content,
|
||||||
|
customAmount: this.fromCents(this.getCustomAmount()),
|
||||||
|
discountPercentage: this.discount? parseFloat(this.discount): 0,
|
||||||
|
subTotal: this.fromCents(this.getTotalProducts()),
|
||||||
|
discountAmount: this.fromCents(this.getDiscountAmount(this.totalAmount)),
|
||||||
|
tip: this.tip? this.tip: 0,
|
||||||
|
total: this.getTotal(true)
|
||||||
|
};
|
||||||
|
console.warn(result);
|
||||||
|
$('#js-cart-posdata').val(JSON.stringify(result));
|
||||||
|
}
|
||||||
|
|
||||||
Cart.prototype.resetDiscount = function() {
|
Cart.prototype.resetDiscount = function() {
|
||||||
this.setDiscount(0);
|
this.setDiscount(0);
|
||||||
|
@ -644,6 +660,7 @@ $.fn.inputAmount = function(obj, type) {
|
||||||
|
|
||||||
obj.updateSummaryTotal();
|
obj.updateSummaryTotal();
|
||||||
obj.updateAmount();
|
obj.updateAmount();
|
||||||
|
obj.updatePosData();
|
||||||
obj.emptyCartToggle();
|
obj.emptyCartToggle();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue