GreenField: Update invoice metadata (#2095)

* GreenField: Update invoice metadata

* add swagger

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
This commit is contained in:
Andrew Camilleri 2020-12-12 07:15:34 +01:00 committed by GitHub
parent b1e9c005b7
commit 034b732e7c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 156 additions and 5 deletions

View file

@ -54,6 +54,17 @@ namespace BTCPayServer.Client
return await HandleResponse<InvoiceData>(response);
}
public virtual async Task<InvoiceData> UpdateInvoice(string storeId, string invoiceId,
UpdateInvoiceRequest 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);
return await HandleResponse<InvoiceData>(response);
}
public virtual async Task<InvoiceData> MarkInvoiceStatus(string storeId, string invoiceId,
MarkInvoiceStatusRequest request, CancellationToken token = default)
{

View file

@ -0,0 +1,9 @@
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models
{
public class UpdateInvoiceRequest
{
public JObject Metadata { get; set; }
}
}

View file

@ -978,7 +978,22 @@ namespace BTCPayServer.Tests
});
});
await AssertHttpError(403, async () =>
{
await viewOnly.UpdateInvoice(user.StoreId, newInvoice.Id,
new UpdateInvoiceRequest()
{
Metadata = JObject.Parse("{\"itemCode\": \"updated\", newstuff: [1,2,3,4,5]}")
});
});
invoice = await client.UpdateInvoice(user.StoreId, newInvoice.Id,
new UpdateInvoiceRequest()
{
Metadata = JObject.Parse("{\"itemCode\": \"updated\", newstuff: [1,2,3,4,5]}")
});
Assert.Equal("updated",invoice.Metadata["itemCode"].Value<string>());
Assert.Equal(15,((JArray) invoice.Metadata["newstuff"]).Values<int>().Sum());
//archive
await AssertHttpError(403, async () =>
{

View file

@ -4,21 +4,16 @@ using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Payments;
using BTCPayServer.Security;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Validation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using NBitcoin;
using NBitpayClient;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using CreateInvoiceRequest = BTCPayServer.Client.Models.CreateInvoiceRequest;
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
@ -100,6 +95,26 @@ namespace BTCPayServer.Controllers.GreenField
return Ok();
}
[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)
{
var store = HttpContext.GetStoreData();
if (store == null)
{
return NotFound();
}
var result = await _invoiceRepository.UpdateInvoiceMetadata(invoiceId, storeId, request.Metadata);
if (result != null)
{
return Ok(ToModel(result));
}
return NotFound();
}
[Authorize(Policy = Policies.CanCreateInvoice,
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpPost("~/api/v1/stores/{storeId}/invoices")]

View file

@ -441,6 +441,22 @@ retry:
await context.SaveChangesAsync().ConfigureAwait(false);
}
}
public async Task<InvoiceEntity> UpdateInvoiceMetadata(string invoiceId, string storeId, JObject metadata)
{
using (var context = _ContextFactory.CreateContext())
{
var invoiceData = await GetInvoiceRaw(invoiceId);
if (invoiceData == null || (storeId != null &&
!invoiceData.StoreDataId.Equals(storeId,
StringComparison.InvariantCultureIgnoreCase)))
return null;
var blob = invoiceData.GetBlob(_Networks);
blob.Metadata = InvoiceMetadata.FromJObject(metadata);
invoiceData.Blob = ToBytes(blob);
await context.SaveChangesAsync().ConfigureAwait(false);
return ToEntity(invoiceData);
}
}
public async Task<bool> MarkInvoiceStatus(string invoiceId, InvoiceStatus status)
{
using (var context = _ContextFactory.CreateContext())

View file

@ -214,6 +214,80 @@
"Basic": []
}
]
},
"put": {
"tags": [
"Invoices"
],
"summary": "Update invoice",
"description": "Updates the specified invoice.",
"operationId": "Invoices_UpdateInvoice",
"parameters": [
{
"name": "storeId",
"in": "path",
"required": true,
"description": "The store the invoice belongs to",
"schema": {
"type": "string"
}
},
{
"name": "invoiceId",
"in": "path",
"required": true,
"description": "The invoice to update",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "The invoice that has been updated",
"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 specified invoice"
},
"404": {
"description": "The key is not found for this invoice"
}
},
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateInvoiceRequest"
}
}
}
},
"security": [
{
"API Key": [
"btcpay.store.canmodifystoresettings"
],
"Basic": []
}
]
}
},
"/api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods": {
@ -628,6 +702,17 @@
}
}
},
"UpdateInvoiceRequest": {
"type": "object",
"additionalProperties": false,
"properties": {
"metadata": {
"type": "object",
"nullable": true,
"description": "Additional information around the invoice that can be supplied."
}
}
},
"CheckoutOptions": {
"type": "object",
"additionalProperties": false,