mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Add callback doc
This commit is contained in:
@@ -54,12 +54,12 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<string>(response);
|
||||
}
|
||||
|
||||
public async Task<JObject> GetWebhookDeliveryRequest(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
|
||||
public async Task<WebhookEvent> GetWebhookDeliveryRequest(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}/request"), token);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
return null;
|
||||
return await HandleResponse<JObject>(response);
|
||||
return await HandleResponse<WebhookEvent>(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,19 @@ namespace BTCPayServer.Client.Models
|
||||
DefaultSerializerSettings.Formatting = Formatting.None;
|
||||
}
|
||||
public string DeliveryId { get; set; }
|
||||
public string WebhookId { get; set; }
|
||||
public string OrignalDeliveryId { get; set; }
|
||||
public bool IsRedelivery { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public WebhookEventType Type { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, JToken> AdditionalData { get; set; }
|
||||
public T ReadAs<T>()
|
||||
{
|
||||
var str = JsonConvert.SerializeObject(this, DefaultSerializerSettings);
|
||||
return JsonConvert.DeserializeObject<T>(str, DefaultSerializerSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,6 @@ namespace BTCPayServer.Client.Models
|
||||
public string StoreId { get; set; }
|
||||
[JsonProperty(Order = 2)]
|
||||
public string InvoiceId { get; set; }
|
||||
|
||||
public T ReadAs<T>()
|
||||
{
|
||||
var str = JsonConvert.SerializeObject(this, DefaultSerializerSettings);
|
||||
return JsonConvert.DeserializeObject<T>(str, DefaultSerializerSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public class WebhookInvoiceConfirmedEvent : WebhookInvoiceEvent
|
||||
|
||||
@@ -16,6 +16,7 @@ using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NBitcoin;
|
||||
using NBitcoin.OpenAsset;
|
||||
using NBitpayClient;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -670,6 +671,9 @@ namespace BTCPayServer.Tests
|
||||
var newDelivery = await clientProfile.GetWebhookDelivery(user.StoreId, hook.Id, newDeliveryId);
|
||||
Assert.NotNull(newDelivery);
|
||||
Assert.Equal(404, newDelivery.HttpCode);
|
||||
var req = await clientProfile.GetWebhookDeliveryRequest(user.StoreId, hook.Id, newDeliveryId);
|
||||
Assert.Equal(delivery.Id, req.OrignalDeliveryId);
|
||||
Assert.True(req.IsRedelivery);
|
||||
Assert.Equal(WebhookDeliveryStatus.HttpError, newDelivery.Status);
|
||||
});
|
||||
deliveries = await clientProfile.GetWebhookDeliveries(user.StoreId, hook.Id);
|
||||
@@ -995,12 +999,13 @@ namespace BTCPayServer.Tests
|
||||
if (marked == InvoiceStatus.Invalid)
|
||||
{
|
||||
Assert.Equal(InvoiceStatus.Invalid, result.Status);
|
||||
user.AssertHasWebhookEvent<WebhookInvoiceInvalidEvent>(WebhookEventType.InvoiceInvalid,
|
||||
var evt = user.AssertHasWebhookEvent<WebhookInvoiceInvalidEvent>(WebhookEventType.InvoiceInvalid,
|
||||
o =>
|
||||
{
|
||||
Assert.Equal(inv.Id, o.InvoiceId);
|
||||
Assert.True(o.ManuallyMarked);
|
||||
});
|
||||
Assert.NotNull(await client.GetWebhookDelivery(evt.StoreId, evt.WebhookId, evt.DeliveryId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
@@ -109,7 +109,10 @@ namespace BTCPayServer.HostedServices
|
||||
newDeliveryBlob.Request = oldDeliveryBlob.Request;
|
||||
var webhookEvent = newDeliveryBlob.ReadRequestAs<WebhookEvent>();
|
||||
webhookEvent.DeliveryId = newDelivery.Id;
|
||||
webhookEvent.WebhookId = webhookDelivery.Webhook.Id;
|
||||
// if we redelivered a redelivery, we still want the initial delivery here
|
||||
webhookEvent.OrignalDeliveryId ??= deliveryId;
|
||||
webhookEvent.IsRedelivery = true;
|
||||
newDeliveryBlob.Request = ToBytes(webhookEvent);
|
||||
newDelivery.SetBlob(newDeliveryBlob);
|
||||
return new WebhookDeliveryRequest(webhookDelivery.Webhook.Id, webhookEvent, newDelivery, webhookDelivery.Webhook.GetBlob());
|
||||
@@ -131,7 +134,9 @@ namespace BTCPayServer.HostedServices
|
||||
webhookEvent.InvoiceId = invoiceEvent.InvoiceId;
|
||||
webhookEvent.StoreId = invoiceEvent.Invoice.StoreId;
|
||||
webhookEvent.DeliveryId = delivery.Id;
|
||||
webhookEvent.WebhookId = webhook.Id;
|
||||
webhookEvent.OrignalDeliveryId = delivery.Id;
|
||||
webhookEvent.IsRedelivery = false;
|
||||
webhookEvent.Timestamp = delivery.Timestamp;
|
||||
var context = new WebhookDeliveryRequest(webhook.Id, webhookEvent, delivery, webhookBlob);
|
||||
EnqueueDelivery(context);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@{
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
@@ -23,6 +23,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<redoc spec-url="@Url.ActionLink("Swagger")"></redoc>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.24/bundles/redoc.standalone.js" integrity="sha384-ZO+OTQZMsYIcoraCBa8iJW/5b2O8K1ujHmRfOwSvpVBlHUeKq5t3/kh1p8JQJ99X" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.45/bundles/redoc.standalone.js" integrity="sha384-RC31+q3tyqdcilXYaU++ii/FAByqeZ+sjKUHMJ8hMzIY5k4kzNqi4Ett88EZ/4lq" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -534,7 +534,7 @@
|
||||
"properties": {
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"description": "Must be used by the callback receiver to ensure the delivery comes from BTCPay Server. BTCPay Server includes the `BTCPay-Sig` HTTP header, whose format is `sha256=[HMAC256(secret,bodyBytes)]`. The pattern to authenticate the webhook is similar to [how to secure webhooks in Github](https://developer.github.com/webhooks/securing/) but with sha256 instead of sha1.",
|
||||
"description": "Must be used by the callback receiver to ensure the delivery comes from BTCPay Server. BTCPay Server includes the `BTCPay-Sig` HTTP header, whose format is `sha256=HMAC256(UTF8(webhook's secret), body)`. The pattern to authenticate the webhook is similar to [how to secure webhooks in Github](https://developer.github.com/webhooks/securing/) but with sha256 instead of sha1.",
|
||||
"nullable": false
|
||||
}
|
||||
}
|
||||
@@ -585,6 +585,341 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebhookEvent": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"deliveryId": {
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The delivery id of the webhook"
|
||||
},
|
||||
"webhookId": {
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The id of the webhook"
|
||||
},
|
||||
"originalDeliveryId": {
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "If this delivery is a redelivery, the is the delivery id of the original delivery."
|
||||
},
|
||||
"isRedelivery": {
|
||||
"type": "boolean",
|
||||
"nullable": false,
|
||||
"description": "True if this delivery is a redelivery"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The type of this event, current available are `InvoiceCreated`, `InvoiceReceivedPayment`, `InvoicePaidInFull`, `InvoiceExpired`, `InvoiceConfirmed`, and `InvoiceInvalid`."
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "number",
|
||||
"format": "int64",
|
||||
"description": "The timestamp when this delivery has been created"
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebhookInvoiceEvent": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/WebhookEvent"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"storeId": {
|
||||
"type": "string",
|
||||
"description": "The store id of the invoice's event",
|
||||
"nullable": false
|
||||
},
|
||||
"invoiceId": {
|
||||
"type": "string",
|
||||
"description": "The invoice id of the invoice's event",
|
||||
"nullable": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebhookInvoiceConfirmedEvent": {
|
||||
"description": "Callback sent if the `type` is `InvoiceConfirmed`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/WebhookInvoiceEvent"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"manuallyMarked": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the invoice have been manually marked as confirmed",
|
||||
"nullable": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebhookInvoiceInvalidEvent": {
|
||||
"description": "Callback sent if the `type` is `InvoiceInvalid`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/WebhookInvoiceEvent"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"manuallyMarked": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the invoice have been manually marked as confirmed. If false, this invoice has received payments which could not confirm in time.",
|
||||
"nullable": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebhookInvoicePaidEvent": {
|
||||
"description": "Callback sent if the `type` is `InvoicePaidInFull`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/WebhookInvoiceEvent"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"overPaid": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this invoice has received more money than expected",
|
||||
"nullable": false
|
||||
},
|
||||
"paidAfterExpiration": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this invoice has been paid too late",
|
||||
"nullable": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebhookInvoiceReceivedPaymentEvent": {
|
||||
"description": "Callback sent if the `type` is `InvoiceReceivedPayment`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/WebhookInvoiceEvent"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"afterExpiration": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this payment has been sent after expiration of the invoice",
|
||||
"nullable": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebhookInvoiceExpiredEvent": {
|
||||
"description": "Callback sent if the `type` is `InvoiceExpired`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/WebhookInvoiceEvent"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"partiallyPaid": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the invoice received some payments before being expired.",
|
||||
"nullable": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-webhooks": {
|
||||
"Invoice created": {
|
||||
"post": {
|
||||
"summary": "Invoice created",
|
||||
"description": "A new invoice has been created",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "header",
|
||||
"name": "BTCPay-Sig",
|
||||
"required": true,
|
||||
"description": "The HMAC of the body's byte with the secret's of the webhook. `sha256=HMAC256(UTF8(webhook's secret), body)`",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "sha256=b438519edde5c8144a4f9bcec51a9d346eca6506887c2ceeae1c0092884a97b9"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WebhookInvoiceEvent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Invoice expired": {
|
||||
"post": {
|
||||
"summary": "Invoice expired",
|
||||
"description": "An invoice expired",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "header",
|
||||
"name": "BTCPay-Sig",
|
||||
"required": true,
|
||||
"description": "The HMAC of the body's byte with the secret's of the webhook. `sha256=HMAC256(UTF8(webhook's secret), body)`",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "sha256=b438519edde5c8144a4f9bcec51a9d346eca6506887c2ceeae1c0092884a97b9"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WebhookInvoiceExpiredEvent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Payment received": {
|
||||
"post": {
|
||||
"summary": "Payment received",
|
||||
"description": "An invoice received a payment",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "header",
|
||||
"name": "BTCPay-Sig",
|
||||
"required": true,
|
||||
"description": "The HMAC of the body's byte with the secret's of the webhook. `sha256=HMAC256(UTF8(webhook's secret), body)`",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "sha256=b438519edde5c8144a4f9bcec51a9d346eca6506887c2ceeae1c0092884a97b9"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WebhookInvoiceReceivedPaymentEvent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Invoice paid": {
|
||||
"post": {
|
||||
"summary": "Invoice paid",
|
||||
"description": "An invoice has been fully paid",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "header",
|
||||
"name": "BTCPay-Sig",
|
||||
"required": true,
|
||||
"description": "The HMAC of the body's byte with the secret's of the webhook. `sha256=HMAC256(UTF8(webhook's secret), body)`",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "sha256=b438519edde5c8144a4f9bcec51a9d346eca6506887c2ceeae1c0092884a97b9"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WebhookInvoicePaidEvent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Invoice invalid": {
|
||||
"post": {
|
||||
"summary": "Invoice invalid",
|
||||
"description": "An invoice became invalid",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "header",
|
||||
"name": "BTCPay-Sig",
|
||||
"required": true,
|
||||
"description": "The HMAC of the body's byte with the secret's of the webhook. `sha256=HMAC256(UTF8(webhook's secret), body)`",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "sha256=b438519edde5c8144a4f9bcec51a9d346eca6506887c2ceeae1c0092884a97b9"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WebhookInvoiceInvalidEvent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Invoice confirmed": {
|
||||
"post": {
|
||||
"summary": "Invoice confirmed",
|
||||
"description": "An invoice has been confirmed, order considered settled",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "header",
|
||||
"name": "BTCPay-Sig",
|
||||
"required": true,
|
||||
"description": "The HMAC of the body's byte with the secret's of the webhook. `sha256=HMAC256(UTF8(webhook's secret), body)`",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "sha256=b438519edde5c8144a4f9bcec51a9d346eca6506887c2ceeae1c0092884a97b9"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WebhookInvoiceConfirmedEvent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user