mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-03 09:29:10 +01:00
Support Topup Invoices in Apps
This commit is contained in:
parent
9df4429fc2
commit
7d2aa28e1f
12 changed files with 129 additions and 89 deletions
|
@ -114,7 +114,7 @@ namespace BTCPayServer.Controllers
|
|||
[DomainMappingConstraint(AppType.PointOfSale)]
|
||||
public async Task<IActionResult> ViewPointOfSale(string appId,
|
||||
PosViewType viewType,
|
||||
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal amount,
|
||||
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? amount,
|
||||
string email,
|
||||
string orderId,
|
||||
string notificationUrl,
|
||||
|
@ -136,7 +136,7 @@ namespace BTCPayServer.Controllers
|
|||
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId, viewType = viewType });
|
||||
}
|
||||
string title = null;
|
||||
var price = 0.0m;
|
||||
decimal? price = null;
|
||||
Dictionary<string, InvoiceSupportedTransactionCurrency> paymentMethods = null;
|
||||
ViewPointOfSaleViewModel.Item choice = null;
|
||||
if (!string.IsNullOrEmpty(choiceKey))
|
||||
|
@ -146,9 +146,17 @@ namespace BTCPayServer.Controllers
|
|||
if (choice == null)
|
||||
return NotFound();
|
||||
title = choice.Title;
|
||||
price = choice.Price.Value;
|
||||
if (amount > price)
|
||||
price = amount;
|
||||
if (choice.Custom == "topup")
|
||||
{
|
||||
price = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
price = choice.Price.Value;
|
||||
if (amount > price)
|
||||
price = amount;
|
||||
}
|
||||
|
||||
|
||||
if (choice.Inventory.HasValue)
|
||||
{
|
||||
|
@ -277,10 +285,7 @@ namespace BTCPayServer.Controllers
|
|||
[DomainMappingConstraintAttribute(AppType.Crowdfund)]
|
||||
public async Task<IActionResult> ContributeToCrowdfund(string appId, ContributeToCrowdfund request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.Amount <= 0)
|
||||
{
|
||||
return NotFound("Please provide an amount greater than 0");
|
||||
}
|
||||
|
||||
var app = await _AppService.GetApp(appId, AppType.Crowdfund, true);
|
||||
|
||||
if (app == null)
|
||||
|
@ -307,7 +312,7 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
var store = await _AppService.GetStore(app);
|
||||
var title = settings.Title;
|
||||
var price = request.Amount;
|
||||
decimal? price = request.Amount;
|
||||
Dictionary<string, InvoiceSupportedTransactionCurrency> paymentMethods = null;
|
||||
ViewPointOfSaleViewModel.Item choice = null;
|
||||
if (!string.IsNullOrEmpty(request.ChoiceKey))
|
||||
|
@ -317,11 +322,17 @@ namespace BTCPayServer.Controllers
|
|||
if (choice == null)
|
||||
return NotFound("Incorrect option provided");
|
||||
title = choice.Title;
|
||||
price = choice.Price.Value;
|
||||
if (request.Amount > price)
|
||||
price = request.Amount;
|
||||
|
||||
|
||||
if (choice.Custom == "topup")
|
||||
{
|
||||
price = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
price = choice.Price.Value;
|
||||
if (request.Amount > price)
|
||||
price = request.Amount;
|
||||
}
|
||||
if (choice.Inventory.HasValue)
|
||||
{
|
||||
if (choice.Inventory <= 0)
|
||||
|
@ -329,14 +340,21 @@ namespace BTCPayServer.Controllers
|
|||
return NotFound("Option was out of stock");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (choice?.PaymentMethods?.Any() is true)
|
||||
{
|
||||
paymentMethods = choice?.PaymentMethods.ToDictionary(s => s,
|
||||
s => new InvoiceSupportedTransactionCurrency() { Enabled = true });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (request.Amount < 0)
|
||||
{
|
||||
return NotFound("Please provide an amount greater than 0");
|
||||
}
|
||||
|
||||
price = null;
|
||||
}
|
||||
|
||||
if (!isAdmin && (settings.EnforceTargetAmount && info.TargetAmount.HasValue && price >
|
||||
(info.TargetAmount - (info.Info.CurrentAmount + info.Info.CurrentPendingAmount))))
|
||||
|
|
|
@ -83,7 +83,7 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
public class ContributeToCrowdfund
|
||||
{
|
||||
public ViewCrowdfundViewModel ViewCrowdfundViewModel { get; set; }
|
||||
[Required] public decimal Amount { get; set; }
|
||||
[Required] public decimal? Amount { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string ChoiceKey { get; set; }
|
||||
public bool RedirectToCheckout { get; set; }
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
public string Image { get; set; }
|
||||
public ItemPrice Price { get; set; }
|
||||
public string Title { get; set; }
|
||||
public bool Custom { get; set; }
|
||||
public string Custom { get; set; }
|
||||
public string BuyButtonText { get; set; }
|
||||
public int? Inventory { get; set; } = null;
|
||||
public string[] PaymentMethods { get; set; }
|
||||
|
|
|
@ -289,7 +289,8 @@ namespace BTCPayServer.Services.Apps
|
|||
{
|
||||
var itemNode = new YamlMappingNode();
|
||||
itemNode.Add("title", new YamlScalarNode(item.Title));
|
||||
itemNode.Add("price", new YamlScalarNode(item.Price.Value.ToStringInvariant()));
|
||||
if(item.Custom!= "topup")
|
||||
itemNode.Add("price", new YamlScalarNode(item.Price.Value.ToStringInvariant()));
|
||||
if (!string.IsNullOrEmpty(item.Description))
|
||||
{
|
||||
itemNode.Add("description", new YamlScalarNode(item.Description)
|
||||
|
@ -341,13 +342,16 @@ namespace BTCPayServer.Services.Apps
|
|||
Id = c.Key,
|
||||
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 = Currencies.FormatCurrency(cc.Value.Value, currency)
|
||||
}).Single(),
|
||||
Custom = c.GetDetailString("custom") == "true",
|
||||
Custom = c.GetDetailString("custom"),
|
||||
Price =
|
||||
c.GetDetailString("custom") == "topup"
|
||||
? null
|
||||
: c.GetDetail("price")
|
||||
.Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice()
|
||||
{
|
||||
Value = decimal.Parse(cc.Value.Value, CultureInfo.InvariantCulture),
|
||||
Formatted = Currencies.FormatCurrency(cc.Value.Value, currency)
|
||||
}).Single(),
|
||||
BuyButtonText = c.GetDetailString("buyButtonText"),
|
||||
Inventory = string.IsNullOrEmpty(c.GetDetailString("inventory")) ? (int?)null : int.Parse(c.GetDetailString("inventory"), CultureInfo.InvariantCulture),
|
||||
PaymentMethods = c.GetDetailStringList("payment_methods"),
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{
|
||||
foreach (var error in errors.Errors)
|
||||
{
|
||||
<br />
|
||||
<br/>
|
||||
<span class="text-danger">@error.ErrorMessage</span>
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-if="!items || items.length === 0" class="col-12 text-center">
|
||||
No items.<br />
|
||||
No items.<br/>
|
||||
<button type="button" class="btn btn-link" v-on:click="editItem(-1)" id="btn-add-first">
|
||||
Add your first item
|
||||
</button>
|
||||
|
@ -46,7 +46,7 @@
|
|||
<div class="modal-header">
|
||||
<h5 class="modal-title" v-if="editingItem">{{editingItem.index>=0? "Edit" : "Create"}} item</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" ref="close">
|
||||
<vc:icon symbol="close" />
|
||||
<vc:icon symbol="close"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" v-if="editingItem">
|
||||
|
@ -55,9 +55,15 @@
|
|||
<div class="form-group row">
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" data-required>Title</label>
|
||||
<input type="text" required pattern="[^\*#]+" class="form-control mb-2" v-model="editingItem.title" autofocus ref="txtTitle" />
|
||||
<input type="text" required pattern="[^\*#]+" class="form-control mb-2" v-model="editingItem.title" autofocus ref="txtTitle"/>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label class="form-label">Price</label>
|
||||
<select class="form-select" v-model="editingItem.custom">
|
||||
<option v-for="option in customPriceOptions" :value="option.value">{{option.text}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-3" v-show="editingItem.custom !== 'topup'">
|
||||
<label class="form-label" data-required>Price</label>
|
||||
<input class="form-control mb-2"
|
||||
inputmode="numeric"
|
||||
|
@ -66,18 +72,13 @@
|
|||
min="0"
|
||||
type="number"
|
||||
required
|
||||
v-model="editingItem.price" ref="txtPrice" />
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label class="form-label">Custom price</label>
|
||||
<select class="form-select" v-model="editingItem.custom">
|
||||
<option v-for="option in customPriceOptions" :value="option.value">{{option.text}}</option>
|
||||
</select>
|
||||
v-model="editingItem.price" ref="txtPrice"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Image</label>
|
||||
<input type="text" class="form-control mb-2" pattern="[^\*#]+" v-model="editingItem.image" ref="txtImage" />
|
||||
<input type="text" class="form-control mb-2" pattern="[^\*#]+" v-model="editingItem.image" ref="txtImage"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Description</label>
|
||||
|
@ -85,18 +86,18 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Inventory (leave blank to not use inventory feature)</label>
|
||||
<input type="number" min="0" step="1" class="form-control mb-2" v-model="editingItem.inventory" ref="txtInventory" />
|
||||
<input type="number" min="0" step="1" class="form-control mb-2" v-model="editingItem.inventory" ref="txtInventory"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Id (leave blank to generate from title)</label>
|
||||
<input type="text" required pattern="[^\*#]+" class="form-control mb-2" v-model="editingItem.id" ref="txtId" />
|
||||
<input type="text" required pattern="[^\*#]+" class="form-control mb-2" v-model="editingItem.id" ref="txtId"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Buy Button Text</label>
|
||||
<input type="text" id="BuyButtonText" class="form-control mb-2" v-model="editingItem.buyButtonText" ref="txtBuyButtonText" />
|
||||
<input type="text" id="BuyButtonText" class="form-control mb-2" v-model="editingItem.buyButtonText" ref="txtBuyButtonText"/>
|
||||
</div>
|
||||
<div class="form-group d-flex align-items-center">
|
||||
<input type="checkbox" id="Disabled" class="btcpay-toggle me-2" v-model="editingItem.disabled" />
|
||||
<input type="checkbox" id="Disabled" class="btcpay-toggle me-2" v-model="editingItem.disabled"/>
|
||||
<label class="form-label mb-0">Disabled</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -120,8 +121,9 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
items: [],
|
||||
editingItem: null,
|
||||
customPriceOptions: [
|
||||
{ text: 'No', value: false },
|
||||
{ text: 'Yes', value: true },
|
||||
{ text: 'Fixed', value: false },
|
||||
{ text: 'Minimum', value: true },
|
||||
{ text: 'Topup', value: 'topup' },
|
||||
],
|
||||
elementId: "@Model.templateId"
|
||||
},
|
||||
|
@ -222,7 +224,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
}
|
||||
}
|
||||
|
||||
if (price != null || title != null) {
|
||||
if (title != null) {
|
||||
// Add product to the list
|
||||
result.push({
|
||||
id: id,
|
||||
|
@ -230,7 +232,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
price: price,
|
||||
image: image || null,
|
||||
description: description || '',
|
||||
custom: custom === "true",
|
||||
custom: custom === "topup"? "topup": custom === "true",
|
||||
buyButtonText: buyButtonText,
|
||||
inventory: isNaN(inventory)? null: inventory,
|
||||
paymentMethods: paymentMethods,
|
||||
|
@ -241,13 +243,13 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
this.items = result;
|
||||
},
|
||||
toYml: function(){
|
||||
var template = '';
|
||||
let template = '';
|
||||
// Construct template from the product list
|
||||
for (var key in this.items) {
|
||||
var product = this.items[key],
|
||||
for (const key in this.items) {
|
||||
const product = this.items[key],
|
||||
id = product.id,
|
||||
title = product.title,
|
||||
price = product.price? product.price : 0,
|
||||
price = product.custom === 'topup'? null : product.price??0,
|
||||
image = product.image,
|
||||
description = product.description,
|
||||
custom = product.custom,
|
||||
|
@ -255,36 +257,36 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
inventory = product.inventory,
|
||||
paymentMethods = product.paymentMethods,
|
||||
disabled = product.disabled;
|
||||
|
||||
template += id + ':\n' +
|
||||
' price: ' + parseFloat(price).noExponents() + '\n' +
|
||||
' title: ' + title + '\n';
|
||||
let itemTemplate = id+":\n";
|
||||
itemTemplate += ( product.custom === 'topup'? '' : (' price: ' + parseFloat(price).noExponents() + '\n'));
|
||||
itemTemplate+= ' title: ' + title + '\n';
|
||||
|
||||
if (description) {
|
||||
template += ' description: "' + description.replaceAll("\n", "<br/>").replaceAll('"', '\\"') + '"\n';
|
||||
itemTemplate += ' description: "' + description.replaceAll("\n", "<br/>").replaceAll('"', '\\"') + '"\n';
|
||||
}
|
||||
if (image) {
|
||||
template += ' image: ' + image + '\n';
|
||||
itemTemplate += ' image: ' + image + '\n';
|
||||
}
|
||||
if (inventory) {
|
||||
template += ' inventory: ' + inventory + '\n';
|
||||
itemTemplate += ' inventory: ' + inventory + '\n';
|
||||
}
|
||||
if (custom != null) {
|
||||
template += ' custom: ' + custom + '\n';
|
||||
itemTemplate += ' custom: ' + (custom === "topup"? '"topup"': custom) + '\n';
|
||||
}
|
||||
if (buyButtonText != null && buyButtonText.length > 0) {
|
||||
template += ' buyButtonText: ' + buyButtonText + '\n';
|
||||
itemTemplate += ' buyButtonText: ' + buyButtonText + '\n';
|
||||
}
|
||||
if (disabled != null) {
|
||||
template += ' disabled: ' + disabled.toString() + '\n';
|
||||
itemTemplate += ' disabled: ' + disabled.toString() + '\n';
|
||||
}
|
||||
if(paymentMethods != null && paymentMethods.length > 0){
|
||||
template+= ' payment_methods:\n';
|
||||
itemTemplate+= ' payment_methods:\n';
|
||||
for (var method of paymentMethods){
|
||||
template+= ' - '+method+'\n';
|
||||
itemTemplate+= ' - '+method+'\n';
|
||||
}
|
||||
}
|
||||
template += '\n';
|
||||
itemTemplate += '\n';
|
||||
template+=itemTemplate;
|
||||
}
|
||||
this.getInputElement().val(template);
|
||||
},
|
||||
|
@ -337,7 +339,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
this.errors.push("Image cannot start with \"- \"");
|
||||
}
|
||||
|
||||
if (!this.$refs.txtPrice.checkValidity()) {
|
||||
if (this.editingItem.custom !== "topup" && !this.$refs.txtPrice.checkValidity()) {
|
||||
this.errors.push("Price must be a valid number");
|
||||
}
|
||||
if (!this.$refs.txtTitle.checkValidity()) {
|
||||
|
|
|
@ -19,26 +19,29 @@
|
|||
<div class="card-body">
|
||||
<div class="card-title d-flex align-items-center justify-content-between mb-1">
|
||||
<label class="h5 d-flex align-items-center">
|
||||
@if (vm.Started && !vm.Ended && (item.Price.Value > 0 || item.Custom))
|
||||
@if (vm.Started && !vm.Ended )
|
||||
{
|
||||
<input type="radio" asp-for="ChoiceKey" value="@item.Id" class="form-check-input mt-0 me-2"/>
|
||||
}
|
||||
@(string.IsNullOrEmpty(item.Title) ? item.Id : item.Title)
|
||||
</label>
|
||||
<span class="text-muted">
|
||||
@if (item.Price.Value > 0)
|
||||
@if (item.Price?.Value > 0)
|
||||
{
|
||||
<span>@item.Price.Value</span>
|
||||
<span>@vm.TargetCurrency</span>
|
||||
|
||||
if (item.Custom)
|
||||
if (item.Custom == "true")
|
||||
{
|
||||
@Safe.Raw("or more")
|
||||
}
|
||||
}
|
||||
else if (item.Custom)
|
||||
else if (item.Custom == "topup" || item.Custom == "true" )
|
||||
{
|
||||
@Safe.Raw("Any amount")
|
||||
}else if (item.Custom == "false")
|
||||
{
|
||||
@Safe.Raw("Free")
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -230,7 +230,17 @@
|
|||
</div>
|
||||
<div class="card-footer bg-transparent border-0 pt-0 pb-3">
|
||||
|
||||
<span class="text-muted small">@((item.BuyButtonText ?? Model.ButtonText).Replace("{0}",item.Price.Formatted).Replace("{Price}",item.Price.Formatted))</span>
|
||||
<span class="text-muted small">
|
||||
@{
|
||||
var buttonText = string.IsNullOrEmpty(item.BuyButtonText) ? item.Custom != "false" ? Model.CustomButtonText : Model.ButtonText : item.BuyButtonText;
|
||||
if (item.Custom != "topup")
|
||||
{
|
||||
buttonText = buttonText.Replace("{0}",item.Price.Formatted)
|
||||
?.Replace("{Price}",item.Price.Formatted);
|
||||
}
|
||||
}
|
||||
@Safe.Raw(buttonText)
|
||||
</span>
|
||||
@if (item.Inventory.HasValue)
|
||||
{
|
||||
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
@for (int x = 0; x < Model.Items.Length; x++)
|
||||
{
|
||||
var item = Model.Items[x];
|
||||
var buttonText = (string.IsNullOrEmpty(item.BuyButtonText) ?
|
||||
item.Custom?
|
||||
Model.CustomButtonText :
|
||||
Model.ButtonText
|
||||
: item.BuyButtonText)
|
||||
.Replace("{0}",item.Price.Formatted)
|
||||
.Replace("{Price}",item.Price.Formatted);
|
||||
var buttonText = string.IsNullOrEmpty(item.BuyButtonText) ? item.Custom != "false" ? Model.CustomButtonText : Model.ButtonText : item.BuyButtonText;
|
||||
if (item.Custom != "topup")
|
||||
{
|
||||
buttonText = buttonText.Replace("{0}",item.Price.Formatted)
|
||||
?.Replace("{Price}",item.Price.Formatted);
|
||||
}
|
||||
|
||||
<div class="card px-0" data-id="@x">
|
||||
@if (!String.IsNullOrWhiteSpace(item.Image))
|
||||
{
|
||||
|
@ -35,7 +35,7 @@
|
|||
<div class="card-footer bg-transparent border-0 pb-3">
|
||||
@if (!item.Inventory.HasValue || item.Inventory.Value > 0)
|
||||
{
|
||||
@if (item.Custom)
|
||||
@if (item.Custom == "true")
|
||||
{
|
||||
<form method="post" asp-controller="AppsPublic" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" data-buy>
|
||||
<input type="hidden" name="choicekey" value="@item.Id"/>
|
||||
|
|
|
@ -326,12 +326,12 @@
|
|||
<div class="card-title d-flex justify-content-between">
|
||||
<span class="h5">{{perk.title? perk.title : perk.id}}</span>
|
||||
<span class="text-muted">
|
||||
<template v-if="perk.price.value">
|
||||
<template v-if="perk.price && perk.price.value">
|
||||
{{perk.price.value.noExponents()}}
|
||||
{{targetCurrency}}
|
||||
<template v-if="perk.custom">or more</template>
|
||||
<template v-if="perk.custom === 'true'">or more</template>
|
||||
</template>
|
||||
<template v-else-if="!perk.price.value && perk.custom">
|
||||
<template v-else-if="perk.custom === 'topup' || (!perk.price.value && perk.custom === 'true')">
|
||||
Any amount
|
||||
</template>
|
||||
</span>
|
||||
|
@ -340,8 +340,9 @@
|
|||
|
||||
<div class="input-group" style="max-width:500px;" v-if="expanded" :id="'perk-form'+ perk.id">
|
||||
<input
|
||||
v-if="perk.custom !== 'topup'"
|
||||
:disabled="!active"
|
||||
:readonly="!perk.custom"
|
||||
:readonly="perk.custom !== 'true'"
|
||||
class="form-control hide-number-spin"
|
||||
type="number"
|
||||
v-model="amount"
|
||||
|
@ -349,7 +350,7 @@
|
|||
step="any"
|
||||
placeholder="Contribution Amount"
|
||||
required>
|
||||
<span class="input-group-text">{{targetCurrency}}</span>
|
||||
<span class="input-group-text" v-if="perk.custom !== 'topup'">{{targetCurrency}}</span>
|
||||
<button
|
||||
class="btn btn-primary d-flex align-items-center"
|
||||
v-bind:class="{ 'btn-disabled': loading}"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@model PaymentModel
|
||||
<div>
|
||||
<p>To complete payment, please send <b>@Model.BtcDue @Model.CryptoCode</b> to <b style="word-break: break-word;">@Model.BtcAddress</b></p>
|
||||
<p>To complete payment, please send <b>@Safe.Raw(Model.IsUnsetTopUp? "any amount of": Model.BtcDue) @Model.CryptoCode</b> to <b style="word-break: break-word;">@Model.BtcAddress</b></p>
|
||||
<p>Time remaining: @Model.TimeLeft</p>
|
||||
<p>
|
||||
<a href="@Model.InvoiceBitcoinUrl" style="word-break: break-word;" rel="noreferrer noopener">@Model.InvoiceBitcoinUrl</a>
|
||||
|
|
|
@ -112,7 +112,7 @@ Cart.prototype.getTotalProducts = function() {
|
|||
typeof this.content[key] != 'undefined' &&
|
||||
!this.content[key].disabled
|
||||
) {
|
||||
var price = this.toCents(this.content[key].price.value);
|
||||
var price = this.toCents(this.content[key].price?.value??0);
|
||||
amount += (this.content[key].count * price);
|
||||
}
|
||||
}
|
||||
|
@ -439,7 +439,7 @@ Cart.prototype.listItems = function() {
|
|||
'title': this.escape(item.title),
|
||||
'count': this.escape(item.count),
|
||||
'inventory': this.escape(item.inventory < 0? 99999: item.inventory),
|
||||
'price': this.escape(item.price.formatted)
|
||||
'price': this.escape(item.price?.formatted??0)
|
||||
});
|
||||
list.push($(tableTemplate));
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ addLoadEvent(function (ev) {
|
|||
},
|
||||
computed: {
|
||||
canExpand: function(){
|
||||
return !this.expanded && this.active && (this.perk.price.value || this.perk.custom) && (this.perk.inventory==null || this.perk.inventory > 0)
|
||||
return !this.expanded && this.active && (this.perk.custom=="topup" || this.perk.price.value || this.perk.custom == "true") && (this.perk.inventory==null || this.perk.inventory > 0)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -58,18 +58,20 @@ addLoadEvent(function (ev) {
|
|||
}
|
||||
},
|
||||
setAmount: function (amount) {
|
||||
this.amount = (amount || 0).noExponents();
|
||||
this.amount = this.perk.custom == "topup"? null : (amount || 0).noExponents();
|
||||
this.expanded = false;
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
mounted: function () {
|
||||
this.setAmount(this.perk.price.value);
|
||||
this.setAmount(this.perk.price?.value);
|
||||
},
|
||||
watch: {
|
||||
perk: function (newValue, oldValue) {
|
||||
if (newValue.price.value != oldValue.price.value) {
|
||||
if(newValue.custom === "topup"){
|
||||
this.setAmount();
|
||||
}else if (newValue.price.value != oldValue.price.value) {
|
||||
this.setAmount(newValue.price.value);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue