mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-19 05:33:31 +01:00
make individual action items
This commit is contained in:
parent
5f6f54db36
commit
8dea7df82a
@ -47,14 +47,35 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<InvoiceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<InvoiceData> UpdateInvoice(string storeId, string invoiceId,
|
||||
UpdateInvoiceRequest request, CancellationToken token = default)
|
||||
public virtual async Task<InvoiceData> AddCustomerEmailToInvoice(string storeId, string invoiceId,
|
||||
AddCustomerEmailRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}", bodyPayload: request,
|
||||
method: HttpMethod.Put), token);
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/email", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<InvoiceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<InvoiceData> MarkInvoiceStatus(string storeId, string invoiceId,
|
||||
MarkInvoiceStatusRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
if (request.Status!= InvoiceStatus.Complete && request.Status!= InvoiceStatus.Invalid)
|
||||
throw new ArgumentOutOfRangeException(nameof(request.Status), "Status can only be Invalid or Complete");
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/status", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<InvoiceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<InvoiceData> UnarchiveInvoice(string storeId, string invoiceId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/unarchive",
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<InvoiceData>(response);
|
||||
}
|
||||
}
|
||||
|
7
BTCPayServer.Client/Models/AddCustomerEmailRequest.cs
Normal file
7
BTCPayServer.Client/Models/AddCustomerEmailRequest.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class AddCustomerEmailRequest
|
||||
{
|
||||
public string Email { get; set; }
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ namespace BTCPayServer.Client.Models
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public InvoiceStatus Status { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public InvoiceExceptionStatus ExceptionStatus { get; set; }
|
||||
public InvoiceExceptionStatus AdditionalStatus { get; set; }
|
||||
public Dictionary<string, PaymentMethodDataModel> PaymentMethodData { get; set; }
|
||||
|
||||
public class PaymentMethodDataModel
|
||||
|
11
BTCPayServer.Client/Models/MarkInvoiceStatusRequest.cs
Normal file
11
BTCPayServer.Client/Models/MarkInvoiceStatusRequest.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class MarkInvoiceStatusRequest
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public InvoiceStatus Status { get; set; }
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class UpdateInvoiceRequest
|
||||
{
|
||||
public bool? Archived { get; set; }
|
||||
public InvoiceStatus? Status { get; set; }
|
||||
public string Email { get; set; }
|
||||
}
|
||||
}
|
@ -762,7 +762,7 @@ namespace BTCPayServer.Tests
|
||||
});
|
||||
await user.RegisterDerivationSchemeAsync("BTC");
|
||||
var newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest() { Currency = "USD", Amount = 1, Metadata = new CreateInvoiceRequest.ProductInformation(){ ItemCode = "testitem"}});
|
||||
new CreateInvoiceRequest() { Currency = "USD", Amount = 1, Metadata = "{\"itemCode\": \"testitem\"}"});
|
||||
|
||||
//list
|
||||
var invoices = await viewOnly.GetInvoices(user.StoreId);
|
||||
@ -773,29 +773,34 @@ namespace BTCPayServer.Tests
|
||||
|
||||
//get payment request
|
||||
var invoice = await viewOnly.GetInvoice(user.StoreId, newInvoice.Id);
|
||||
Assert.Equal(newInvoice.Metadata.ItemCode, invoice.Metadata.ItemCode);
|
||||
Assert.Equal(newInvoice.Metadata, invoice.Metadata);
|
||||
|
||||
//update
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnly.UpdateInvoice(user.StoreId, invoice.Id, new UpdateInvoiceRequest()
|
||||
await viewOnly.AddCustomerEmailToInvoice(user.StoreId, invoice.Id, new AddCustomerEmailRequest()
|
||||
{
|
||||
Email = "j@g.com"
|
||||
});
|
||||
});
|
||||
await client.UpdateInvoice(user.StoreId, invoice.Id, new UpdateInvoiceRequest()
|
||||
await client.AddCustomerEmailToInvoice(user.StoreId, invoice.Id, new AddCustomerEmailRequest()
|
||||
{
|
||||
Email = "j@g.com"
|
||||
});
|
||||
invoice = await viewOnly.GetInvoice(user.StoreId, newInvoice.Id);
|
||||
Assert.Equal(invoice.Customer.BuyerEmail, "j@g.com");
|
||||
Assert.Equal("j@g.com", invoice.CustomerEmail);
|
||||
|
||||
await AssertValidationError(new[] { nameof(UpdateInvoiceRequest.Email), nameof(UpdateInvoiceRequest.Archived),nameof(UpdateInvoiceRequest.Status) }, async () =>
|
||||
await AssertValidationError(new[] { nameof(AddCustomerEmailRequest.Email) }, async () =>
|
||||
{
|
||||
await client.UpdateInvoice(user.StoreId, invoice.Id, new UpdateInvoiceRequest()
|
||||
await client.AddCustomerEmailToInvoice(user.StoreId, invoice.Id, new AddCustomerEmailRequest()
|
||||
{
|
||||
Email = "j@g2.com",
|
||||
Archived = true,
|
||||
});
|
||||
});
|
||||
await AssertValidationError(new[] { nameof(MarkInvoiceStatusRequest.Status) }, async () =>
|
||||
{
|
||||
await client.MarkInvoiceStatus(user.StoreId, invoice.Id, new MarkInvoiceStatusRequest()
|
||||
{
|
||||
Status = InvoiceStatus.Complete
|
||||
});
|
||||
});
|
||||
@ -812,10 +817,7 @@ namespace BTCPayServer.Tests
|
||||
(await client.GetInvoices(user.StoreId)).Select(data => data.Id));
|
||||
|
||||
//unarchive
|
||||
await client.UpdateInvoice(user.StoreId, invoice.Id, new UpdateInvoiceRequest()
|
||||
{
|
||||
Archived = false,
|
||||
});
|
||||
await client.UnarchiveInvoice(user.StoreId, invoice.Id);
|
||||
Assert.NotNull(await client.GetInvoice(user.StoreId,invoice.Id));
|
||||
|
||||
}
|
||||
|
@ -160,8 +160,9 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPut("~/api/v1/stores/{storeId}/invoices/{invoiceId}")]
|
||||
public async Task<IActionResult> UpdateInvoice(string storeId, string invoiceId, UpdateInvoiceRequest request)
|
||||
[HttpPost("~/api/v1/stores/{storeId}/invoices/{invoiceId}/status")]
|
||||
public async Task<IActionResult> MarkInvoiceStatus(string storeId, string invoiceId,
|
||||
MarkInvoiceStatusRequest request)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
@ -175,30 +176,36 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (request.Archived.HasValue)
|
||||
{
|
||||
if (request.Archived.Value && !invoice.Archived)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.Archived),
|
||||
"You can only archive an invoice via HTTP DELETE.");
|
||||
}
|
||||
else if (!request.Archived.Value && invoice.Archived)
|
||||
{
|
||||
await _invoiceRepository.ToggleInvoiceArchival(invoiceId, false, storeId);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.Status != null)
|
||||
{
|
||||
if (!await _invoiceRepository.MarkInvoiceStatus(invoice.Id, request.Status.Value))
|
||||
if (!await _invoiceRepository.MarkInvoiceStatus(invoice.Id, request.Status))
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.Status),
|
||||
"Status can only be marked to invalid or complete within certain conditions.");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return this.CreateValidationError(ModelState);
|
||||
|
||||
return await GetInvoice(storeId, invoiceId);
|
||||
}
|
||||
|
||||
if (request.Email != null)
|
||||
[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)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice.StoreId != store.Id)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!EmailValidator.IsEmail(request.Email))
|
||||
{
|
||||
request.AddModelError(invoiceRequest => invoiceRequest.Email, "Invalid email address",
|
||||
@ -210,15 +217,45 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
this);
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return this.CreateValidationError(ModelState);
|
||||
|
||||
await _invoiceRepository.UpdateInvoice(invoice.Id, new UpdateCustomerModel() {Email = request.Email});
|
||||
|
||||
return await GetInvoice(storeId, invoiceId);
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
|
||||
|
||||
private InvoiceData ToModel(InvoiceEntity entity)
|
||||
{
|
||||
return new InvoiceData()
|
||||
@ -226,7 +263,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
Amount = entity.ProductInformation.Price,
|
||||
Id = entity.Id,
|
||||
Status = entity.Status,
|
||||
ExceptionStatus = entity.ExceptionStatus,
|
||||
AdditionalStatus = entity.ExceptionStatus,
|
||||
Currency = entity.ProductInformation.Currency,
|
||||
Metadata = entity.PosData,
|
||||
CustomerEmail = entity.RefundMail ?? entity.BuyerInformation.BuyerEmail,
|
||||
@ -307,6 +344,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
return new Models.CreateInvoiceRequest()
|
||||
{
|
||||
Buyer = buyer,
|
||||
@ -323,7 +361,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
PaymentCurrencies = entity.Checkout.PaymentMethods,
|
||||
NotificationURL = entity.Checkout.RedirectUri,
|
||||
PosData = entity.Metadata,
|
||||
Physical = pi?.Physical??false,
|
||||
Physical = pi?.Physical ?? false,
|
||||
ItemCode = pi?.ItemCode,
|
||||
ItemDesc = pi?.ItemDesc,
|
||||
TaxIncluded = pi?.TaxIncluded,
|
||||
|
@ -32,10 +32,10 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
[Authorize(Policy = Policies.CanViewPaymentRequests, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-requests")]
|
||||
public async Task<ActionResult<IEnumerable<PaymentRequestData>>> GetPaymentRequests(string storeId)
|
||||
public async Task<ActionResult<IEnumerable<PaymentRequestData>>> GetPaymentRequests(string storeId, bool includeArchived = false)
|
||||
{
|
||||
var prs = await _paymentRequestRepository.FindPaymentRequests(
|
||||
new PaymentRequestQuery() { StoreId = storeId, IncludeArchived = false });
|
||||
new PaymentRequestQuery() { StoreId = storeId, IncludeArchived = includeArchived });
|
||||
return Ok(prs.Items.Select(FromModel));
|
||||
}
|
||||
|
||||
|
@ -214,12 +214,14 @@
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"/api/v1/stores/{storeId}/invoices/{invoiceId}/email": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Invoices"
|
||||
],
|
||||
"summary": "Update invoice",
|
||||
"summary": "Add customer email to invoice",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "storeId",
|
||||
@ -240,8 +242,8 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Update an invoice",
|
||||
"operationId": "Invoices_UpdateInvoice",
|
||||
"description": "Adds the customer's email to the invoice if it has not been set already.",
|
||||
"operationId": "Invoices_AddCustomerEmail",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The updated invoice",
|
||||
@ -272,11 +274,147 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateInvoiceRequest"
|
||||
"$ref": "#/components/schemas/AddCustomerEmailRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [
|
||||
"btcpay.store.cancreateinvoice"
|
||||
],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/stores/{storeId}/invoices/{invoiceId}/status": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Invoices"
|
||||
],
|
||||
"summary": "Mark invoice status",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "storeId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The store to query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "invoiceId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The invoice to update",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Mark an invoice as invalid or completed.",
|
||||
"operationId": "Invoices_MarkInvoiceStatus",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The updated invoice",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/InvoiceData"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "A list of errors that occurred when updating the invoice",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ValidationProblemDetails"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "If you are authenticated but forbidden to update the invoice"
|
||||
}
|
||||
},
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MarkInvoiceStatusRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [
|
||||
"btcpay.store.canmodifystoresettings"
|
||||
],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/stores/{storeId}/invoices/{invoiceId}/unarchive": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Invoices"
|
||||
],
|
||||
"summary": "Unarchive invoice",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "storeId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The store to query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "invoiceId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The invoice to update",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Unarchive an invoice",
|
||||
"operationId": "Invoices_UnarchiveInvoice",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The unarchived invoice",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/InvoiceData"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "A list of errors that occurred when updating the invoice",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ValidationProblemDetails"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "If you are authenticated but forbidden to update the invoice"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [
|
||||
@ -296,27 +434,28 @@
|
||||
"$ref": "#/components/schemas/InvoiceData"
|
||||
}
|
||||
},
|
||||
"UpdateInvoiceRequest": {
|
||||
"MarkInvoiceStatusRequest": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean",
|
||||
"nullable": true,
|
||||
"description": "Unarchive an invoice. You can only archive from the HTTP DELETE endpoint."
|
||||
},
|
||||
"status": {
|
||||
"nullable": true,
|
||||
"nullable": false,
|
||||
"description": "Mark an invoice as completed or invalid.",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/InvoiceStatusMark"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"AddCustomerEmailRequest": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"nullable": false,
|
||||
"description": "Sets the customer email, if it was not set before."
|
||||
}
|
||||
}
|
||||
@ -353,9 +492,9 @@
|
||||
"Confirmed"
|
||||
]
|
||||
},
|
||||
"InvoiceExceptionStatus": {
|
||||
"InvoiceAdditionalStatus": {
|
||||
"type": "string",
|
||||
"description": "",
|
||||
"description": "An additional status that describes why an invoice is in its current status.",
|
||||
"x-enumNames": [
|
||||
"None",
|
||||
"PaidLate",
|
||||
@ -391,8 +530,8 @@
|
||||
"$ref": "#/components/schemas/InvoiceStatus",
|
||||
"description": "The status of the invoice"
|
||||
},
|
||||
"exceptionStatus": {
|
||||
"$ref": "#/components/schemas/InvoiceExceptionStatus",
|
||||
"additionalStatus": {
|
||||
"$ref": "#/components/schemas/InvoiceAdditionalStatus",
|
||||
"description": "a secondary status of the invoice"
|
||||
},
|
||||
"paymentMethodData": {
|
||||
|
Loading…
Reference in New Issue
Block a user