btcpayserver/BTCPayServer/Controllers/GreenField/InvoiceController.cs

277 lines
10 KiB
C#
Raw Normal View History

2020-07-22 13:58:41 +02:00
using System;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Client;
2020-07-24 08:13:21 +02:00
using BTCPayServer.Client.Models;
2020-07-24 09:40:37 +02:00
using BTCPayServer.Models.InvoicingModels;
2020-07-22 13:58:41 +02:00
using BTCPayServer.Payments;
using BTCPayServer.Security;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Validation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
2020-07-22 13:58:41 +02:00
using NBitcoin;
using NBitpayClient;
2020-07-27 09:46:23 +02:00
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
2020-07-22 13:58:41 +02:00
using CreateInvoiceRequest = BTCPayServer.Client.Models.CreateInvoiceRequest;
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
namespace BTCPayServer.Controllers.GreenField
{
[ApiController]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[EnableCors(CorsPolicies.All)]
public class GreenFieldInvoiceController : Controller
{
private readonly InvoiceController _invoiceController;
private readonly InvoiceRepository _invoiceRepository;
public GreenFieldInvoiceController(InvoiceController invoiceController, InvoiceRepository invoiceRepository)
{
_invoiceController = invoiceController;
_invoiceRepository = invoiceRepository;
}
[Authorize(Policy = Policies.CanViewInvoices,
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
2020-07-24 12:46:46 +02:00
[HttpGet("~/api/v1/stores/{storeId}/invoices")]
public async Task<IActionResult> GetInvoices(string storeId, bool includeArchived = false)
2020-07-22 13:58:41 +02:00
{
var store = HttpContext.GetStoreData();
if (store == null)
{
return NotFound();
}
2020-07-24 08:13:21 +02:00
var invoices =
await _invoiceRepository.GetInvoices(new InvoiceQuery()
{
StoreId = new[] { store.Id },
IncludeArchived = includeArchived
2020-07-24 08:13:21 +02:00
});
2020-07-22 13:58:41 +02:00
return Ok(invoices.Select(ToModel));
}
[Authorize(Policy = Policies.CanViewInvoices,
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpGet("~/api/v1/stores/{storeId}/invoices/{invoiceId}")]
public async Task<IActionResult> GetInvoice(string storeId, string invoiceId)
{
var store = HttpContext.GetStoreData();
if (store == null)
{
return NotFound();
}
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
if (invoice.StoreId != store.Id)
{
return NotFound();
}
return Ok(ToModel(invoice));
}
[Authorize(Policy = Policies.CanModifyStoreSettings,
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpDelete("~/api/v1/stores/{storeId}/invoices/{invoiceId}")]
public async Task<IActionResult> ArchiveInvoice(string storeId, string invoiceId)
{
var store = HttpContext.GetStoreData();
if (store == null)
{
return NotFound();
}
2020-07-24 08:13:21 +02:00
await _invoiceRepository.ToggleInvoiceArchival(invoiceId, true, storeId);
2020-07-22 13:58:41 +02:00
return Ok();
}
[Authorize(Policy = Policies.CanCreateInvoice,
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpPost("~/api/v1/stores/{storeId}/invoices")]
public async Task<IActionResult> CreateInvoice(string storeId, CreateInvoiceRequest request)
{
var store = HttpContext.GetStoreData();
if (store == null)
{
return NotFound();
}
if (request.Amount < 0.0m)
{
ModelState.AddModelError(nameof(request.Amount), "The amount should be 0 or more.");
}
2020-07-24 12:46:46 +02:00
if (string.IsNullOrEmpty(request.Currency))
{
ModelState.AddModelError(nameof(request.Currency), "Currency is required");
}
2020-07-22 13:58:41 +02:00
if (request.Checkout.PaymentMethods?.Any() is true)
{
for (int i = 0; i < request.Checkout.PaymentMethods.Length; i++)
{
if (!PaymentMethodId.TryParse(request.Checkout.PaymentMethods[i], out _))
{
request.AddModelError(invoiceRequest => invoiceRequest.Checkout.PaymentMethods[i],
"Invalid payment method", this);
}
}
}
if (request.Checkout.Expiration != null && request.Checkout.Expiration < TimeSpan.FromSeconds(30.0))
2020-07-22 13:58:41 +02:00
{
request.AddModelError(invoiceRequest => invoiceRequest.Checkout.Expiration,
"Expiration time must be at least 30 seconds", this);
2020-07-22 13:58:41 +02:00
}
if (request.Checkout.PaymentTolerance != null &&
(request.Checkout.PaymentTolerance < 0 || request.Checkout.PaymentTolerance > 100))
{
request.AddModelError(invoiceRequest => invoiceRequest.Checkout.PaymentTolerance,
"PaymentTolerance can only be between 0 and 100 percent", this);
}
if (!ModelState.IsValid)
return this.CreateValidationError(ModelState);
2020-07-24 12:46:46 +02:00
try
{
var invoice = await _invoiceController.CreateInvoiceCoreRaw(request, store,
2020-07-24 12:46:46 +02:00
Request.GetAbsoluteUri(""));
return Ok(ToModel(invoice));
}
catch (BitpayHttpException e)
{
2020-07-27 10:43:35 +02:00
return this.CreateAPIError(null, e.Message);
2020-07-24 12:46:46 +02:00
}
2020-07-22 13:58:41 +02:00
}
2020-07-24 08:13:21 +02:00
[Authorize(Policy = Policies.CanModifyStoreSettings,
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
2020-07-27 10:43:35 +02:00
[HttpPost("~/api/v1/stores/{storeId}/invoices/{invoiceId}/status")]
public async Task<IActionResult> MarkInvoiceStatus(string storeId, string invoiceId,
MarkInvoiceStatusRequest request)
2020-07-24 08:13:21 +02:00
{
var store = HttpContext.GetStoreData();
if (store == null)
{
return NotFound();
}
2020-07-24 09:40:37 +02:00
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
if (invoice.StoreId != store.Id)
{
return NotFound();
}
2020-07-27 10:43:35 +02:00
if (!await _invoiceRepository.MarkInvoiceStatus(invoice.Id, request.Status))
2020-07-24 09:40:37 +02:00
{
2020-07-27 10:43:35 +02:00
ModelState.AddModelError(nameof(request.Status),
"Status can only be marked to invalid or complete within certain conditions.");
2020-07-24 09:40:37 +02:00
}
2020-07-27 10:43:35 +02:00
if (!ModelState.IsValid)
return this.CreateValidationError(ModelState);
return await GetInvoice(storeId, invoiceId);
}
[Authorize(Policy = Policies.CanCreateInvoice,
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpPost("~/api/v1/stores/{storeId}/invoices/{invoiceId}/email")]
public async Task<IActionResult> AddCustomerEmail(string storeId, string invoiceId,
AddCustomerEmailRequest request)
{
var store = HttpContext.GetStoreData();
if (store == null)
2020-07-24 09:40:37 +02:00
{
2020-07-27 10:43:35 +02:00
return NotFound();
2020-07-24 09:40:37 +02:00
}
2020-07-27 10:43:35 +02:00
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
if (invoice.StoreId != store.Id)
2020-07-24 09:40:37 +02:00
{
2020-07-27 10:43:35 +02:00
return NotFound();
}
2020-07-24 09:40:37 +02:00
2020-07-27 10:43:35 +02:00
if (!EmailValidator.IsEmail(request.Email))
{
request.AddModelError(invoiceRequest => invoiceRequest.Email, "Invalid email address",
this);
}
else if (!string.IsNullOrEmpty(invoice.Metadata.BuyerEmail))
2020-07-27 10:43:35 +02:00
{
request.AddModelError(invoiceRequest => invoiceRequest.Email, "Email address already set",
this);
2020-07-24 09:40:37 +02:00
}
if (!ModelState.IsValid)
return this.CreateValidationError(ModelState);
await _invoiceRepository.UpdateInvoice(invoice.Id, new UpdateCustomerModel() { Email = request.Email });
2020-07-27 10:43:35 +02:00
2020-07-24 08:13:21 +02:00
return await GetInvoice(storeId, invoiceId);
}
2020-07-27 10:43:35 +02:00
[Authorize(Policy = Policies.CanModifyStoreSettings,
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpPost("~/api/v1/stores/{storeId}/invoices/{invoiceId}/unarchive")]
public async Task<IActionResult> UnarchiveInvoice(string storeId, string invoiceId)
{
var store = HttpContext.GetStoreData();
if (store == null)
{
return NotFound();
}
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
if (invoice.StoreId != store.Id)
{
return NotFound();
}
if (!invoice.Archived)
{
return this.CreateAPIError("already-unarchived", "Invoice is already unarchived");
}
if (!ModelState.IsValid)
return this.CreateValidationError(ModelState);
await _invoiceRepository.ToggleInvoiceArchival(invoiceId, false, storeId);
return await GetInvoice(storeId, invoiceId);
}
2020-07-24 09:40:37 +02:00
private InvoiceData ToModel(InvoiceEntity entity)
2020-07-22 13:58:41 +02:00
{
return new InvoiceData()
{
Amount = entity.Price,
2020-07-22 13:58:41 +02:00
Id = entity.Id,
2020-07-24 09:40:37 +02:00
Status = entity.Status,
2020-07-27 10:43:35 +02:00
AdditionalStatus = entity.ExceptionStatus,
Currency = entity.Currency,
Metadata = entity.Metadata.ToJObject(),
2020-07-22 13:58:41 +02:00
Checkout = new CreateInvoiceRequest.CheckoutOptions()
{
Expiration = entity.ExpirationTime - entity.InvoiceTime,
Monitoring = entity.MonitoringExpiration - entity.ExpirationTime,
2020-07-22 13:58:41 +02:00
PaymentTolerance = entity.PaymentTolerance,
PaymentMethods =
entity.GetPaymentMethods().Select(method => method.GetId().ToString()).ToArray(),
SpeedPolicy = entity.SpeedPolicy
}
2020-07-22 13:58:41 +02:00
};
}
}
}