Allow checkout with litecoin

This commit is contained in:
nicolas.dorier
2018-01-09 11:41:07 +09:00
parent 0c735f4e29
commit 6ae9d13c43
14 changed files with 600 additions and 34 deletions

View File

@@ -23,5 +23,7 @@ namespace BTCPayServer
return CryptoCode == "BTC"; return CryptoCode == "BTC";
} }
} }
public string CryptoImagePath { get; set; }
} }
} }

View File

@@ -31,7 +31,8 @@ namespace BTCPayServer
BlockExplorerLink = "https://www.smartbit.com.au/tx/{0}", BlockExplorerLink = "https://www.smartbit.com.au/tx/{0}",
NBitcoinNetwork = Network.Main, NBitcoinNetwork = Network.Main,
UriScheme = "bitcoin", UriScheme = "bitcoin",
DefaultRateProvider = btcRate DefaultRateProvider = btcRate,
CryptoImagePath = "imlegacy/bitcoin-symbol.svg"
}); });
Add(new BTCPayNetwork() Add(new BTCPayNetwork()
{ {
@@ -39,7 +40,8 @@ namespace BTCPayServer
BlockExplorerLink = "https://live.blockcypher.com/ltc/tx/{0}/", BlockExplorerLink = "https://live.blockcypher.com/ltc/tx/{0}/",
NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Mainnet, NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Mainnet,
UriScheme = "litecoin", UriScheme = "litecoin",
DefaultRateProvider = ltcRate DefaultRateProvider = ltcRate,
CryptoImagePath = "imlegacy/litecoin-symbol.svg"
}); });
} }
@@ -51,7 +53,8 @@ namespace BTCPayServer
BlockExplorerLink = "https://testnet.smartbit.com.au/tx/{0}", BlockExplorerLink = "https://testnet.smartbit.com.au/tx/{0}",
NBitcoinNetwork = Network.TestNet, NBitcoinNetwork = Network.TestNet,
UriScheme = "bitcoin", UriScheme = "bitcoin",
DefaultRateProvider = btcRate DefaultRateProvider = btcRate,
CryptoImagePath = "imlegacy/bitcoin-symbol.svg"
}); });
Add(new BTCPayNetwork() Add(new BTCPayNetwork()
{ {
@@ -59,7 +62,8 @@ namespace BTCPayServer
BlockExplorerLink = "http://explorer.litecointools.com/tx/{0}", BlockExplorerLink = "http://explorer.litecointools.com/tx/{0}",
NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Testnet, NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Testnet,
UriScheme = "litecoin", UriScheme = "litecoin",
DefaultRateProvider = ltcRate DefaultRateProvider = ltcRate,
CryptoImagePath = "imlegacy/litecoin-symbol.svg"
}); });
} }
@@ -71,7 +75,8 @@ namespace BTCPayServer
BlockExplorerLink = "https://testnet.smartbit.com.au/tx/{0}", BlockExplorerLink = "https://testnet.smartbit.com.au/tx/{0}",
NBitcoinNetwork = Network.RegTest, NBitcoinNetwork = Network.RegTest,
UriScheme = "bitcoin", UriScheme = "bitcoin",
DefaultRateProvider = btcRate DefaultRateProvider = btcRate,
CryptoImagePath = "imlegacy/bitcoin-symbol.svg"
}); });
Add(new BTCPayNetwork() Add(new BTCPayNetwork()
{ {
@@ -79,7 +84,8 @@ namespace BTCPayServer
BlockExplorerLink = "http://explorer.litecointools.com/tx/{0}", BlockExplorerLink = "http://explorer.litecointools.com/tx/{0}",
NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Regtest, NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Regtest,
UriScheme = "litecoin", UriScheme = "litecoin",
DefaultRateProvider = ltcRate DefaultRateProvider = ltcRate,
CryptoImagePath = "imlegacy/litecoin-symbol.svg"
}); });
} }
} }

View File

