2017-09-13 15:47:34 +09:00
|
|
|
|
using NBitcoin;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
2017-09-26 01:31:43 +09:00
|
|
|
|
using BTCPayServer.Models;
|
|
|
|
|
using NBitpayClient;
|
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
using NBitcoin.DataEncoders;
|
2017-10-25 01:41:01 +09:00
|
|
|
|
using BTCPayServer.Data;
|
2017-11-06 00:31:02 -08:00
|
|
|
|
using NBXplorer.Models;
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-20 14:06:37 -05:00
|
|
|
|
namespace BTCPayServer.Services.Invoices
|
2017-09-13 15:47:34 +09:00
|
|
|
|
{
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public class BuyerInformation
|
|
|
|
|
{
|
|
|
|
|
[JsonProperty(PropertyName = "buyerName")]
|
|
|
|
|
public string BuyerName
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "buyerEmail")]
|
|
|
|
|
public string BuyerEmail
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "buyerCountry")]
|
|
|
|
|
public string BuyerCountry
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "buyerZip")]
|
|
|
|
|
public string BuyerZip
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "buyerState")]
|
|
|
|
|
public string BuyerState
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "buyerCity")]
|
|
|
|
|
public string BuyerCity
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "buyerAddress2")]
|
|
|
|
|
public string BuyerAddress2
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "buyerAddress1")]
|
|
|
|
|
public string BuyerAddress1
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
[JsonProperty(PropertyName = "buyerPhone")]
|
|
|
|
|
public string BuyerPhone
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public class ProductInformation
|
|
|
|
|
{
|
|
|
|
|
[JsonProperty(PropertyName = "itemDesc")]
|
|
|
|
|
public string ItemDesc
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "itemCode")]
|
|
|
|
|
public string ItemCode
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "physical")]
|
|
|
|
|
public bool Physical
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
[JsonProperty(PropertyName = "price")]
|
|
|
|
|
public double Price
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
[JsonProperty(PropertyName = "currency")]
|
|
|
|
|
public string Currency
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public enum SpeedPolicy
|
|
|
|
|
{
|
|
|
|
|
HighSpeed = 0,
|
|
|
|
|
MediumSpeed = 1,
|
|
|
|
|
LowSpeed = 2
|
|
|
|
|
}
|
|
|
|
|
public class InvoiceEntity
|
|
|
|
|
{
|
|
|
|
|
public string Id
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public string StoreId
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
2017-09-14 01:06:11 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public int GetTxCount()
|
|
|
|
|
{
|
|
|
|
|
return Calculate().TxCount;
|
|
|
|
|
}
|
2017-09-14 01:06:11 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public string OrderId
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public Money GetTotalCryptoDue()
|
|
|
|
|
{
|
|
|
|
|
return Calculate().TotalDue;
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
private (Money TotalDue, Money Paid, int TxCount) Calculate()
|
|
|
|
|
{
|
|
|
|
|
var totalDue = Money.Coins((decimal)(ProductInformation.Price / Rate)) + TxFee;
|
|
|
|
|
var paid = Money.Zero;
|
|
|
|
|
int txCount = 1;
|
|
|
|
|
var payments =
|
|
|
|
|
Payments
|
2017-11-06 00:31:02 -08:00
|
|
|
|
.Where(p => p.Accounted)
|
2017-10-27 17:53:04 +09:00
|
|
|
|
.OrderByDescending(p => p.ReceivedTime)
|
|
|
|
|
.Select(_ =>
|
|
|
|
|
{
|
|
|
|
|
paid += _.Output.Value;
|
|
|
|
|
return _;
|
|
|
|
|
})
|
|
|
|
|
.TakeWhile(_ =>
|
|
|
|
|
{
|
|
|
|
|
var paidEnough = totalDue <= paid;
|
|
|
|
|
if (!paidEnough)
|
|
|
|
|
{
|
|
|
|
|
txCount++;
|
|
|
|
|
totalDue += TxFee;
|
|
|
|
|
}
|
|
|
|
|
return !paidEnough;
|
|
|
|
|
})
|
|
|
|
|
.ToArray();
|
|
|
|
|
return (totalDue, paid, txCount);
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public Money GetTotalPaid()
|
|
|
|
|
{
|
|
|
|
|
return Calculate().Paid;
|
|
|
|
|
}
|
|
|
|
|
public Money GetCryptoDue()
|
|
|
|
|
{
|
|
|
|
|
var o = Calculate();
|
|
|
|
|
var v = o.TotalDue - o.Paid;
|
|
|
|
|
return v < Money.Zero ? Money.Zero : v;
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public SpeedPolicy SpeedPolicy
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public double Rate
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public DateTimeOffset InvoiceTime
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public DateTimeOffset ExpirationTime
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public BitcoinAddress DepositAddress
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public ProductInformation ProductInformation
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public BuyerInformation BuyerInformation
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public string PosData
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public string DerivationStrategy
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public string Status
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public string ExceptionStatus
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public List<PaymentEntity> Payments
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public bool Refundable
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public string RefundMail
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public string RedirectURL
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public Money TxFee
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public bool FullNotifications
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public string NotificationURL
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public string ServerUrl
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public DateTimeOffset? MonitoringExpiration
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public HistoricalAddressInvoiceData[] HistoricalAddresses
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-11-06 00:31:02 -08:00
|
|
|
|
public HashSet<string> AvailableAddressHashes
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public bool IsExpired()
|
|
|
|
|
{
|
|
|
|
|
return DateTimeOffset.UtcNow > ExpirationTime;
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-09-26 01:31:43 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public InvoiceResponse EntityToDTO()
|
|
|
|
|
{
|
|
|
|
|
ServerUrl = ServerUrl ?? "";
|
|
|
|
|
InvoiceResponse dto = new InvoiceResponse
|
|
|
|
|
{
|
|
|
|
|
Id = Id,
|
|
|
|
|
OrderId = OrderId,
|
|
|
|
|
PosData = PosData,
|
|
|
|
|
CurrentTime = DateTimeOffset.UtcNow,
|
|
|
|
|
InvoiceTime = InvoiceTime,
|
|
|
|
|
ExpirationTime = ExpirationTime,
|
|
|
|
|
BTCPrice = Money.Coins((decimal)(1.0 / Rate)).ToString(),
|
|
|
|
|
Status = Status,
|
|
|
|
|
Url = ServerUrl.WithTrailingSlash() + "invoice?id=" + Id,
|
|
|
|
|
Currency = ProductInformation.Currency,
|
|
|
|
|
Flags = new Flags() { Refundable = Refundable }
|
|
|
|
|
};
|
|
|
|
|
Populate(ProductInformation, dto);
|
|
|
|
|
Populate(BuyerInformation, dto);
|
|
|
|
|
dto.ExRates = new Dictionary<string, double>
|
|
|
|
|
{
|
|
|
|
|
{ ProductInformation.Currency, Rate }
|
|
|
|
|
};
|
|
|
|
|
dto.PaymentUrls = new InvoicePaymentUrls()
|
|
|
|
|
{
|
|
|
|
|
BIP72 = $"bitcoin:{DepositAddress}?amount={GetCryptoDue()}&r={ServerUrl.WithTrailingSlash() + ($"i/{Id}")}",
|
|
|
|
|
BIP72b = $"bitcoin:?r={ServerUrl.WithTrailingSlash() + ($"i/{Id}")}",
|
|
|
|
|
BIP73 = ServerUrl.WithTrailingSlash() + ($"i/{Id}"),
|
|
|
|
|
BIP21 = $"bitcoin:{DepositAddress}?amount={GetCryptoDue()}",
|
|
|
|
|
};
|
|
|
|
|
dto.BitcoinAddress = DepositAddress.ToString();
|
|
|
|
|
dto.Token = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16)); //No idea what it is useful for
|
|
|
|
|
dto.Guid = Guid.NewGuid().ToString();
|
2017-09-26 01:31:43 +09:00
|
|
|
|
|
2017-11-06 00:31:02 -08:00
|
|
|
|
var paid = Payments.Where(p => p.Accounted).Select(p => p.Output.Value).Sum();
|
2017-10-27 17:53:04 +09:00
|
|
|
|
dto.BTCPaid = paid.ToString();
|
|
|
|
|
dto.BTCDue = GetCryptoDue().ToString();
|
2017-09-26 01:31:43 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
dto.ExceptionStatus = ExceptionStatus == null ? new JValue(false) : new JValue(ExceptionStatus);
|
|
|
|
|
return dto;
|
|
|
|
|
}
|
2017-09-26 01:31:43 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
private void Populate<TFrom, TDest>(TFrom from, TDest dest)
|
|
|
|
|
{
|
|
|
|
|
var str = JsonConvert.SerializeObject(from);
|
|
|
|
|
JsonConvert.PopulateObject(str, dest);
|
|
|
|
|
}
|
2017-10-13 00:25:45 +09:00
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public Money GetNetworkFee()
|
|
|
|
|
{
|
|
|
|
|
var item = Calculate();
|
|
|
|
|
return TxFee * item.TxCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
2017-11-06 00:31:02 -08:00
|
|
|
|
public class AccountedPaymentEntity
|
|
|
|
|
{
|
|
|
|
|
public int Confirmations
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
public PaymentEntity Payment { get; set; }
|
|
|
|
|
public Transaction Transaction { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
|
public class PaymentEntity
|
|
|
|
|
{
|
|
|
|
|
public DateTimeOffset ReceivedTime
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public OutPoint Outpoint
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
public TxOut Output
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
2017-11-06 00:31:02 -08:00
|
|
|
|
public bool Accounted
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
2017-10-27 17:53:04 +09:00
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
}
|