mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Merge pull request #1901 from btcpayserver/api/invoice-extras
GreenField: Add Invoice Payment methods endpoints
This commit is contained in:
@@ -26,6 +26,13 @@ namespace BTCPayServer.Client
|
|||||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}"), token);
|
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}"), token);
|
||||||
return await HandleResponse<InvoiceData>(response);
|
return await HandleResponse<InvoiceData>(response);
|
||||||
}
|
}
|
||||||
|
public virtual async Task<InvoicePaymentMethodDataModel[]> GetInvoicePaymentMethods(string storeId, string invoiceId,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var response = await _httpClient.SendAsync(
|
||||||
|
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods"), token);
|
||||||
|
return await HandleResponse<InvoicePaymentMethodDataModel[]>(response);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual async Task ArchiveInvoice(string storeId, string invoiceId,
|
public virtual async Task ArchiveInvoice(string storeId, string invoiceId,
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using BTCPayServer.JsonConverters;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
|
|||||||
61
BTCPayServer.Client/Models/InvoicePaymentMethodDataModel.cs
Normal file
61
BTCPayServer.Client/Models/InvoicePaymentMethodDataModel.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using BTCPayServer.JsonConverters;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Client.Models
|
||||||
|
{
|
||||||
|
public class InvoicePaymentMethodDataModel
|
||||||
|
{
|
||||||
|
public string Destination { get; set; }
|
||||||
|
public string PaymentLink { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
|
public decimal Rate { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
|
public decimal PaymentMethodPaid { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
|
public decimal TotalPaid { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
|
public decimal Due { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
|
public decimal Amount { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
|
public decimal NetworkFee { get; set; }
|
||||||
|
|
||||||
|
public List<Payment> Payments { get; set; }
|
||||||
|
public string PaymentMethod { get; set; }
|
||||||
|
|
||||||
|
public class Payment
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||||
|
public DateTime ReceivedDate { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
|
public decimal Fee { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public PaymentStatus Status { get; set; }
|
||||||
|
|
||||||
|
public string Destination { get; set; }
|
||||||
|
|
||||||
|
public enum PaymentStatus
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
AwaitingCompletion,
|
||||||
|
Complete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -945,9 +945,15 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Single(invoices);
|
Assert.Single(invoices);
|
||||||
Assert.Equal(newInvoice.Id, invoices.First().Id);
|
Assert.Equal(newInvoice.Id, invoices.First().Id);
|
||||||
|
|
||||||
//get payment request
|
//get
|
||||||
var invoice = await viewOnly.GetInvoice(user.StoreId, newInvoice.Id);
|
var invoice = await viewOnly.GetInvoice(user.StoreId, newInvoice.Id);
|
||||||
Assert.Equal(newInvoice.Metadata, invoice.Metadata);
|
Assert.Equal(newInvoice.Metadata, invoice.Metadata);
|
||||||
|
var paymentMethods = await viewOnly.GetInvoicePaymentMethods(user.StoreId, newInvoice.Id);
|
||||||
|
Assert.Equal(1, paymentMethods.Length);
|
||||||
|
var paymentMethod = paymentMethods.First();
|
||||||
|
Assert.Equal("BTC", paymentMethod.PaymentMethod);
|
||||||
|
Assert.Equal(0, paymentMethod.Payments.Count);
|
||||||
|
|
||||||
|
|
||||||
//update
|
//update
|
||||||
invoice = await viewOnly.GetInvoice(user.StoreId, newInvoice.Id);
|
invoice = await viewOnly.GetInvoice(user.StoreId, newInvoice.Id);
|
||||||
|
|||||||
@@ -214,7 +214,70 @@ namespace BTCPayServer.Controllers.GreenField
|
|||||||
return await GetInvoice(storeId, invoiceId);
|
return await GetInvoice(storeId, invoiceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = Policies.CanViewInvoices,
|
||||||
|
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
|
[HttpGet("~/api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods")]
|
||||||
|
public async Task<IActionResult> GetInvoicePaymentMethods(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(ToPaymentMethodModels(invoice));
|
||||||
|
}
|
||||||
|
|
||||||
|
private InvoicePaymentMethodDataModel[] ToPaymentMethodModels(InvoiceEntity entity)
|
||||||
|
{
|
||||||
|
return entity.GetPaymentMethods().Select(
|
||||||
|
method =>
|
||||||
|
{
|
||||||
|
var accounting = method.Calculate();
|
||||||
|
var details = method.GetPaymentMethodDetails();
|
||||||
|
var payments = method.ParentEntity.GetPayments().Where(paymentEntity =>
|
||||||
|
paymentEntity.GetPaymentMethodId() == method.GetId());
|
||||||
|
|
||||||
|
return new InvoicePaymentMethodDataModel()
|
||||||
|
{
|
||||||
|
PaymentMethod = method.GetId().ToStringNormalized(),
|
||||||
|
Destination = details.GetPaymentDestination(),
|
||||||
|
Rate = method.Rate,
|
||||||
|
Due = accounting.Due.ToDecimal(MoneyUnit.BTC),
|
||||||
|
TotalPaid = accounting.Paid.ToDecimal(MoneyUnit.BTC),
|
||||||
|
PaymentMethodPaid = accounting.CryptoPaid.ToDecimal(MoneyUnit.BTC),
|
||||||
|
Amount = accounting.Due.ToDecimal(MoneyUnit.BTC),
|
||||||
|
NetworkFee = accounting.NetworkFee.ToDecimal(MoneyUnit.BTC),
|
||||||
|
PaymentLink =
|
||||||
|
method.GetId().PaymentType.GetPaymentLink(method.Network, details, accounting.Due,
|
||||||
|
Request.GetAbsoluteRoot()),
|
||||||
|
Payments = payments.Select(paymentEntity =>
|
||||||
|
{
|
||||||
|
var data = paymentEntity.GetCryptoPaymentData();
|
||||||
|
return new InvoicePaymentMethodDataModel.Payment()
|
||||||
|
{
|
||||||
|
Destination = data.GetDestination(),
|
||||||
|
Id = data.GetPaymentId(),
|
||||||
|
Status = !paymentEntity.Accounted
|
||||||
|
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Invalid
|
||||||
|
: data.PaymentConfirmed(paymentEntity, entity.SpeedPolicy) ||
|
||||||
|
data.PaymentCompleted(paymentEntity)
|
||||||
|
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Complete
|
||||||
|
: InvoicePaymentMethodDataModel.Payment.PaymentStatus.AwaitingCompletion,
|
||||||
|
Fee = paymentEntity.NetworkFee,
|
||||||
|
Value = data.GetValue(),
|
||||||
|
ReceivedDate = paymentEntity.ReceivedTime.DateTime
|
||||||
|
};
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
}).ToArray();
|
||||||
|
}
|
||||||
private InvoiceData ToModel(InvoiceEntity entity)
|
private InvoiceData ToModel(InvoiceEntity entity)
|
||||||
{
|
{
|
||||||
return new InvoiceData()
|
return new InvoiceData()
|
||||||
|
|||||||
@@ -216,6 +216,123 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Invoices"
|
||||||
|
],
|
||||||
|
"summary": "Get invoice payment methods",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "storeId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"description": "The store to fetch",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "invoiceId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"description": "The invoice to fetch",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "View information about the specified invoice's payment methods",
|
||||||
|
"operationId": "Invoices_GetInvoicePaymentMethods",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "specified invoice payment methods data",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"nullable": false,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/InvoicePaymentMethodDataModel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "If you are authenticated but forbidden to view the specified invoie"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "The key is not found for this invoice"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"API Key": [
|
||||||
|
"btcpay.store.canviewinvoices"
|
||||||
|
],
|
||||||
|
"Basic": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"Invoices"
|
||||||
|
],
|
||||||
|
"summary": "Archive invoice",
|
||||||
|
"description": "Archives the specified invoice.",
|
||||||
|
"operationId": "Invoices_ArchiveInvoice",
|
||||||
|
"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 remove",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The invoice has been archived"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "A list of errors that occurred when archiving the invoice",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ValidationProblemDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "If you are authenticated but forbidden to archive the specified invoice"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "The key is not found for this invoice"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"API Key": [
|
||||||
|
"btcpay.store.canmodifystoresettings"
|
||||||
|
],
|
||||||
|
"Basic": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/stores/{storeId}/invoices/{invoiceId}/status": {
|
"/api/v1/stores/{storeId}/invoices/{invoiceId}/status": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -565,6 +682,110 @@
|
|||||||
"LowSpeed",
|
"LowSpeed",
|
||||||
"LowMediumSpeed"
|
"LowMediumSpeed"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"InvoicePaymentMethodDataModel": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"paymentMethod": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The payment method"
|
||||||
|
},
|
||||||
|
"destination": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The destination the payment must be made to"
|
||||||
|
},
|
||||||
|
"paymentLink": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true,
|
||||||
|
"description": "A payment link that helps pay to the payment destination"
|
||||||
|
},
|
||||||
|
"rate": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "decimal",
|
||||||
|
"description": "The rate between this payment method's currency and the invoice currency"
|
||||||
|
},
|
||||||
|
"paymentMethodPaid": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "decimal",
|
||||||
|
"description": "The amount paid by this payment method"
|
||||||
|
},
|
||||||
|
"totalPaid": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "decimal",
|
||||||
|
"description": "The total amount paid by all payment methods to the invoice, converted to this payment method's currency"
|
||||||
|
},
|
||||||
|
"due": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "decimal",
|
||||||
|
"description": "The total amount left to be paid, converted to this payment method's currency"
|
||||||
|
},
|
||||||
|
"amount": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "decimal",
|
||||||
|
"description": "The invoice amount, converted to this payment method's currency"
|
||||||
|
},
|
||||||
|
"networkFee": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "decimal",
|
||||||
|
"description": "The added merchant fee to pay for network costs of this payment method."
|
||||||
|
},
|
||||||
|
"payments": {
|
||||||
|
"type": "array",
|
||||||
|
"nullable": false,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/Payment"
|
||||||
|
},
|
||||||
|
"description": "Payments made with this payment method."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Payment": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A unique identifier for this payment"
|
||||||
|
},
|
||||||
|
"receivedDate": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "int64",
|
||||||
|
"description": "The date the payment was recorded"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "decimal",
|
||||||
|
"description": "The value of the payment"
|
||||||
|
},
|
||||||
|
"fee": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "decimal",
|
||||||
|
"description": "The fee paid for the payment"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"$ref": "#/components/schemas/PaymentStatus",
|
||||||
|
"description": "The status of the payment"
|
||||||
|
},
|
||||||
|
"destination": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The destination the payment was made to"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PaymentStatus": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "",
|
||||||
|
"x-enumNames": [
|
||||||
|
"Invalid",
|
||||||
|
"AwaitingCompletion",
|
||||||
|
"Complete"
|
||||||
|
],
|
||||||
|
"enum": [
|
||||||
|
"Invalid",
|
||||||
|
"AwaitingCompletion",
|
||||||
|
"Complete"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -299,7 +299,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The creation date of the payment request",
|
"description": "The creation date of the payment request",
|
||||||
"nullable": false,
|
"nullable": false,
|
||||||
"format": "date-time"
|
"format": "int64"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -342,7 +342,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The expiry date of the payment request",
|
"description": "The expiry date of the payment request",
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"format": "date-time"
|
"format": "int64"
|
||||||
},
|
},
|
||||||
"embeddedCSS": {
|
"embeddedCSS": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
Reference in New Issue
Block a user