@@ -97,13 +97,12 @@ namespace BTCPayServer.Controllers
[HttpGet] [HttpGet]
[Route("i/{invoiceId}")] [Route("i/{invoiceId}")]
[Route("i/{invoiceId}/{cryptoCode}")]
[Route("invoice")] [Route("invoice")]
[AcceptMediaTypeConstraint("application/bitcoin-paymentrequest", false)] [AcceptMediaTypeConstraint("application/bitcoin-paymentrequest", false)]
[XFrameOptionsAttribute(null)] [XFrameOptionsAttribute(null)]
public async Task<IActionResult> Checkout(string invoiceId, string id = null, string cryptoCode = null) public async Task<IActionResult> Checkout(string invoiceId, string id = null, string cryptoCode = null)
{ {
if (cryptoCode == null)
cryptoCode = "BTC";
//Keep compatibility with Bitpay //Keep compatibility with Bitpay
invoiceId = invoiceId ?? id; invoiceId = invoiceId ?? id;
id = invoiceId; id = invoiceId;
@@ -118,21 +117,24 @@ namespace BTCPayServer.Controllers
private async Task<PaymentModel> GetInvoiceModel(string invoiceId, string cryptoCode) private async Task<PaymentModel> GetInvoiceModel(string invoiceId, string cryptoCode)
{ {
if (cryptoCode == null)
throw new ArgumentNullException(nameof(cryptoCode));
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId); var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
var store = await _StoreRepository.FindStore(invoice.StoreId);
if (cryptoCode == null)
cryptoCode = store.GetDefaultCrypto();
var network = _NetworkProvider.GetNetwork(cryptoCode); var network = _NetworkProvider.GetNetwork(cryptoCode);
if (invoice == null || network == null || !invoice.Support(network)) if (invoice == null || network == null || !invoice.Support(network))
return null; return null;
var cryptoData = invoice.GetCryptoData(network); var cryptoData = invoice.GetCryptoData(network);
var store = await _StoreRepository.FindStore(invoice.StoreId);
var dto = invoice.EntityToDTO(_NetworkProvider); var dto = invoice.EntityToDTO(_NetworkProvider);
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode == network.CryptoCode); var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode == network.CryptoCode);
var currency = invoice.ProductInformation.Currency; var currency = invoice.ProductInformation.Currency;
var accounting = cryptoData.Calculate(); var accounting = cryptoData.Calculate();
var model = new PaymentModel() var model = new PaymentModel()
{ {
CryptoCode = network.CryptoCode,
ServerUrl = HttpContext.Request.GetAbsoluteRoot(), ServerUrl = HttpContext.Request.GetAbsoluteRoot(),
OrderId = invoice.OrderId, OrderId = invoice.OrderId,
InvoiceId = invoice.Id, InvoiceId = invoice.Id,
@@ -151,7 +153,8 @@ namespace BTCPayServer.Controllers
InvoiceBitcoinUrl = cryptoInfo.PaymentUrls.BIP21, InvoiceBitcoinUrl = cryptoInfo.PaymentUrls.BIP21,
TxCount = accounting.TxCount, TxCount = accounting.TxCount,
BtcPaid = accounting.Paid.ToString(), BtcPaid = accounting.Paid.ToString(),
Status = invoice.Status Status = invoice.Status,
CryptoImage = "/" + Url.Content(network.CryptoImagePath)
}; };
var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds); var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);
@@ -182,10 +185,9 @@ namespace BTCPayServer.Controllers
[HttpGet] [HttpGet]
[Route("i/{invoiceId}/status")] [Route("i/{invoiceId}/status")]
[Route("i/{invoiceId}/{cryptoCode}/status")]
public async Task<IActionResult> GetStatus(string invoiceId, string cryptoCode) public async Task<IActionResult> GetStatus(string invoiceId, string cryptoCode)
{ {
if (cryptoCode == null)
cryptoCode = "BTC";
var model = await GetInvoiceModel(invoiceId, cryptoCode); var model = await GetInvoiceModel(invoiceId, cryptoCode);
if (model == null) if (model == null)
return NotFound(); return NotFound();

View File

@@ -153,6 +153,7 @@ namespace BTCPayServer.Controllers
var vm = new StoreViewModel(); var vm = new StoreViewModel();
vm.Id = store.Id; vm.Id = store.Id;
vm.StoreName = store.StoreName; vm.StoreName = store.StoreName;
vm.SetCryptoCurrencies(_ExplorerProvider, store.GetDefaultCrypto());
vm.StoreWebsite = store.StoreWebsite; vm.StoreWebsite = store.StoreWebsite;
vm.NetworkFee = !storeBlob.NetworkFeeDisabled; vm.NetworkFee = !storeBlob.NetworkFeeDisabled;
vm.SpeedPolicy = store.SpeedPolicy; vm.SpeedPolicy = store.SpeedPolicy;
@@ -286,6 +287,13 @@ namespace BTCPayServer.Controllers
store.StoreWebsite = model.StoreWebsite; store.StoreWebsite = model.StoreWebsite;
} }
if (store.GetDefaultCrypto() != model.DefaultCryptoCurrency)
{
needUpdate = true;
store.SetDefaultCrypto(model.DefaultCryptoCurrency);
}
model.SetCryptoCurrencies(_ExplorerProvider, model.DefaultCryptoCurrency);
var blob = store.GetStoreBlob(); var blob = store.GetStoreBlob();
blob.NetworkFeeDisabled = !model.NetworkFee; blob.NetworkFeeDisabled = !model.NetworkFee;
blob.MonitoringExpiration = model.MonitoringExpiration; blob.MonitoringExpiration = model.MonitoringExpiration;

