2018-08-22 13:57:54 +02:00
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Linq ;
2018-10-24 07:52:19 +02:00
using System.Net.Http ;
2019-03-05 09:09:17 +01:00
using System.Threading ;
2018-08-22 13:57:54 +02:00
using System.Threading.Tasks ;
2020-03-19 11:11:15 +01:00
using BTCPayServer.Client ;
2020-05-23 21:13:18 +02:00
using BTCPayServer.Client.Models ;
2018-02-26 10:58:02 +01:00
using BTCPayServer.Configuration ;
2017-10-17 06:52:30 +02:00
using BTCPayServer.Data ;
2019-08-20 10:38:15 +02:00
using BTCPayServer.HostedServices ;
2017-09-13 16:50:36 +02:00
using BTCPayServer.Models ;
using BTCPayServer.Models.StoreViewModels ;
2019-01-07 09:52:27 +01:00
using BTCPayServer.Payments ;
2018-10-24 07:52:19 +02:00
using BTCPayServer.Payments.Changelly ;
2019-01-07 09:52:27 +01:00
using BTCPayServer.Payments.Lightning ;
2018-05-03 18:46:52 +02:00
using BTCPayServer.Rating ;
2018-04-29 19:33:42 +02:00
using BTCPayServer.Security ;
2019-10-18 17:54:20 +02:00
using BTCPayServer.Security.Bitpay ;
2018-02-12 19:27:36 +01:00
using BTCPayServer.Services ;
2020-03-15 10:51:57 +01:00
using BTCPayServer.Services.Apps ;
2019-05-29 16:33:31 +02:00
using BTCPayServer.Services.Invoices ;
2018-04-18 09:38:17 +02:00
using BTCPayServer.Services.Rates ;
2017-09-15 09:06:57 +02:00
using BTCPayServer.Services.Stores ;
using BTCPayServer.Services.Wallets ;
2017-09-13 16:50:36 +02:00
using Microsoft.AspNetCore.Authorization ;
using Microsoft.AspNetCore.Hosting ;
using Microsoft.AspNetCore.Identity ;
using Microsoft.AspNetCore.Mvc ;
2017-10-17 06:52:30 +02:00
using Microsoft.AspNetCore.Mvc.Rendering ;
2017-09-13 16:50:36 +02:00
using NBitcoin ;
2017-12-06 10:08:21 +01:00
using NBitcoin.DataEncoders ;
2020-05-07 22:37:10 +02:00
using NBXplorer ;
2020-05-23 21:13:18 +02:00
using StoreData = BTCPayServer . Data . StoreData ;
2017-09-13 16:50:36 +02:00
namespace BTCPayServer.Controllers
{
2017-10-27 10:53:04 +02:00
[Route("stores")]
2019-10-12 13:35:30 +02:00
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
2020-03-20 05:41:47 +01:00
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
2017-10-27 10:53:04 +02:00
[AutoValidateAntiforgeryToken]
2018-02-25 16:48:12 +01:00
public partial class StoresController : Controller
2017-10-27 10:53:04 +02:00
{
2018-08-22 09:53:40 +02:00
RateFetcher _RateFactory ;
2018-03-23 08:24:57 +01:00
public string CreatedStoreId { get ; set ; }
2017-10-27 10:53:04 +02:00
public StoresController (
2018-02-25 16:48:12 +01:00
IServiceProvider serviceProvider ,
2018-02-26 10:58:02 +01:00
BTCPayServerOptions btcpayServerOptions ,
BTCPayServerEnvironment btcpayEnv ,
2017-10-27 10:53:04 +02:00
StoreRepository repo ,
TokenRepository tokenRepo ,
UserManager < ApplicationUser > userManager ,
AccessTokenController tokenController ,
2018-01-11 06:36:12 +01:00
BTCPayWalletProvider walletProvider ,
2017-12-21 07:52:04 +01:00
BTCPayNetworkProvider networkProvider ,
2018-08-22 09:53:40 +02:00
RateFetcher rateFactory ,
2018-01-08 14:45:09 +01:00
ExplorerClientProvider explorerProvider ,
2018-02-12 19:27:36 +01:00
IFeeProviderFactory feeRateProvider ,
2018-03-23 09:27:48 +01:00
LanguageService langService ,
2018-10-24 07:52:19 +02:00
ChangellyClientProvider changellyClientProvider ,
2019-10-03 10:06:49 +02:00
IWebHostEnvironment env , IHttpClientFactory httpClientFactory ,
2019-08-20 10:38:15 +02:00
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary ,
2019-12-18 14:28:03 +01:00
SettingsRepository settingsRepository ,
IAuthorizationService authorizationService ,
2020-01-18 06:12:27 +01:00
EventAggregator eventAggregator ,
2020-03-15 10:51:57 +01:00
CssThemeManager cssThemeManager ,
AppService appService )
2017-10-27 10:53:04 +02:00
{
2018-05-03 18:46:52 +02:00
_RateFactory = rateFactory ;
2017-10-27 10:53:04 +02:00
_Repo = repo ;
_TokenRepository = tokenRepo ;
_UserManager = userManager ;
2018-03-23 09:27:48 +01:00
_LangService = langService ;
2018-10-24 07:52:19 +02:00
_changellyClientProvider = changellyClientProvider ;
2017-10-27 10:53:04 +02:00
_TokenController = tokenController ;
2018-01-11 06:36:12 +01:00
_WalletProvider = walletProvider ;
2017-10-27 10:53:04 +02:00
_Env = env ;
2018-10-24 07:52:19 +02:00
_httpClientFactory = httpClientFactory ;
2019-05-29 16:33:31 +02:00
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary ;
2019-12-18 14:28:03 +01:00
_settingsRepository = settingsRepository ;
_authorizationService = authorizationService ;
2019-08-20 10:38:15 +02:00
_CssThemeManager = cssThemeManager ;
2020-03-15 10:51:57 +01:00
_appService = appService ;
2020-01-18 06:12:27 +01:00
_EventAggregator = eventAggregator ;
2018-01-06 10:57:56 +01:00
_NetworkProvider = networkProvider ;
2018-01-08 14:45:09 +01:00
_ExplorerProvider = explorerProvider ;
2018-02-12 19:27:36 +01:00
_FeeRateProvider = feeRateProvider ;
2018-02-25 16:48:12 +01:00
_ServiceProvider = serviceProvider ;
2018-02-26 10:58:02 +01:00
_BtcpayServerOptions = btcpayServerOptions ;
_BTCPayEnv = btcpayEnv ;
2017-10-27 10:53:04 +02:00
}
2018-02-26 10:58:02 +01:00
BTCPayServerOptions _BtcpayServerOptions ;
BTCPayServerEnvironment _BTCPayEnv ;
2018-02-25 16:48:12 +01:00
IServiceProvider _ServiceProvider ;
2018-01-06 10:57:56 +01:00
BTCPayNetworkProvider _NetworkProvider ;
2018-01-08 14:45:09 +01:00
private ExplorerClientProvider _ExplorerProvider ;
2018-02-12 19:27:36 +01:00
private IFeeProviderFactory _FeeRateProvider ;
2018-01-11 06:36:12 +01:00
BTCPayWalletProvider _WalletProvider ;
2017-10-27 10:53:04 +02:00
AccessTokenController _TokenController ;
StoreRepository _Repo ;
TokenRepository _TokenRepository ;
UserManager < ApplicationUser > _UserManager ;
2018-03-23 09:27:48 +01:00
private LanguageService _LangService ;
2018-10-24 07:52:19 +02:00
private readonly ChangellyClientProvider _changellyClientProvider ;
2019-10-03 10:06:49 +02:00
IWebHostEnvironment _Env ;
2018-10-24 07:52:19 +02:00
private IHttpClientFactory _httpClientFactory ;
2019-05-29 16:33:31 +02:00
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary ;
2019-12-18 14:28:03 +01:00
private readonly SettingsRepository _settingsRepository ;
private readonly IAuthorizationService _authorizationService ;
2019-08-20 10:38:15 +02:00
private readonly CssThemeManager _CssThemeManager ;
2020-03-15 10:51:57 +01:00
private readonly AppService _appService ;
2020-01-18 06:12:27 +01:00
private readonly EventAggregator _EventAggregator ;
2017-10-27 10:53:04 +02:00
2019-01-18 11:15:31 +01:00
[TempData]
public bool StoreNotConfigured
{
get ; set ;
}
2017-10-27 10:53:04 +02:00
[HttpGet]
2018-03-23 08:24:57 +01:00
[Route("{storeId}/users")]
2018-04-30 15:00:43 +02:00
public async Task < IActionResult > StoreUsers ( )
2018-03-23 08:24:57 +01:00
{
StoreUsersViewModel vm = new StoreUsersViewModel ( ) ;
2018-04-30 15:00:43 +02:00
await FillUsers ( vm ) ;
2018-03-23 08:24:57 +01:00
return View ( vm ) ;
}
2018-04-30 15:00:43 +02:00
private async Task FillUsers ( StoreUsersViewModel vm )
2017-10-27 10:53:04 +02:00
{
2019-10-12 13:35:30 +02:00
var users = await _Repo . GetStoreUsers ( CurrentStore . Id ) ;
vm . StoreId = CurrentStore . Id ;
2018-03-23 08:24:57 +01:00
vm . Users = users . Select ( u = > new StoreUsersViewModel . StoreUserViewModel ( )
2017-10-27 10:53:04 +02:00
{
2018-03-23 08:24:57 +01:00
Email = u . Email ,
Id = u . Id ,
Role = u . Role
} ) . ToList ( ) ;
2017-10-27 10:53:04 +02:00
}
2019-10-12 13:35:30 +02:00
public StoreData CurrentStore
2018-04-30 15:00:43 +02:00
{
get
{
return this . HttpContext . GetStoreData ( ) ;
}
}
2018-03-23 08:24:57 +01:00
[HttpPost]
[Route("{storeId}/users")]
2018-04-30 15:00:43 +02:00
public async Task < IActionResult > StoreUsers ( StoreUsersViewModel vm )
2018-02-15 05:33:29 +01:00
{
2018-04-30 15:00:43 +02:00
await FillUsers ( vm ) ;
2018-03-24 12:40:26 +01:00
if ( ! ModelState . IsValid )
2018-02-15 05:33:29 +01:00
{
2018-03-23 08:24:57 +01:00
return View ( vm ) ;
}
var user = await _UserManager . FindByEmailAsync ( vm . Email ) ;
2018-03-24 12:40:26 +01:00
if ( user = = null )
2018-03-23 08:24:57 +01:00
{
ModelState . AddModelError ( nameof ( vm . Email ) , "User not found" ) ;
return View ( vm ) ;
}
2018-03-24 12:40:26 +01:00
if ( ! StoreRoles . AllRoles . Contains ( vm . Role ) )
2018-03-23 08:24:57 +01:00
{
ModelState . AddModelError ( nameof ( vm . Role ) , "Invalid role" ) ;
return View ( vm ) ;
}
2019-10-12 13:35:30 +02:00
if ( ! await _Repo . AddStoreUser ( CurrentStore . Id , user . Id , vm . Role ) )
2018-03-23 08:24:57 +01:00
{
ModelState . AddModelError ( nameof ( vm . Email ) , "The user already has access to this store" ) ;
return View ( vm ) ;
2018-02-15 05:33:29 +01:00
}
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "User added successfully" ;
2018-03-23 08:24:57 +01:00
return RedirectToAction ( nameof ( StoreUsers ) ) ;
2018-02-15 05:33:29 +01:00
}
2017-10-27 10:53:04 +02:00
[HttpGet]
2018-03-23 08:24:57 +01:00
[Route("{storeId}/users/{userId}/delete")]
2018-04-30 15:00:43 +02:00
public async Task < IActionResult > DeleteStoreUser ( string userId )
2017-10-27 10:53:04 +02:00
{
2018-03-23 08:24:57 +01:00
StoreUsersViewModel vm = new StoreUsersViewModel ( ) ;
var user = await _UserManager . FindByIdAsync ( userId ) ;
if ( user = = null )
return NotFound ( ) ;
2017-10-27 10:53:04 +02:00
return View ( "Confirm" , new ConfirmModel ( )
{
2018-03-23 08:24:57 +01:00
Title = $"Remove store user" ,
2019-01-21 05:19:01 +01:00
Description = $"Are you sure you want to remove store access for {user.Email}?" ,
2017-10-27 10:53:04 +02:00
Action = "Delete"
} ) ;
}
[HttpPost]
2018-03-23 08:24:57 +01:00
[Route("{storeId}/users/{userId}/delete")]
public async Task < IActionResult > DeleteStoreUserPost ( string storeId , string userId )
2017-10-27 10:53:04 +02:00
{
2018-03-23 08:24:57 +01:00
await _Repo . RemoveStoreUser ( storeId , userId ) ;
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "User removed successfully" ;
2018-03-23 08:24:57 +01:00
return RedirectToAction ( nameof ( StoreUsers ) , new { storeId = storeId , userId = userId } ) ;
2017-10-27 10:53:04 +02:00
}
2018-05-03 18:46:52 +02:00
[HttpGet]
[Route("{storeId}/rates")]
2020-01-13 14:20:45 +01:00
public IActionResult Rates ( )
2018-05-03 18:46:52 +02:00
{
2020-01-13 14:20:45 +01:00
var exchanges = GetSupportedExchanges ( ) ;
2019-10-12 13:35:30 +02:00
var storeBlob = CurrentStore . GetStoreBlob ( ) ;
2018-05-03 18:46:52 +02:00
var vm = new RatesViewModel ( ) ;
2020-01-10 14:50:39 +01:00
vm . SetExchangeRates ( exchanges , storeBlob . PreferredExchange ? ? CoinGeckoRateProvider . CoinGeckoName ) ;
2018-08-01 11:38:46 +02:00
vm . Spread = ( double ) ( storeBlob . Spread * 100 m ) ;
2019-10-12 13:35:30 +02:00
vm . StoreId = CurrentStore . Id ;
2018-05-03 18:46:52 +02:00
vm . Script = storeBlob . GetRateRules ( _NetworkProvider ) . ToString ( ) ;
vm . DefaultScript = storeBlob . GetDefaultRateRules ( _NetworkProvider ) . ToString ( ) ;
2020-01-10 14:50:39 +01:00
vm . AvailableExchanges = exchanges ;
2019-03-11 10:39:21 +01:00
vm . DefaultCurrencyPairs = storeBlob . GetDefaultCurrencyPairString ( ) ;
2018-05-03 18:46:52 +02:00
vm . ShowScripting = storeBlob . RateScripting ;
return View ( vm ) ;
}
[HttpPost]
[Route("{storeId}/rates")]
2019-03-11 10:39:21 +01:00
public async Task < IActionResult > Rates ( RatesViewModel model , string command = null , string storeId = null , CancellationToken cancellationToken = default )
2018-05-03 18:46:52 +02:00
{
2020-01-10 14:50:39 +01:00
if ( command = = "scripting-on" )
{
return RedirectToAction ( nameof ( ShowRateRules ) , new { scripting = true , storeId = model . StoreId } ) ;
} else if ( command = = "scripting-off" )
{
return RedirectToAction ( nameof ( ShowRateRules ) , new { scripting = false , storeId = model . StoreId } ) ;
}
2020-01-13 14:20:45 +01:00
var exchanges = GetSupportedExchanges ( ) ;
2020-01-10 14:50:39 +01:00
model . SetExchangeRates ( exchanges , model . PreferredExchange ) ;
2019-03-11 10:39:21 +01:00
model . StoreId = storeId ? ? model . StoreId ;
CurrencyPair [ ] currencyPairs = null ;
try
{
currencyPairs = model . DefaultCurrencyPairs ?
. Split ( new [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries )
. Select ( p = > CurrencyPair . Parse ( p ) )
. ToArray ( ) ;
}
catch
{
ModelState . AddModelError ( nameof ( model . DefaultCurrencyPairs ) , "Invalid currency pairs (should be for example: BTC_USD,BTC_CAD,BTC_JPY)" ) ;
}
2018-05-03 18:46:52 +02:00
if ( ! ModelState . IsValid )
{
return View ( model ) ;
}
if ( model . PreferredExchange ! = null )
model . PreferredExchange = model . PreferredExchange . Trim ( ) . ToLowerInvariant ( ) ;
2019-10-12 13:35:30 +02:00
var blob = CurrentStore . GetStoreBlob ( ) ;
2018-05-03 18:46:52 +02:00
model . DefaultScript = blob . GetDefaultRateRules ( _NetworkProvider ) . ToString ( ) ;
2020-01-10 14:50:39 +01:00
model . AvailableExchanges = exchanges ;
2018-05-03 18:46:52 +02:00
blob . PreferredExchange = model . PreferredExchange ;
2018-08-01 11:38:46 +02:00
blob . Spread = ( decimal ) model . Spread / 100.0 m ;
2019-03-11 10:39:21 +01:00
blob . DefaultCurrencyPairs = currencyPairs ;
2018-05-03 18:46:52 +02:00
if ( ! model . ShowScripting )
{
2020-01-10 14:50:39 +01:00
if ( ! exchanges . Any ( provider = > provider . Id . Equals ( model . PreferredExchange , StringComparison . InvariantCultureIgnoreCase ) ) )
2018-05-03 18:46:52 +02:00
{
ModelState . AddModelError ( nameof ( model . PreferredExchange ) , $"Unsupported exchange ({model.RateSource})" ) ;
return View ( model ) ;
}
}
RateRules rules = null ;
if ( model . ShowScripting )
{
if ( ! RateRules . TryParse ( model . Script , out rules , out var errors ) )
{
errors = errors ? ? new List < RateRulesErrors > ( ) ;
var errorString = String . Join ( ", " , errors . ToArray ( ) ) ;
ModelState . AddModelError ( nameof ( model . Script ) , $"Parsing error ({errorString})" ) ;
return View ( model ) ;
}
else
{
blob . RateScript = rules . ToString ( ) ;
2018-05-04 04:48:03 +02:00
ModelState . Remove ( nameof ( model . Script ) ) ;
model . Script = blob . RateScript ;
2018-05-03 18:46:52 +02:00
}
}
rules = blob . GetRateRules ( _NetworkProvider ) ;
if ( command = = "Test" )
{
if ( string . IsNullOrWhiteSpace ( model . ScriptTest ) )
{
ModelState . AddModelError ( nameof ( model . ScriptTest ) , "Fill out currency pair to test for (like BTC_USD,BTC_CAD)" ) ;
return View ( model ) ;
}
var splitted = model . ScriptTest . Split ( ',' , StringSplitOptions . RemoveEmptyEntries ) ;
var pairs = new List < CurrencyPair > ( ) ;
foreach ( var pair in splitted )
{
if ( ! CurrencyPair . TryParse ( pair , out var currencyPair ) )
{
ModelState . AddModelError ( nameof ( model . ScriptTest ) , $"Invalid currency pair '{pair}' (it should be formatted like BTC_USD,BTC_CAD)" ) ;
return View ( model ) ;
}
pairs . Add ( currencyPair ) ;
}
2019-03-05 09:09:17 +01:00
var fetchs = _RateFactory . FetchRates ( pairs . ToHashSet ( ) , rules , cancellationToken ) ;
2018-05-03 18:46:52 +02:00
var testResults = new List < RatesViewModel . TestResultViewModel > ( ) ;
foreach ( var fetch in fetchs )
{
var testResult = await ( fetch . Value ) ;
testResults . Add ( new RatesViewModel . TestResultViewModel ( )
{
CurrencyPair = fetch . Key . ToString ( ) ,
Error = testResult . Errors . Count ! = 0 ,
2018-07-27 11:04:41 +02:00
Rule = testResult . Errors . Count = = 0 ? testResult . Rule + " = " + testResult . BidAsk . Bid . ToString ( CultureInfo . InvariantCulture )
2018-05-03 18:46:52 +02:00
: testResult . EvaluatedRule
} ) ;
}
model . TestRateRules = testResults ;
return View ( model ) ;
}
else // command == Save
{
2019-10-12 13:35:30 +02:00
if ( CurrentStore . SetStoreBlob ( blob ) )
2018-05-03 18:46:52 +02:00
{
2019-10-12 13:35:30 +02:00
await _Repo . UpdateStore ( CurrentStore ) ;
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "Rate settings updated" ;
2018-05-03 18:46:52 +02:00
}
return RedirectToAction ( nameof ( Rates ) , new
{
2019-10-12 13:35:30 +02:00
storeId = CurrentStore . Id
2018-05-03 18:46:52 +02:00
} ) ;
}
}
[HttpGet]
[Route("{storeId}/rates/confirm")]
public IActionResult ShowRateRules ( bool scripting )
{
return View ( "Confirm" , new ConfirmModel ( )
{
2018-05-04 09:09:43 +02:00
Action = "Continue" ,
2018-05-03 18:46:52 +02:00
Title = "Rate rule scripting" ,
Description = scripting ?
2018-06-25 04:58:07 +02:00
"This action will modify your current rate sources. Are you sure to turn on rate rules scripting? (Advanced users)"
2018-05-03 18:46:52 +02:00
: "This action will delete your rate script. Are you sure to turn off rate rules scripting?" ,
2020-01-13 14:20:45 +01:00
ButtonClass = scripting ? "btn-primary" : "btn-danger"
2018-05-03 18:46:52 +02:00
} ) ;
}
[HttpPost]
[Route("{storeId}/rates/confirm")]
public async Task < IActionResult > ShowRateRulesPost ( bool scripting )
{
2019-10-12 13:35:30 +02:00
var blob = CurrentStore . GetStoreBlob ( ) ;
2018-05-03 18:46:52 +02:00
blob . RateScripting = scripting ;
blob . RateScript = blob . GetDefaultRateRules ( _NetworkProvider ) . ToString ( ) ;
2019-10-12 13:35:30 +02:00
CurrentStore . SetStoreBlob ( blob ) ;
await _Repo . UpdateStore ( CurrentStore ) ;
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "Rate rules scripting activated" ;
2019-10-12 13:35:30 +02:00
return RedirectToAction ( nameof ( Rates ) , new { storeId = CurrentStore . Id } ) ;
2018-05-03 18:46:52 +02:00
}
2018-03-27 07:48:32 +02:00
[HttpGet]
[Route("{storeId}/checkout")]
2018-04-30 15:00:43 +02:00
public IActionResult CheckoutExperience ( )
2018-03-27 07:48:32 +02:00
{
2019-10-12 13:35:30 +02:00
var storeBlob = CurrentStore . GetStoreBlob ( ) ;
2018-03-27 07:48:32 +02:00
var vm = new CheckoutExperienceViewModel ( ) ;
2019-10-12 13:35:30 +02:00
SetCryptoCurrencies ( vm , CurrentStore ) ;
2019-11-06 04:01:29 +01:00
vm . CustomCSS = storeBlob . CustomCSS ;
vm . CustomLogo = storeBlob . CustomLogo ;
2018-05-03 23:51:04 +02:00
vm . HtmlTitle = storeBlob . HtmlTitle ;
2019-03-16 04:43:57 +01:00
vm . SetLanguages ( _LangService , storeBlob . DefaultLang ) ;
vm . RequiresRefundEmail = storeBlob . RequiresRefundEmail ;
2019-10-08 06:06:12 +02:00
vm . ShowRecommendedFee = storeBlob . ShowRecommendedFee ;
2019-11-07 01:21:33 +01:00
vm . RecommendedFeeBlockTarget = storeBlob . RecommendedFeeBlockTarget ;
2019-03-16 04:43:57 +01:00
vm . OnChainMinValue = storeBlob . OnChainMinValue ? . ToString ( ) ? ? "" ;
vm . LightningMaxValue = storeBlob . LightningMaxValue ? . ToString ( ) ? ? "" ;
vm . LightningAmountInSatoshi = storeBlob . LightningAmountInSatoshi ;
2020-05-19 23:26:03 +02:00
vm . LightningPrivateRouteHints = storeBlob . LightningPrivateRouteHints ;
2019-04-11 11:53:31 +02:00
vm . RedirectAutomatically = storeBlob . RedirectAutomatically ;
2018-03-27 07:48:32 +02:00
return View ( vm ) ;
}
2019-01-31 14:03:28 +01:00
void SetCryptoCurrencies ( CheckoutExperienceViewModel vm , Data . StoreData storeData )
{
var choices = storeData . GetEnabledPaymentIds ( _NetworkProvider )
2019-06-03 18:06:03 +02:00
. Select ( o = > new CheckoutExperienceViewModel . Format ( ) { Name = o . ToPrettyString ( ) , Value = o . ToString ( ) , PaymentId = o } ) . ToArray ( ) ;
2019-01-31 14:03:28 +01:00
var defaultPaymentId = storeData . GetDefaultPaymentId ( _NetworkProvider ) ;
var chosen = choices . FirstOrDefault ( c = > c . PaymentId = = defaultPaymentId ) ;
vm . CryptoCurrencies = new SelectList ( choices , nameof ( chosen . Value ) , nameof ( chosen . Name ) , chosen ? . Value ) ;
vm . DefaultPaymentMethod = chosen ? . Value ;
}
2018-03-27 07:48:32 +02:00
[HttpPost]
[Route("{storeId}/checkout")]
2018-04-30 15:00:43 +02:00
public async Task < IActionResult > CheckoutExperience ( CheckoutExperienceViewModel model )
2018-03-27 07:48:32 +02:00
{
2018-04-03 10:39:28 +02:00
CurrencyValue lightningMaxValue = null ;
2018-03-27 07:48:32 +02:00
if ( ! string . IsNullOrWhiteSpace ( model . LightningMaxValue ) )
{
2018-04-03 10:39:28 +02:00
if ( ! CurrencyValue . TryParse ( model . LightningMaxValue , out lightningMaxValue ) )
2018-03-27 07:48:32 +02:00
{
2018-04-03 10:39:28 +02:00
ModelState . AddModelError ( nameof ( model . LightningMaxValue ) , "Invalid lightning max value" ) ;
2018-03-27 07:48:32 +02:00
}
}
2018-04-03 10:39:28 +02:00
CurrencyValue onchainMinValue = null ;
if ( ! string . IsNullOrWhiteSpace ( model . OnChainMinValue ) )
{
if ( ! CurrencyValue . TryParse ( model . OnChainMinValue , out onchainMinValue ) )
{
ModelState . AddModelError ( nameof ( model . OnChainMinValue ) , "Invalid on chain min value" ) ;
}
}
2018-03-27 07:48:32 +02:00
bool needUpdate = false ;
2019-10-12 13:35:30 +02:00
var blob = CurrentStore . GetStoreBlob ( ) ;
2019-01-31 11:07:38 +01:00
var defaultPaymentMethodId = model . DefaultPaymentMethod = = null ? null : PaymentMethodId . Parse ( model . DefaultPaymentMethod ) ;
2019-10-12 13:35:30 +02:00
if ( CurrentStore . GetDefaultPaymentId ( _NetworkProvider ) ! = defaultPaymentMethodId )
2018-03-27 07:48:32 +02:00
{
needUpdate = true ;
2019-10-12 13:35:30 +02:00
CurrentStore . SetDefaultPaymentId ( defaultPaymentMethodId ) ;
2018-03-27 07:48:32 +02:00
}
2019-10-12 13:35:30 +02:00
SetCryptoCurrencies ( model , CurrentStore ) ;
2018-03-27 07:48:32 +02:00
model . SetLanguages ( _LangService , model . DefaultLang ) ;
2018-04-03 10:54:50 +02:00
2018-04-18 09:38:17 +02:00
if ( ! ModelState . IsValid )
2018-04-03 10:54:50 +02:00
{
return View ( model ) ;
}
2019-11-06 04:01:29 +01:00
blob . CustomLogo = model . CustomLogo ;
blob . CustomCSS = model . CustomCSS ;
2018-05-03 23:51:04 +02:00
blob . HtmlTitle = string . IsNullOrWhiteSpace ( model . HtmlTitle ) ? null : model . HtmlTitle ;
2019-03-16 04:43:57 +01:00
blob . DefaultLang = model . DefaultLang ;
blob . RequiresRefundEmail = model . RequiresRefundEmail ;
2019-10-08 06:06:12 +02:00
blob . ShowRecommendedFee = model . ShowRecommendedFee ;
2019-11-07 01:21:33 +01:00
blob . RecommendedFeeBlockTarget = model . RecommendedFeeBlockTarget ;
2019-03-16 04:43:57 +01:00
blob . OnChainMinValue = onchainMinValue ;
blob . LightningMaxValue = lightningMaxValue ;
blob . LightningAmountInSatoshi = model . LightningAmountInSatoshi ;
2020-05-19 23:26:03 +02:00
blob . LightningPrivateRouteHints = model . LightningPrivateRouteHints ;
2019-04-11 11:53:31 +02:00
blob . RedirectAutomatically = model . RedirectAutomatically ;
2019-10-12 13:35:30 +02:00
if ( CurrentStore . SetStoreBlob ( blob ) )
2018-03-27 07:48:32 +02:00
{
needUpdate = true ;
}
if ( needUpdate )
{
2019-10-12 13:35:30 +02:00
await _Repo . UpdateStore ( CurrentStore ) ;
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "Store successfully updated" ;
2018-03-27 07:48:32 +02:00
}
return RedirectToAction ( nameof ( CheckoutExperience ) , new
{
2019-10-12 13:35:30 +02:00
storeId = CurrentStore . Id
2018-03-27 07:48:32 +02:00
} ) ;
}
2017-10-27 10:53:04 +02:00
[HttpGet]
[Route("{storeId}")]
2018-05-03 18:46:52 +02:00
public IActionResult UpdateStore ( )
2017-10-27 10:53:04 +02:00
{
2018-04-29 19:33:42 +02:00
var store = HttpContext . GetStoreData ( ) ;
2017-10-27 10:53:04 +02:00
if ( store = = null )
return NotFound ( ) ;
2017-12-21 07:52:04 +01:00
var storeBlob = store . GetStoreBlob ( ) ;
2017-10-27 10:53:04 +02:00
var vm = new StoreViewModel ( ) ;
2017-12-03 15:35:52 +01:00
vm . Id = store . Id ;
2017-10-27 10:53:04 +02:00
vm . StoreName = store . StoreName ;
vm . StoreWebsite = store . StoreWebsite ;
2019-01-04 16:37:09 +01:00
vm . NetworkFeeMode = storeBlob . NetworkFeeMode ;
2018-09-08 07:32:26 +02:00
vm . AnyoneCanCreateInvoice = storeBlob . AnyoneCanInvoice ;
2017-10-27 10:53:04 +02:00
vm . SpeedPolicy = store . SpeedPolicy ;
2018-07-19 15:23:14 +02:00
vm . CanDelete = _Repo . CanDeleteStores ( ) ;
2018-07-27 13:37:16 +02:00
AddPaymentMethods ( store , storeBlob , vm ) ;
2017-12-03 06:43:52 +01:00
vm . MonitoringExpiration = storeBlob . MonitoringExpiration ;
2018-01-17 07:11:05 +01:00
vm . InvoiceExpiration = storeBlob . InvoiceExpiration ;
2018-04-07 09:27:46 +02:00
vm . LightningDescriptionTemplate = storeBlob . LightningDescriptionTemplate ;
2018-05-04 16:15:34 +02:00
vm . PaymentTolerance = storeBlob . PaymentTolerance ;
2020-03-17 08:43:42 +01:00
vm . PayJoinEnabled = storeBlob . PayJoinEnabled ;
2017-10-27 10:53:04 +02:00
return View ( vm ) ;
}
2018-03-24 12:40:26 +01:00
2017-10-27 10:53:04 +02:00
2018-07-27 13:37:16 +02:00
private void AddPaymentMethods ( StoreData store , StoreBlob storeBlob , StoreViewModel vm )
2017-10-27 10:53:04 +02:00
{
2018-07-27 13:37:16 +02:00
var excludeFilters = storeBlob . GetExcludedPaymentMethods ( ) ;
2018-03-24 12:40:26 +01:00
var derivationByCryptoCode =
2018-03-20 18:48:11 +01:00
store
. GetSupportedPaymentMethods ( _NetworkProvider )
2019-05-08 16:39:11 +02:00
. OfType < DerivationSchemeSettings > ( )
2019-12-24 08:20:44 +01:00
. ToDictionary ( c = > c . Network . CryptoCode . ToUpperInvariant ( ) ) ;
2018-02-07 13:59:16 +01:00
2018-03-20 18:48:11 +01:00
var lightningByCryptoCode = store
2019-05-29 16:33:31 +02:00
. GetSupportedPaymentMethods ( _NetworkProvider )
. OfType < LightningSupportedPaymentMethod > ( )
2019-12-24 08:20:44 +01:00
. ToDictionary ( c = > c . CryptoCode . ToUpperInvariant ( ) ) ;
2018-03-20 18:48:11 +01:00
2019-05-29 16:33:31 +02:00
foreach ( var paymentMethodId in _paymentMethodHandlerDictionary . Distinct ( ) . SelectMany ( handler = > handler . GetSupportedPaymentMethods ( ) ) )
2018-01-08 14:45:09 +01:00
{
2019-05-29 16:33:31 +02:00
switch ( paymentMethodId . PaymentType )
2017-10-27 10:53:04 +02:00
{
2019-06-04 01:59:01 +02:00
case BitcoinPaymentType _ :
2019-05-29 16:33:31 +02:00
var strategy = derivationByCryptoCode . TryGet ( paymentMethodId . CryptoCode ) ;
2019-12-24 08:20:44 +01:00
var network = _NetworkProvider . GetNetwork < BTCPayNetwork > ( paymentMethodId . CryptoCode ) ;
2020-01-26 11:45:24 +01:00
var value = strategy ? . ToPrettyString ( ) ? ? string . Empty ;
2019-05-29 16:33:31 +02:00
vm . DerivationSchemes . Add ( new StoreViewModel . DerivationScheme ( )
{
Crypto = paymentMethodId . CryptoCode ,
2019-12-24 08:20:44 +01:00
WalletSupported = network . WalletSupported ,
2020-01-26 11:45:24 +01:00
Value = value ,
2019-05-29 16:33:31 +02:00
WalletId = new WalletId ( store . Id , paymentMethodId . CryptoCode ) ,
2020-01-26 11:45:24 +01:00
Enabled = ! excludeFilters . Match ( paymentMethodId ) & & strategy ! = null ,
Collapsed = network is ElementsBTCPayNetwork elementsBTCPayNetwork & & elementsBTCPayNetwork . NetworkCryptoCode ! = elementsBTCPayNetwork . CryptoCode & & string . IsNullOrEmpty ( value )
2019-05-29 16:33:31 +02:00
} ) ;
break ;
2019-06-04 01:59:01 +02:00
case LightningPaymentType _ :
2019-05-29 16:33:31 +02:00
var lightning = lightningByCryptoCode . TryGet ( paymentMethodId . CryptoCode ) ;
vm . LightningNodes . Add ( new StoreViewModel . LightningNode ( )
{
CryptoCode = paymentMethodId . CryptoCode ,
Address = lightning ? . GetLightningUrl ( ) ? . BaseUri . AbsoluteUri ? ? string . Empty ,
2019-08-01 10:10:12 +02:00
Enabled = ! excludeFilters . Match ( paymentMethodId ) & & lightning ? . GetLightningUrl ( ) ! = null
2019-05-29 16:33:31 +02:00
} ) ;
break ;
}
2018-01-08 14:45:09 +01:00
}
2018-10-24 07:52:19 +02:00
var changellyEnabled = storeBlob . ChangellySettings ! = null & & storeBlob . ChangellySettings . Enabled ;
2019-09-03 13:11:36 +02:00
vm . ThirdPartyPaymentMethods . Add ( new StoreViewModel . AdditionalPaymentMethod ( )
2018-10-24 07:52:19 +02:00
{
Enabled = changellyEnabled ,
Action = nameof ( UpdateChangellySettings ) ,
Provider = "Changelly"
} ) ;
2019-04-04 20:56:12 +02:00
2018-12-11 12:47:38 +01:00
var coinSwitchEnabled = storeBlob . CoinSwitchSettings ! = null & & storeBlob . CoinSwitchSettings . Enabled ;
2019-09-03 13:11:36 +02:00
vm . ThirdPartyPaymentMethods . Add ( new StoreViewModel . AdditionalPaymentMethod ( )
2018-12-11 12:47:38 +01:00
{
Enabled = coinSwitchEnabled ,
Action = nameof ( UpdateCoinSwitchSettings ) ,
Provider = "CoinSwitch"
} ) ;
2018-01-08 14:45:09 +01:00
}
2020-05-07 22:37:10 +02:00
2018-01-08 14:45:09 +01:00
[HttpPost]
[Route("{storeId}")]
2018-07-19 15:23:14 +02:00
public async Task < IActionResult > UpdateStore ( StoreViewModel model , string command = null )
2018-01-08 14:45:09 +01:00
{
bool needUpdate = false ;
2019-10-12 13:35:30 +02:00
if ( CurrentStore . SpeedPolicy ! = model . SpeedPolicy )
2018-01-08 14:45:09 +01:00
{
needUpdate = true ;
2019-10-12 13:35:30 +02:00
CurrentStore . SpeedPolicy = model . SpeedPolicy ;
2018-01-08 14:45:09 +01:00
}
2019-10-12 13:35:30 +02:00
if ( CurrentStore . StoreName ! = model . StoreName )
2018-01-08 14:45:09 +01:00
{
needUpdate = true ;
2019-10-12 13:35:30 +02:00
CurrentStore . StoreName = model . StoreName ;
2018-01-08 14:45:09 +01:00
}
2019-10-12 13:35:30 +02:00
if ( CurrentStore . StoreWebsite ! = model . StoreWebsite )
2018-01-08 14:45:09 +01:00
{
needUpdate = true ;
2019-10-12 13:35:30 +02:00
CurrentStore . StoreWebsite = model . StoreWebsite ;
2018-01-08 14:45:09 +01:00
}
2019-10-12 13:35:30 +02:00
var blob = CurrentStore . GetStoreBlob ( ) ;
2018-09-08 07:32:26 +02:00
blob . AnyoneCanInvoice = model . AnyoneCanCreateInvoice ;
2019-01-04 16:37:09 +01:00
blob . NetworkFeeMode = model . NetworkFeeMode ;
2018-01-08 14:45:09 +01:00
blob . MonitoringExpiration = model . MonitoringExpiration ;
2018-01-17 07:59:31 +01:00
blob . InvoiceExpiration = model . InvoiceExpiration ;
2018-04-07 09:27:46 +02:00
blob . LightningDescriptionTemplate = model . LightningDescriptionTemplate ? ? string . Empty ;
2018-05-04 16:15:34 +02:00
blob . PaymentTolerance = model . PaymentTolerance ;
2020-05-07 22:37:10 +02:00
var payjoinChanged = blob . PayJoinEnabled ! = model . PayJoinEnabled ;
2020-03-17 08:43:42 +01:00
blob . PayJoinEnabled = model . PayJoinEnabled ;
2019-10-12 13:35:30 +02:00
if ( CurrentStore . SetStoreBlob ( blob ) )
2018-01-08 14:45:09 +01:00
{
needUpdate = true ;
}
if ( needUpdate )
{
2019-10-12 13:35:30 +02:00
await _Repo . UpdateStore ( CurrentStore ) ;
2020-05-07 22:37:10 +02:00
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "Store successfully updated" ;
2020-05-07 22:37:10 +02:00
if ( payjoinChanged & & blob . PayJoinEnabled )
{
var problematicPayjoinEnabledMethods = CurrentStore . GetSupportedPaymentMethods ( _NetworkProvider )
. OfType < DerivationSchemeSettings > ( )
. Where ( settings = >
settings . Network . SupportPayJoin & &
string . IsNullOrEmpty ( _ExplorerProvider . GetExplorerClient ( settings . Network )
. GetMetadata < string > ( settings . AccountDerivation ,
WellknownMetadataKeys . Mnemonic ) ) )
. Select ( settings = > settings . PaymentId . CryptoCode )
. ToArray ( ) ;
if ( problematicPayjoinEnabledMethods . Any ( ) )
{
TempData . Remove ( WellKnownTempData . SuccessMessage ) ;
TempData . SetStatusMessageModel ( new StatusMessageModel ( )
{
Severity = StatusMessageModel . StatusSeverity . Warning ,
2020-06-11 16:09:33 +02:00
Html = $"The store was updated successfully. However, payjoin will not work for {string.Join(" , ", problematicPayjoinEnabledMethods)} until you configure them to be a <a href='https://docs.btcpayserver.org/HotWallet/' class='alert-link' target='_blank'>hot wallet</a>."
2020-05-07 22:37:10 +02:00
} ) ;
}
}
2018-01-08 14:45:09 +01:00
}
return RedirectToAction ( nameof ( UpdateStore ) , new
{
2019-10-12 13:35:30 +02:00
storeId = CurrentStore . Id
2018-01-08 14:45:09 +01:00
} ) ;
2018-07-19 15:23:14 +02:00
}
[HttpGet]
[Route("{storeId}/delete")]
public IActionResult DeleteStore ( string storeId )
{
return View ( "Confirm" , new ConfirmModel ( )
{
2019-10-31 07:10:00 +01:00
Action = "Delete" ,
2018-07-19 15:23:14 +02:00
Title = "Delete this store" ,
Description = "This action is irreversible and will remove all information related to this store. (Invoices, Apps etc...)" ,
ButtonClass = "btn-danger"
} ) ;
}
[HttpPost]
[Route("{storeId}/delete")]
public async Task < IActionResult > DeleteStorePost ( string storeId )
{
2019-10-12 13:35:30 +02:00
await _Repo . DeleteStore ( CurrentStore . Id ) ;
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "Store successfully deleted" ;
2018-07-19 15:23:14 +02:00
return RedirectToAction ( nameof ( UserStoresController . ListStores ) , "UserStores" ) ;
2017-10-27 10:53:04 +02:00
}
2020-01-13 14:20:45 +01:00
private IEnumerable < AvailableRateProvider > GetSupportedExchanges ( )
2018-04-18 09:38:17 +02:00
{
2020-01-13 14:20:45 +01:00
var exchanges = _RateFactory . RateProviderFactory . GetSupportedExchanges ( ) ;
2020-01-10 14:50:39 +01:00
return exchanges
. Where ( r = > ! string . IsNullOrWhiteSpace ( r . Name ) )
. OrderBy ( s = > s . Id , StringComparer . OrdinalIgnoreCase ) ;
2018-04-18 09:38:17 +02:00
}
2019-05-08 16:39:11 +02:00
private DerivationSchemeSettings ParseDerivationStrategy ( string derivationScheme , Script hint , BTCPayNetwork network )
2017-10-27 10:53:04 +02:00
{
2019-05-09 09:05:18 +02:00
var parser = new DerivationSchemeParser ( network ) ;
2018-03-24 12:40:26 +01:00
parser . HintScriptPubKey = hint ;
2019-05-08 16:39:11 +02:00
return new DerivationSchemeSettings ( parser . Parse ( derivationScheme ) , network ) ;
2017-10-27 10:53:04 +02:00
}
[HttpGet]
[Route("{storeId}/Tokens")]
2018-04-30 15:00:43 +02:00
public async Task < IActionResult > ListTokens ( )
2017-10-27 10:53:04 +02:00
{
var model = new TokensViewModel ( ) ;
2019-10-12 13:35:30 +02:00
var tokens = await _TokenRepository . GetTokensByStoreIdAsync ( CurrentStore . Id ) ;
2019-01-18 11:15:31 +01:00
model . StoreNotConfigured = StoreNotConfigured ;
2017-10-27 10:53:04 +02:00
model . Tokens = tokens . Select ( t = > new TokenViewModel ( )
{
Label = t . Label ,
SIN = t . SIN ,
Id = t . Value
} ) . ToArray ( ) ;
2018-04-29 11:28:04 +02:00
2019-10-12 13:35:30 +02:00
model . ApiKey = ( await _TokenRepository . GetLegacyAPIKeys ( CurrentStore . Id ) ) . FirstOrDefault ( ) ;
2018-04-29 11:28:04 +02:00
if ( model . ApiKey = = null )
model . EncodedApiKey = "*API Key*" ;
else
model . EncodedApiKey = Encoders . Base64 . EncodeData ( Encoders . ASCII . DecodeData ( model . ApiKey ) ) ;
2017-10-27 10:53:04 +02:00
return View ( model ) ;
}
2020-02-21 05:40:00 +01:00
2018-10-31 09:59:09 +01:00
[HttpGet]
[Route("{storeId}/tokens/{tokenId}/revoke")]
public async Task < IActionResult > RevokeToken ( string tokenId )
{
var token = await _TokenRepository . GetToken ( tokenId ) ;
2019-10-12 13:35:30 +02:00
if ( token = = null | | token . StoreId ! = CurrentStore . Id )
2018-10-31 09:59:09 +01:00
return NotFound ( ) ;
return View ( "Confirm" , new ConfirmModel ( )
{
2019-10-31 07:10:00 +01:00
Action = "Revoke" ,
2018-10-31 09:59:09 +01:00
Title = "Revoke the token" ,
Description = $"The access token with the label \" { token . Label } \ " will be revoked, do you wish to continue?" ,
ButtonClass = "btn-danger"
} ) ;
}
[HttpPost]
[Route("{storeId}/tokens/{tokenId}/revoke")]
public async Task < IActionResult > RevokeTokenConfirm ( string tokenId )
{
var token = await _TokenRepository . GetToken ( tokenId ) ;
if ( token = = null | |
2019-10-12 13:35:30 +02:00
token . StoreId ! = CurrentStore . Id | |
2018-10-31 09:59:09 +01:00
! await _TokenRepository . DeleteToken ( tokenId ) )
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . ErrorMessage ] = "Failure to revoke this token" ;
2018-10-31 09:59:09 +01:00
else
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "Token revoked" ;
2020-02-07 08:23:00 +01:00
return RedirectToAction ( nameof ( ListTokens ) , new { storeId = token . StoreId } ) ;
2018-10-31 09:59:09 +01:00
}
[HttpGet]
[Route("{storeId}/tokens/{tokenId}")]
public async Task < IActionResult > ShowToken ( string tokenId )
{
var token = await _TokenRepository . GetToken ( tokenId ) ;
2019-10-12 13:35:30 +02:00
if ( token = = null | | token . StoreId ! = CurrentStore . Id )
2018-10-31 09:59:09 +01:00
return NotFound ( ) ;
return View ( token ) ;
}
2017-10-27 10:53:04 +02:00
[HttpPost]
[Route("{storeId}/Tokens/Create")]
2019-11-03 08:16:52 +01:00
public async Task < IActionResult > CreateToken ( string storeId , CreateTokenViewModel model )
2017-10-27 10:53:04 +02:00
{
if ( ! ModelState . IsValid )
{
2019-11-03 08:16:52 +01:00
return View ( nameof ( CreateToken ) , model ) ;
2017-10-27 10:53:04 +02:00
}
model . Label = model . Label ? ? String . Empty ;
2018-03-23 08:24:57 +01:00
var userId = GetUserId ( ) ;
if ( userId = = null )
2019-10-12 13:35:30 +02:00
return Challenge ( AuthenticationSchemes . Cookie ) ;
2019-11-03 08:16:52 +01:00
storeId = model . StoreId ;
var store = CurrentStore ? ? await _Repo . FindStore ( storeId , userId ) ;
if ( store = = null )
return Challenge ( AuthenticationSchemes . Cookie ) ;
2017-10-27 10:53:04 +02:00
var tokenRequest = new TokenRequest ( )
{
Label = model . Label ,
Id = model . PublicKey = = null ? null : NBitpayClient . Extensions . BitIdExtensions . GetBitIDSIN ( new PubKey ( model . PublicKey ) )
} ;
string pairingCode = null ;
if ( model . PublicKey = = null )
{
tokenRequest . PairingCode = await _TokenRepository . CreatePairingCodeAsync ( ) ;
await _TokenRepository . UpdatePairingCode ( new PairingCodeEntity ( )
{
Id = tokenRequest . PairingCode ,
Label = model . Label ,
} ) ;
await _TokenRepository . PairWithStoreAsync ( tokenRequest . PairingCode , storeId ) ;
pairingCode = tokenRequest . PairingCode ;
}
else
{
pairingCode = ( ( DataWrapper < List < PairingCodeResponse > > ) await _TokenController . Tokens ( tokenRequest ) ) . Data [ 0 ] . PairingCode ;
}
2018-01-09 18:07:42 +01:00
GeneratedPairingCode = pairingCode ;
2017-10-27 10:53:04 +02:00
return RedirectToAction ( nameof ( RequestPairing ) , new
{
pairingCode = pairingCode ,
selectedStore = storeId
} ) ;
}
2018-01-09 18:07:42 +01:00
public string GeneratedPairingCode { get ; set ; }
2017-10-27 10:53:04 +02:00
[HttpGet]
[Route("{storeId}/Tokens/Create")]
2019-11-03 08:16:52 +01:00
public IActionResult CreateToken ( string storeId )
{
var model = new CreateTokenViewModel ( ) ;
ViewBag . HidePublicKey = storeId = = null ;
ViewBag . ShowStores = storeId = = null ;
ViewBag . ShowMenu = storeId ! = null ;
model . StoreId = storeId ;
return View ( model ) ;
}
[HttpGet]
[Route("/api-tokens")]
[AllowAnonymous]
2018-04-30 15:00:43 +02:00
public async Task < IActionResult > CreateToken ( )
2017-10-27 10:53:04 +02:00
{
var userId = GetUserId ( ) ;
if ( string . IsNullOrWhiteSpace ( userId ) )
2019-10-12 13:35:30 +02:00
return Challenge ( AuthenticationSchemes . Cookie ) ;
var storeId = CurrentStore ? . Id ;
2017-10-27 10:53:04 +02:00
var model = new CreateTokenViewModel ( ) ;
2019-11-03 08:16:52 +01:00
ViewBag . HidePublicKey = true ;
ViewBag . ShowStores = true ;
ViewBag . ShowMenu = false ;
var stores = await _Repo . GetStoresByUserId ( userId ) ;
model . Stores = new SelectList ( stores . Where ( s = > s . Role = = StoreRoles . Owner ) , nameof ( CurrentStore . Id ) , nameof ( CurrentStore . StoreName ) ) ;
2020-01-12 07:32:26 +01:00
if ( ! model . Stores . Any ( ) )
2019-11-03 08:16:52 +01:00
{
TempData [ WellKnownTempData . ErrorMessage ] = "You need to be owner of at least one store before pairing" ;
return RedirectToAction ( nameof ( UserStoresController . ListStores ) , "UserStores" ) ;
2017-10-27 10:53:04 +02:00
}
return View ( model ) ;
}
2019-11-03 08:16:52 +01:00
[HttpPost]
[Route("/api-tokens")]
[AllowAnonymous]
public Task < IActionResult > CreateToken2 ( CreateTokenViewModel model )
{
return CreateToken ( model . StoreId , model ) ;
}
2018-04-29 11:28:04 +02:00
[HttpPost]
[Route("{storeId}/tokens/apikey")]
2020-02-21 05:40:00 +01:00
public async Task < IActionResult > GenerateAPIKey ( string storeId , string command = "" )
2018-04-29 11:28:04 +02:00
{
2018-04-29 19:33:42 +02:00
var store = HttpContext . GetStoreData ( ) ;
2018-04-29 11:28:04 +02:00
if ( store = = null )
return NotFound ( ) ;
2020-02-21 05:40:00 +01:00
if ( command = = "revoke" )
{
await _TokenRepository . RevokeLegacyAPIKeys ( CurrentStore . Id ) ;
TempData [ WellKnownTempData . SuccessMessage ] = "API Key revoked" ;
}
else
{
await _TokenRepository . GenerateLegacyAPIKey ( CurrentStore . Id ) ;
TempData [ WellKnownTempData . SuccessMessage ] = "API Key re-generated" ;
}
2020-01-14 14:06:46 +01:00
return RedirectToAction ( nameof ( ListTokens ) , new
{
storeId
} ) ;
2018-04-29 11:28:04 +02:00
}
2017-10-27 10:53:04 +02:00
[HttpGet]
[Route("/api-access-request")]
2018-04-30 15:00:43 +02:00
[AllowAnonymous]
2017-10-27 10:53:04 +02:00
public async Task < IActionResult > RequestPairing ( string pairingCode , string selectedStore = null )
{
2018-04-30 15:00:43 +02:00
var userId = GetUserId ( ) ;
if ( userId = = null )
2019-10-12 13:35:30 +02:00
return Challenge ( AuthenticationSchemes . Cookie ) ;
2018-03-23 08:24:57 +01:00
if ( pairingCode = = null )
return NotFound ( ) ;
2017-10-27 10:53:04 +02:00
var pairing = await _TokenRepository . GetPairingAsync ( pairingCode ) ;
if ( pairing = = null )
{
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . ErrorMessage ] = "Unknown pairing code" ;
2018-03-23 08:24:57 +01:00
return RedirectToAction ( nameof ( UserStoresController . ListStores ) , "UserStores" ) ;
2017-10-27 10:53:04 +02:00
}
else
{
2018-04-30 15:00:43 +02:00
var stores = await _Repo . GetStoresByUserId ( userId ) ;
2017-10-27 10:53:04 +02:00
return View ( new PairingModel ( )
{
Id = pairing . Id ,
Label = pairing . Label ,
SIN = pairing . SIN ? ? "Server-Initiated Pairing" ,
2019-10-12 13:35:30 +02:00
StoreId = selectedStore ? ? stores . FirstOrDefault ( ) ? . Id ,
Stores = stores . Where ( u = > u . Role = = StoreRoles . Owner ) . Select ( s = > new PairingModel . StoreViewModel ( )
2017-10-27 10:53:04 +02:00
{
Id = s . Id ,
Name = string . IsNullOrEmpty ( s . StoreName ) ? s . Id : s . StoreName
} ) . ToArray ( )
} ) ;
}
}
[HttpPost]
2018-03-23 08:24:57 +01:00
[Route("/api-access-request")]
2019-10-12 13:35:30 +02:00
public async Task < IActionResult > Pair ( string pairingCode , string storeId )
2017-10-27 10:53:04 +02:00
{
if ( pairingCode = = null )
return NotFound ( ) ;
2019-10-12 13:35:30 +02:00
var store = CurrentStore ;
2017-10-27 10:53:04 +02:00
var pairing = await _TokenRepository . GetPairingAsync ( pairingCode ) ;
if ( store = = null | | pairing = = null )
return NotFound ( ) ;
var pairingResult = await _TokenRepository . PairWithStoreAsync ( pairingCode , store . Id ) ;
if ( pairingResult = = PairingResult . Complete | | pairingResult = = PairingResult . Partial )
{
2019-01-18 11:15:31 +01:00
var excludeFilter = store . GetStoreBlob ( ) . GetExcludedPaymentMethods ( ) ;
2020-01-12 07:32:26 +01:00
StoreNotConfigured = ! store . GetSupportedPaymentMethods ( _NetworkProvider )
2019-01-18 11:15:31 +01:00
. Where ( p = > ! excludeFilter . Match ( p . PaymentId ) )
2020-01-12 07:32:26 +01:00
. Any ( ) ;
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "Pairing is successful" ;
2017-10-27 10:53:04 +02:00
if ( pairingResult = = PairingResult . Partial )
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "Server initiated pairing code: " + pairingCode ;
2017-10-27 10:53:04 +02:00
return RedirectToAction ( nameof ( ListTokens ) , new
{
2018-11-22 07:13:35 +01:00
storeId = store . Id ,
pairingCode = pairingCode
2017-10-27 10:53:04 +02:00
} ) ;
}
else
{
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . ErrorMessage ] = $"Pairing failed ({pairingResult})" ;
2017-10-27 10:53:04 +02:00
return RedirectToAction ( nameof ( ListTokens ) , new
{
storeId = store . Id
} ) ;
}
}
private string GetUserId ( )
{
2019-10-12 13:35:30 +02:00
if ( User . Identity . AuthenticationType ! = AuthenticationSchemes . Cookie )
2018-04-30 15:00:43 +02:00
return null ;
2017-10-27 10:53:04 +02:00
return _UserManager . GetUserId ( User ) ;
}
2018-08-22 13:57:54 +02:00
// TODO: Need to have talk about how architect default currency implementation
// For now we have also hardcoded USD for Store creation and then Invoice creation
const string DEFAULT_CURRENCY = "USD" ;
[Route("{storeId}/paybutton")]
2020-03-15 10:51:57 +01:00
public async Task < IActionResult > PayButton ( )
2018-08-22 13:57:54 +02:00
{
2019-10-12 13:35:30 +02:00
var store = CurrentStore ;
2018-08-22 13:57:54 +02:00
2018-09-04 06:48:53 +02:00
var storeBlob = store . GetStoreBlob ( ) ;
2018-09-08 07:32:26 +02:00
if ( ! storeBlob . AnyoneCanInvoice )
2018-09-04 06:48:53 +02:00
{
return View ( "PayButtonEnable" , null ) ;
}
2020-03-15 10:51:57 +01:00
var apps = await _appService . GetAllApps ( _UserManager . GetUserId ( User ) , false , store . Id ) ;
2018-08-23 04:11:39 +02:00
var appUrl = HttpContext . Request . GetAbsoluteRoot ( ) . WithTrailingSlash ( ) ;
2018-08-22 13:57:54 +02:00
var model = new PayButtonViewModel
{
Price = 10 ,
Currency = DEFAULT_CURRENCY ,
ButtonSize = 2 ,
UrlRoot = appUrl ,
2019-09-20 12:14:08 +02:00
PayButtonImageUrl = appUrl + "img/paybutton/pay.svg" ,
2019-04-03 21:43:53 +02:00
StoreId = store . Id ,
2019-04-04 21:32:16 +02:00
ButtonType = 0 ,
2019-04-04 20:56:12 +02:00
Min = 1 ,
Max = 20 ,
2020-03-15 10:51:57 +01:00
Step = 1 ,
Apps = apps
2018-08-22 13:57:54 +02:00
} ;
return View ( model ) ;
}
2018-09-04 06:48:53 +02:00
[HttpPost]
[Route("{storeId}/paybutton")]
public async Task < IActionResult > PayButton ( bool enableStore )
{
2019-10-12 13:35:30 +02:00
var blob = CurrentStore . GetStoreBlob ( ) ;
2018-09-08 07:32:26 +02:00
blob . AnyoneCanInvoice = enableStore ;
2019-10-12 13:35:30 +02:00
if ( CurrentStore . SetStoreBlob ( blob ) )
2018-09-04 06:48:53 +02:00
{
2019-10-12 13:35:30 +02:00
await _Repo . UpdateStore ( CurrentStore ) ;
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "Store successfully updated" ;
2018-09-04 06:48:53 +02:00
}
return RedirectToAction ( nameof ( PayButton ) , new
{
2019-10-12 13:35:30 +02:00
storeId = CurrentStore . Id
2018-09-04 06:48:53 +02:00
} ) ;
}
2017-10-27 10:53:04 +02:00
}
2017-09-13 16:50:36 +02:00
}