mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Add ability to set default payment method for pay button (#3606)
* Add ability to set default payment method for pay button close #3604 * Add "#nullable enable" to UIStoresController * Add PaymentMethodOptionViewModel * Add explicit "Use the store’s default" option
This commit is contained in:
@@ -67,7 +67,8 @@ namespace BTCPayServer.Controllers
|
|||||||
NotificationEmail = model.NotifyEmail,
|
NotificationEmail = model.NotifyEmail,
|
||||||
NotificationURL = model.ServerIpn,
|
NotificationURL = model.ServerIpn,
|
||||||
RedirectURL = model.BrowserRedirect,
|
RedirectURL = model.BrowserRedirect,
|
||||||
FullNotifications = true
|
FullNotifications = true,
|
||||||
|
DefaultPaymentMethod = model.DefaultPaymentMethod
|
||||||
}, store, HttpContext.Request.GetAbsoluteRoot(), cancellationToken: cancellationToken);
|
}, store, HttpContext.Request.GetAbsoluteRoot(), cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
catch (BitpayHttpException e)
|
catch (BitpayHttpException e)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#nullable enable
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -229,7 +230,7 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{storeId}/rates")]
|
[HttpPost("{storeId}/rates")]
|
||||||
public async Task<IActionResult> Rates(RatesViewModel model, string command = null, string storeId = null, CancellationToken cancellationToken = default)
|
public async Task<IActionResult> Rates(RatesViewModel model, string? command = null, string? storeId = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (command == "scripting-on")
|
if (command == "scripting-on")
|
||||||
{
|
{
|
||||||
@@ -243,7 +244,7 @@ namespace BTCPayServer.Controllers
|
|||||||
var exchanges = GetSupportedExchanges();
|
var exchanges = GetSupportedExchanges();
|
||||||
model.SetExchangeRates(exchanges, model.PreferredExchange);
|
model.SetExchangeRates(exchanges, model.PreferredExchange);
|
||||||
model.StoreId = storeId ?? model.StoreId;
|
model.StoreId = storeId ?? model.StoreId;
|
||||||
CurrencyPair[] currencyPairs = null;
|
CurrencyPair[]? currencyPairs = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
currencyPairs = model.DefaultCurrencyPairs?
|
currencyPairs = model.DefaultCurrencyPairs?
|
||||||
@@ -277,7 +278,7 @@ namespace BTCPayServer.Controllers
|
|||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RateRules rules = null;
|
RateRules? rules = null;
|
||||||
if (model.ShowScripting)
|
if (model.ShowScripting)
|
||||||
{
|
{
|
||||||
if (!RateRules.TryParse(model.Script, out rules, out var errors))
|
if (!RateRules.TryParse(model.Script, out rules, out var errors))
|
||||||
@@ -421,6 +422,29 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetCryptoCurrencies(CheckoutAppearanceViewModel vm, Data.StoreData storeData)
|
void SetCryptoCurrencies(CheckoutAppearanceViewModel vm, Data.StoreData storeData)
|
||||||
|
{
|
||||||
|
var choices = GetEnabledPaymentMethodChoices(storeData);
|
||||||
|
var chosen = GetDefaultPaymentMethodChoice(storeData);
|
||||||
|
|
||||||
|
vm.PaymentMethods = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen?.Value);
|
||||||
|
vm.DefaultPaymentMethod = chosen?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
PaymentMethodOptionViewModel.Format[] GetEnabledPaymentMethodChoices(Data.StoreData storeData)
|
||||||
|
{
|
||||||
|
var enabled = storeData.GetEnabledPaymentIds(_NetworkProvider);
|
||||||
|
|
||||||
|
return enabled
|
||||||
|
.Select(o =>
|
||||||
|
new PaymentMethodOptionViewModel.Format()
|
||||||
|
{
|
||||||
|
Name = o.ToPrettyString(),
|
||||||
|
Value = o.ToString(),
|
||||||
|
PaymentId = o
|
||||||
|
}).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
PaymentMethodOptionViewModel.Format? GetDefaultPaymentMethodChoice(Data.StoreData storeData)
|
||||||
{
|
{
|
||||||
var enabled = storeData.GetEnabledPaymentIds(_NetworkProvider);
|
var enabled = storeData.GetEnabledPaymentIds(_NetworkProvider);
|
||||||
var defaultPaymentId = storeData.GetDefaultPaymentId();
|
var defaultPaymentId = storeData.GetDefaultPaymentId();
|
||||||
@@ -431,17 +455,9 @@ namespace BTCPayServer.Controllers
|
|||||||
enabled.FirstOrDefault(e => e.CryptoCode == "BTC" && e.PaymentType == PaymentTypes.LightningLike) ??
|
enabled.FirstOrDefault(e => e.CryptoCode == "BTC" && e.PaymentType == PaymentTypes.LightningLike) ??
|
||||||
enabled.FirstOrDefault();
|
enabled.FirstOrDefault();
|
||||||
}
|
}
|
||||||
var choices = enabled
|
var choices = GetEnabledPaymentMethodChoices(storeData);
|
||||||
.Select(o =>
|
|
||||||
new CheckoutAppearanceViewModel.Format()
|
return defaultChoice is null ? null : choices.FirstOrDefault(c => defaultChoice.ToString().Equals(c.Value, StringComparison.OrdinalIgnoreCase));
|
||||||
{
|
|
||||||
Name = o.ToPrettyString(),
|
|
||||||
Value = o.ToString(),
|
|
||||||
PaymentId = o
|
|
||||||
}).ToArray();
|
|
||||||
var chosen = defaultChoice is null ? null : choices.FirstOrDefault(c => defaultChoice.ToString().Equals(c.Value, StringComparison.OrdinalIgnoreCase));
|
|
||||||
vm.PaymentMethods = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen?.Value);
|
|
||||||
vm.DefaultPaymentMethod = chosen?.Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
@@ -616,7 +632,7 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{storeId}/settings")]
|
[HttpPost("{storeId}/settings")]
|
||||||
public async Task<IActionResult> GeneralSettings(GeneralSettingsViewModel model, string command = null)
|
public async Task<IActionResult> GeneralSettings(GeneralSettingsViewModel model, string? command = null)
|
||||||
{
|
{
|
||||||
bool needUpdate = false;
|
bool needUpdate = false;
|
||||||
if (CurrentStore.StoreName != model.StoreName)
|
if (CurrentStore.StoreName != model.StoreName)
|
||||||
@@ -747,7 +763,7 @@ namespace BTCPayServer.Controllers
|
|||||||
TempData[WellKnownTempData.ErrorMessage] = "Failure to revoke this token.";
|
TempData[WellKnownTempData.ErrorMessage] = "Failure to revoke this token.";
|
||||||
else
|
else
|
||||||
TempData[WellKnownTempData.SuccessMessage] = "Token revoked";
|
TempData[WellKnownTempData.SuccessMessage] = "Token revoked";
|
||||||
return RedirectToAction(nameof(ListTokens), new { storeId = token.StoreId });
|
return RedirectToAction(nameof(ListTokens), new { storeId = token?.StoreId });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -782,7 +798,7 @@ namespace BTCPayServer.Controllers
|
|||||||
Id = model.PublicKey == null ? null : NBitpayClient.Extensions.BitIdExtensions.GetBitIDSIN(new PubKey(model.PublicKey).Compress())
|
Id = model.PublicKey == null ? null : NBitpayClient.Extensions.BitIdExtensions.GetBitIDSIN(new PubKey(model.PublicKey).Compress())
|
||||||
};
|
};
|
||||||
|
|
||||||
string pairingCode = null;
|
string? pairingCode = null;
|
||||||
if (model.PublicKey == null)
|
if (model.PublicKey == null)
|
||||||
{
|
{
|
||||||
tokenRequest.PairingCode = await _TokenRepository.CreatePairingCodeAsync();
|
tokenRequest.PairingCode = await _TokenRepository.CreatePairingCodeAsync();
|
||||||
@@ -807,7 +823,7 @@ namespace BTCPayServer.Controllers
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GeneratedPairingCode { get; set; }
|
public string? GeneratedPairingCode { get; set; }
|
||||||
public WebhookSender WebhookNotificationManager { get; }
|
public WebhookSender WebhookNotificationManager { get; }
|
||||||
public IDataProtector DataProtector { get; }
|
public IDataProtector DataProtector { get; }
|
||||||
|
|
||||||
@@ -880,7 +896,7 @@ namespace BTCPayServer.Controllers
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("/api-access-request")]
|
[Route("/api-access-request")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> RequestPairing(string pairingCode, string selectedStore = null)
|
public async Task<IActionResult> RequestPairing(string pairingCode, string? selectedStore = null)
|
||||||
{
|
{
|
||||||
var userId = GetUserId();
|
var userId = GetUserId();
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
@@ -956,9 +972,9 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetUserId()
|
private string? GetUserId()
|
||||||
{
|
{
|
||||||
if (User.Identity.AuthenticationType != AuthenticationSchemes.Cookie)
|
if (User.Identity?.AuthenticationType != AuthenticationSchemes.Cookie)
|
||||||
return null;
|
return null;
|
||||||
return _UserManager.GetUserId(User);
|
return _UserManager.GetUserId(User);
|
||||||
}
|
}
|
||||||
@@ -990,6 +1006,8 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
Price = null,
|
Price = null,
|
||||||
Currency = storeBlob.DefaultCurrency,
|
Currency = storeBlob.DefaultCurrency,
|
||||||
|
DefaultPaymentMethod = String.Empty,
|
||||||
|
PaymentMethods = GetEnabledPaymentMethodChoices(store),
|
||||||
ButtonSize = 2,
|
ButtonSize = 2,
|
||||||
UrlRoot = appUrl,
|
UrlRoot = appUrl,
|
||||||
PayButtonImageUrl = appUrl + "img/paybutton/pay.svg",
|
PayButtonImageUrl = appUrl + "img/paybutton/pay.svg",
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace BTCPayServer.Data
|
|||||||
return paymentMethodIds;
|
return paymentMethodIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetDefaultPaymentId(this StoreData storeData, PaymentMethodId defaultPaymentId)
|
public static void SetDefaultPaymentId(this StoreData storeData, PaymentMethodId? defaultPaymentId)
|
||||||
{
|
{
|
||||||
storeData.DefaultCrypto = defaultPaymentId?.ToString();
|
storeData.DefaultCrypto = defaultPaymentId?.ToString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using BTCPayServer.Payments;
|
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
|
||||||
@@ -10,18 +9,12 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||||||
{
|
{
|
||||||
public class CheckoutAppearanceViewModel
|
public class CheckoutAppearanceViewModel
|
||||||
{
|
{
|
||||||
public class Format
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string Value { get; set; }
|
|
||||||
public PaymentMethodId PaymentId { get; set; }
|
|
||||||
}
|
|
||||||
public SelectList PaymentMethods { get; set; }
|
public SelectList PaymentMethods { get; set; }
|
||||||
|
|
||||||
public void SetLanguages(LanguageService langService, string defaultLang)
|
public void SetLanguages(LanguageService langService, string defaultLang)
|
||||||
{
|
{
|
||||||
defaultLang = langService.GetLanguages().Any(language => language.Code == 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().OrderBy(o => o.Name);
|
var choices = langService.GetLanguages().Select(o => new PaymentMethodOptionViewModel.Format() { Name = o.DisplayName, Value = o.Code }).ToArray().OrderBy(o => o.Name);
|
||||||
var chosen = choices.FirstOrDefault(f => f.Value == defaultLang) ?? choices.FirstOrDefault();
|
var chosen = choices.FirstOrDefault(f => f.Value == defaultLang) ?? choices.FirstOrDefault();
|
||||||
Languages = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen);
|
Languages = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen);
|
||||||
DefaultLang = chosen.Value;
|
DefaultLang = chosen.Value;
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||||||
public string InvoiceId { get; set; }
|
public string InvoiceId { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public string Currency { get; set; }
|
public string Currency { get; set; }
|
||||||
|
public string DefaultPaymentMethod { get; set; }
|
||||||
|
public PaymentMethodOptionViewModel.Format[] PaymentMethods { get; set; }
|
||||||
public string CheckoutDesc { get; set; }
|
public string CheckoutDesc { get; set; }
|
||||||
public string OrderId { get; set; }
|
public string OrderId { get; set; }
|
||||||
public int ButtonSize { get; set; }
|
public int ButtonSize { get; set; }
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using BTCPayServer.Payments;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Models.StoreViewModels
|
||||||
|
{
|
||||||
|
public class PaymentMethodOptionViewModel
|
||||||
|
{
|
||||||
|
public class Format
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
public PaymentMethodId PaymentId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -159,6 +159,13 @@
|
|||||||
v-model="srvModel.currency" v-on:change="inputChanges"
|
v-model="srvModel.currency" v-on:change="inputChanges"
|
||||||
:class="{'is-invalid': errors.has('currency') }">
|
:class="{'is-invalid': errors.has('currency') }">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group col-md-4" v-if="!srvModel.appIdEndpoint">
|
||||||
|
<label class="form-label" for="defaultPaymentMethod">Default Payment Method</label>
|
||||||
|
<select v-model="srvModel.defaultPaymentMethod" v-on:change="inputChanges" class="form-select" id="default-payment-method">
|
||||||
|
<option value="" selected>Use the store’s default</option>
|
||||||
|
<option v-for="pm in srvModel.paymentMethods" v-bind:value="pm.value">{{pm.name}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-group" v-if="!srvModel.appIdEndpoint">
|
<div class="form-group" v-if="!srvModel.appIdEndpoint">
|
||||||
<label class="form-label" for="description">Checkout Description</label>
|
<label class="form-label" for="description">Checkout Description</label>
|
||||||
<input name="checkoutDesc" type="text" class="form-control" id="description"
|
<input name="checkoutDesc" type="text" class="form-control" id="description"
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ function inputChanges(event, buttonSize) {
|
|||||||
let priceInputName = 'price';
|
let priceInputName = 'price';
|
||||||
let app = srvModel.appIdEndpoint? srvModel.apps.find(value => value.id === srvModel.appIdEndpoint ): null;
|
let app = srvModel.appIdEndpoint? srvModel.apps.find(value => value.id === srvModel.appIdEndpoint ): null;
|
||||||
let allowCurrencySelection = true;
|
let allowCurrencySelection = true;
|
||||||
|
let allowDefaultPaymentMethodSelection = true;
|
||||||
if (app) {
|
if (app) {
|
||||||
if (app.appType.toLowerCase() === 'pointofsale') {
|
if (app.appType.toLowerCase() === 'pointofsale') {
|
||||||
actionUrl = `apps/${app.id}/pos`;
|
actionUrl = `apps/${app.id}/pos`;
|
||||||
@@ -97,6 +98,7 @@ function inputChanges(event, buttonSize) {
|
|||||||
if (actionUrl !== 'api/v1/invoices') {
|
if (actionUrl !== 'api/v1/invoices') {
|
||||||
priceInputName = 'amount';
|
priceInputName = 'amount';
|
||||||
allowCurrencySelection = false;
|
allowCurrencySelection = false;
|
||||||
|
allowDefaultPaymentMethodSelection = false;
|
||||||
srvModel.useModal = false;
|
srvModel.useModal = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,6 +152,15 @@ function inputChanges(event, buttonSize) {
|
|||||||
html += addSlider(srvModel.price, srvModel.min, srvModel.max, srvModel.step, width);
|
html += addSlider(srvModel.price, srvModel.min, srvModel.max, srvModel.step, width);
|
||||||
html += ' </div>\n';
|
html += ' </div>\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
allowDefaultPaymentMethodSelection &&
|
||||||
|
// Only add default payment method to HTML if user explicitly selected it
|
||||||
|
event && event.target.id === 'default-payment-method' && event.target.value !== ""
|
||||||
|
)
|
||||||
|
{
|
||||||
|
html += addInput("defaultPaymentMethod", srvModel.defaultPaymentMethod)
|
||||||
|
}
|
||||||
|
|
||||||
html += srvModel.payButtonText
|
html += srvModel.payButtonText
|
||||||
? `<button type="submit" class="submit" name="submit" style="min-width:${width};min-height:${height};border-radius:4px;border-style:none;background-color:#0f3b21;" title="Pay with BTCPay Server, a Self-Hosted Bitcoin Payment Processor"><span style="color:#fff">${esc(srvModel.payButtonText)}</span>\n` +
|
? `<button type="submit" class="submit" name="submit" style="min-width:${width};min-height:${height};border-radius:4px;border-style:none;background-color:#0f3b21;" title="Pay with BTCPay Server, a Self-Hosted Bitcoin Payment Processor"><span style="color:#fff">${esc(srvModel.payButtonText)}</span>\n` +
|
||||||
|
|||||||
Reference in New Issue
Block a user