View File

@@ -141,6 +141,19 @@ namespace BTCPayServer.Data
get; get;
set; set;
} }
[Obsolete("Use GetDefaultCrypto instead")]
public string DefaultCrypto { get; set; }
#pragma warning disable CS0618
public string GetDefaultCrypto()
{
return DefaultCrypto ?? "BTC";
}
public void SetDefaultCrypto(string defaultCryptoCurrency)
{
DefaultCrypto = defaultCryptoCurrency;
}
#pragma warning restore CS0618
static Network Dummy = Network.Main; static Network Dummy = Network.Main;

View File

@@ -0,0 +1,485 @@
// <auto-generated />
using BTCPayServer.Data;
using BTCPayServer.Services.Invoices;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using System;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20180109021122_defaultcrypto")]
partial class defaultcrypto
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.1-rtm-125");
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
{
b.Property<string>("Address")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset?>("CreatedTime");
b.Property<string>("InvoiceDataId");
b.HasKey("Address");
b.HasIndex("InvoiceDataId");
b.ToTable("AddressInvoices");
});
modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b =>
{
b.Property<string>("InvoiceDataId");
b.Property<string>("Address");
b.Property<DateTimeOffset>("Assigned");
b.Property<string>("CryptoCode");
b.Property<DateTimeOffset?>("UnAssigned");
b.HasKey("InvoiceDataId", "Address");
b.ToTable("HistoricalAddressInvoices");
});
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<byte[]>("Blob");
b.Property<DateTimeOffset>("Created");
b.Property<string>("CustomerEmail");
b.Property<string>("ExceptionStatus");
b.Property<string>("ItemCode");
b.Property<string>("OrderId");
b.Property<string>("Status");
b.Property<string>("StoreDataId");
b.HasKey("Id");
b.HasIndex("StoreDataId");
b.ToTable("Invoices");
});
modelBuilder.Entity("BTCPayServer.Data.PairedSINData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Facade");
b.Property<string>("Label");
b.Property<DateTimeOffset>("PairingTime");
b.Property<string>("SIN");
b.Property<string>("StoreDataId");
b.HasKey("Id");
b.HasIndex("SIN");
b.HasIndex("StoreDataId");
b.ToTable("PairedSINData");
});
modelBuilder.Entity("BTCPayServer.Data.PairingCodeData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateCreated");
b.Property<DateTimeOffset>("Expiration");
b.Property<string>("Facade");
b.Property<string>("Label");
b.Property<string>("SIN");
b.Property<string>("StoreDataId");
b.Property<string>("TokenValue");
b.HasKey("Id");
b.ToTable("PairingCodes");
});
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("Accounted");
b.Property<byte[]>("Blob");
b.Property<string>("InvoiceDataId");
b.HasKey("Id");
b.HasIndex("InvoiceDataId");
b.ToTable("Payments");
});
modelBuilder.Entity("BTCPayServer.Data.PendingInvoiceData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.HasKey("Id");
b.ToTable("PendingInvoices");
});
modelBuilder.Entity("BTCPayServer.Data.RefundAddressesData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<byte[]>("Blob");
b.Property<string>("InvoiceDataId");
b.HasKey("Id");
b.HasIndex("InvoiceDataId");
b.ToTable("RefundAddresses");
});
modelBuilder.Entity("BTCPayServer.Data.SettingData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("Settings");
});
modelBuilder.Entity("BTCPayServer.Data.StoreData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("DefaultCrypto");
b.Property<string>("DerivationStrategies");
b.Property<string>("DerivationStrategy");
b.Property<int>("SpeedPolicy");
b.Property<byte[]>("StoreBlob");
b.Property<byte[]>("StoreCertificate");
b.Property<string>("StoreName");
b.Property<string>("StoreWebsite");
b.HasKey("Id");
b.ToTable("Stores");
});
modelBuilder.Entity("BTCPayServer.Data.UserStore", b =>
{
b.Property<string>("ApplicationUserId");
b.Property<string>("StoreDataId");
b.Property<string>("Role");
b.HasKey("ApplicationUserId", "StoreDataId");
b.HasIndex("StoreDataId");
b.ToTable("UserStore");
});
modelBuilder.Entity("BTCPayServer.Models.ApplicationUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<bool>("RequiresEmailConfirmation");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("AddressInvoices")
.HasForeignKey("InvoiceDataId");
});
modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData")
.WithMany("HistoricalAddressInvoices")
.HasForeignKey("InvoiceDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany()
.HasForeignKey("StoreDataId");
});
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("Payments")
.HasForeignKey("InvoiceDataId");
});
modelBuilder.Entity("BTCPayServer.Data.RefundAddressesData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("RefundAddresses")
.HasForeignKey("InvoiceDataId");
});
modelBuilder.Entity("BTCPayServer.Data.UserStore", b =>
{
b.HasOne("BTCPayServer.Models.ApplicationUser", "ApplicationUser")
.WithMany("UserStores")
.HasForeignKey("ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany("UserStores")
.HasForeignKey("StoreDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("BTCPayServer.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("BTCPayServer.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("BTCPayServer.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("BTCPayServer.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
public partial class defaultcrypto : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "DefaultCrypto",
table: "Stores",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DefaultCrypto",
table: "Stores");
}
}
}

View File

@@ -190,6 +190,8 @@ namespace BTCPayServer.Migrations
b.Property<string>("Id") b.Property<string>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<string>("DefaultCrypto");
b.Property<string>("DerivationStrategies"); b.Property<string>("DerivationStrategies");
b.Property<string>("DerivationStrategy"); b.Property<string>("DerivationStrategy");

View File

@@ -7,6 +7,7 @@ namespace BTCPayServer.Models.InvoicingModels
{ {
public class PaymentModel public class PaymentModel
{ {
public string CryptoCode { get; set; }
public string ServerUrl { get; set; } public string ServerUrl { get; set; }
public string InvoiceId { get; set; } public string InvoiceId { get; set; }
public string BtcAddress { get; set; } public string BtcAddress { get; set; }
@@ -31,5 +32,6 @@ namespace BTCPayServer.Models.InvoicingModels
public string StoreEmail { get; set; } public string StoreEmail { get; set; }
public string OrderId { get; set; } public string OrderId { get; set; }
public string CryptoImage { get; set; }
} }
} }

View File

@@ -16,9 +16,14 @@ namespace BTCPayServer.Models.StoreViewModels
public string Crypto { get; set; } public string Crypto { get; set; }
public string Value { get; set; } public string Value { get; set; }
} }
class Format
{
public string Name { get; set; }
public string Value { get; set; }
}
public StoreViewModel() public StoreViewModel()
{ {
} }
public string Id { get; set; } public string Id { get; set; }
@@ -66,5 +71,17 @@ namespace BTCPayServer.Models.StoreViewModels
{ {
get; set; get; set;
} }
public SelectList CryptoCurrencies { get; set; }
[Display(Name = "Default crypto currency on checkout")]
public string DefaultCryptoCurrency { get; set; }
public void SetCryptoCurrencies(ExplorerClientProvider explorerProvider, string defaultCrypto)
{
var choices = explorerProvider.GetAll().Select(o => new Format() { Name = o.Item1.CryptoCode, Value = o.Item1.CryptoCode }).ToArray();
var chosen = choices.FirstOrDefault(f => f.Name == defaultCrypto) ?? choices.FirstOrDefault();
CryptoCurrencies = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen);
DefaultCryptoCurrency = chosen.Name;
}
} }
} }

