btcpaymailer/WEBHOOK.md
Erling 6edd208245 Handle BTCPay __test__ webhooks with Postmark test email
Send a bannered test transactional email when BTCPay UI test events
arrive instead of failing on a missing invoice API lookup.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 13:18:21 +02:00

2.0 KiB
Raw Blame History

BTCPay webhook reference

Endpoint

POST https://mailer.nxtgroup.org/btcpay-webhook

No query-string token. BTCPay signs each request with the webhook secret from the store webhook settings.

Authentication

BTCPay sends header BTCPAY-SIG:

sha256=<hmac-sha256-hex>

The HMAC is computed over the raw request body bytes using the webhook Secret shown in BTCPay (same value as BTCPAY_WEBHOOK_SECRET in Portainer).

Disable quickly

  1. BTCPay → Store → Webhooks → delete or disable the webhook.
  2. Portainer → Stacks → btcpaymailer → stop/remove the stack.

Event subscribed

Only InvoiceReceivedPayment is processed. Other events return 200 with "ignored" (silent unless DEBUG=true).

Webhook body (BTCPay → mailer)

{
  "deliveryId": "abc123",
  "webhookId": "wh_xyz",
  "isRedelivery": false,
  "type": "InvoiceReceivedPayment",
  "timestamp": 1717843200,
  "storeId": "your-store-id",
  "invoiceId": "invoice-id-here"
}

The app then fetches the full invoice:

GET {BTCPAY_URL}/api/v1/stores/{storeId}/invoices/{invoiceId}

Send conditions

  1. Valid BTCPAY-SIG signature
  2. type == InvoiceReceivedPayment
  3. BTCPay API returns invoice
  4. status == "New"
  5. 0 < amountPaid < amount
  6. metadata.buyerEmail is set

BTCPay UI test events

Invoice IDs containing __test__ (BTCPays “Send test” button) skip the API fetch and trigger a Postmark test email to WEBHOOK_TEST_EMAIL, or the first BCC_EMAIL address if unset. The email includes a red THIS IS A TEST TRANSACTIONAL EMAIL, PLEASE IGNORE. banner.

Response codes

Code Meaning
401 Missing or invalid BTCPAY-SIG
400 Partial payment but no buyerEmail
500 BTCPay API or Postmark failure
200 Ignored, or email sent

Logging

DEBUG Container logs
false Evaluation + send result for payment events only
true Full payload and every decision step