View File

@@ -106,11 +106,11 @@
<!----> <!---->
<div class="single-item-order__right"> <div class="single-item-order__right">
<div class="single-item-order__right__btc-price" id="buyerTotalBtcAmount"> <div class="single-item-order__right__btc-price" id="buyerTotalBtcAmount">
<span>{{ srvModel.btcDue }} BTC</span> <span>{{ srvModel.btcDue }} {{ srvModel.cryptoCode }}</span>
</div> </div>
<!----> <!---->
<div class="single-item-order__right__ex-rate"> <div class="single-item-order__right__ex-rate">
1 BTC = {{ srvModel.rate }} 1 {{ srvModel.cryptoCode }} = {{ srvModel.rate }}
</div> </div>
<!----> <!---->
</div> </div>
@@ -124,23 +124,23 @@
<!----> <!---->
<div class="line-items__item"> <div class="line-items__item">
<div class="line-items__item__label" i18n="">Payment Amount</div> <div class="line-items__item__label" i18n="">Payment Amount</div>
<div class="line-items__item__value">{{srvModel.btcAmount}} BTC</div> <div class="line-items__item__value">{{srvModel.btcAmount}} {{ srvModel.cryptoCode }}</div>
</div> </div>
<div class="line-items__item"> <div class="line-items__item">
<div class="line-items__item__label"> <div class="line-items__item__label">
<span i18n="">Network Cost</span> <span i18n="">Network Cost</span>
</div> </div>
<div class="line-items__item__value" i18n="">{{srvModel.txCount }} transaction x {{ srvModel.txFees}} BTC</div> <div class="line-items__item__value" i18n="">{{srvModel.txCount }} transaction x {{ srvModel.txFees}} {{ srvModel.cryptoCode }}</div>
</div> </div>
<div class="line-items__item"> <div class="line-items__item">
<div class="line-items__item__label"> <div class="line-items__item__label">
<span i18n="">Already Paid</span> <span i18n="">Already Paid</span>
</div> </div>
<div class="line-items__item__value" i18n="">-{{srvModel.btcPaid }} BTC</div> <div class="line-items__item__value" i18n="">-{{srvModel.btcPaid }} {{ srvModel.cryptoCode }}</div>
</div> </div>
<div class="line-items__item line-items__item--total"> <div class="line-items__item line-items__item--total">
<div class="line-items__item__label" i18n="">Due </div> <div class="line-items__item__label" i18n="">Due </div>
<div class="line-items__item__value">{{srvModel.btcDue}} BTC</div> <div class="line-items__item__value">{{srvModel.btcDue}} {{ srvModel.cryptoCode }}</div>
</div> </div>
<!----> <!---->
</div> </div>
@@ -311,7 +311,7 @@
<bp-refund-address name="refundAddress" ngmodel="" class="ng-untouched ng-pristine ng-invalid"> <bp-refund-address name="refundAddress" ngmodel="" class="ng-untouched ng-pristine ng-invalid">
<div class="bp-refund-address"> <div class="bp-refund-address">
<div class="bitcoin-logo"> <div class="bitcoin-logo">
<div><img src="~/imlegacy/bitcoin-symbol.svg"></div> <div><img src="@Model.CryptoImage"></div>
</div> </div>
<input class="bp-input {'not-empty': addressValue.length &gt; 0} ng-untouched ng-pristine ng-valid" id="refund-address-input" name="refundAddress" ngclass="{'not-empty': addressValue.length &gt; 0}"> <input class="bp-input {'not-empty': addressValue.length &gt; 0} ng-untouched ng-pristine ng-valid" id="refund-address-input" name="refundAddress" ngclass="{'not-empty': addressValue.length &gt; 0}">
</div> </div>
@@ -336,14 +336,14 @@
</div> </div>
<div class="bp-view payment manual-flow" id="copy"> <div class="bp-view payment manual-flow" id="copy">
<div class="manual__step-two__instructions"> <div class="manual__step-two__instructions">
<span i18n="">To complete your payment, please send {{ srvModel.btcDue }} BTC to the address below.</span> <span i18n="">To complete your payment, please send {{ srvModel.btcDue }} {{ srvModel.cryptoCode }} to the address below.</span>
</div> </div>
<div class="manual-box flipped" style="margin-bottom: 30px;"> <div class="manual-box flipped" style="margin-bottom: 30px;">
<div class="manual-box__amount"> <div class="manual-box__amount">
<div class="manual-box__amount__label label" i18n="">Amount</div> <div class="manual-box__amount__label label" i18n="">Amount</div>
<!----> <!---->
<div class="manual-box__amount__value copy-cursor" ngxclipboard=""> <div class="manual-box__amount__value copy-cursor" ngxclipboard="">
<span>{{srvModel.btcDue}}</span> BTC <span>{{srvModel.btcDue}}</span> {{ srvModel.cryptoCode }}
<div class="copied-label"> <div class="copied-label">
<span i18n="">Copied</span> <span i18n="">Copied</span>
</div> </div>
@@ -360,7 +360,7 @@
<div class="manual-box__address__value copy-cursor" ngxclipboard=""> <div class="manual-box__address__value copy-cursor" ngxclipboard="">
<div class="manual-box__address__wrapper"> <div class="manual-box__address__wrapper">
<div class="manual-box__address__wrapper__logo"> <div class="manual-box__address__wrapper__logo">
<img src="~/imlegacy/bitcoin-symbol.svg"> <img src="@Model.CryptoImage">
</div> </div>
<div class="manual-box__address__wrapper__value">{{srvModel.btcAddress}}</div> <div class="manual-box__address__wrapper__value">{{srvModel.btcAddress}}</div>
</div> </div>

View File

@@ -35,6 +35,10 @@
<input asp-for="StoreWebsite" class="form-control" /> <input asp-for="StoreWebsite" class="form-control" />
<span asp-validation-for="StoreWebsite" class="text-danger"></span> <span asp-validation-for="StoreWebsite" class="text-danger"></span>
</div> </div>
<div class="form-group">
<label asp-for="DefaultCryptoCurrency"></label>
<select asp-for="DefaultCryptoCurrency" asp-items="Model.CryptoCurrencies" class="form-control"></select>
</div>
<div class="form-group"> <div class="form-group">
<label asp-for="NetworkFee"></label> <label asp-for="NetworkFee"></label>
<input asp-for="NetworkFee" type="checkbox" class="form-check" /> <input asp-for="NetworkFee" type="checkbox" class="form-check" />
@@ -69,12 +73,12 @@
</thead> </thead>
<tbody> <tbody>
@foreach(var scheme in Model.DerivationSchemes) @foreach(var scheme in Model.DerivationSchemes)
{ {
<tr> <tr>
<td>@scheme.Crypto</td> <td>@scheme.Crypto</td>
<td>@scheme.Value</td> <td>@scheme.Value</td>
</tr> </tr>
} }
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0.847 0.876 329.254 329.256"><title>Litecoin</title><path d="M330.102 165.503c0 90.922-73.705 164.629-164.626 164.629C74.554 330.132.848 256.425.848 165.503.848 74.582 74.554.876 165.476.876c90.92 0 164.626 73.706 164.626 164.627" fill="#bebebe"/><path d="M295.15 165.505c0 71.613-58.057 129.675-129.674 129.675-71.616 0-129.677-58.062-129.677-129.675 0-71.619 58.061-129.677 129.677-129.677 71.618 0 129.674 58.057 129.674 129.677" fill="#bebebe"/><path d="M155.854 209.482l10.693-40.264 25.316-9.249 6.297-23.663-.215-.587-24.92 9.104 17.955-67.608h-50.921l-23.481 88.23-19.605 7.162-6.478 24.395 19.59-7.156-13.839 51.998h135.521l8.688-32.362h-84.601" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 747 B

View File

@@ -192,7 +192,7 @@ function onDataCallback(jsonData) {
} }
function fetchStatus() { function fetchStatus() {
var path = srvModel.serverUrl + "/i/" + srvModel.invoiceId + "/status"; var path = srvModel.serverUrl + "/i/" + srvModel.invoiceId + "/" + srvModel.cryptoCode + "/status";
$.ajax({ $.ajax({
url: path, url: path,
type: "GET" type: "GET"
@@ -215,7 +215,7 @@ if (supportsWebSockets) {
}; };
} }
catch (e) { catch (e) {
console.error("Error while connecting to websocket for invoice notifictions"); console.error("Error while connecting to websocket for invoice notifications");
} }
} }