btcpaymailer/WEBHOOK.md
Erling 203bdd3567 Add webhook evaluation logging with optional DEBUG mode
Log partial-payment decisions and email results to stdout for Portainer
logs; DEBUG enables full webhook payload tracing. Document BTCPay payload
and shutdown steps in WEBHOOK.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 12:05:38 +02:00

91 lines
2.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# BTCPay webhook reference
## Endpoint
```
POST https://mailer.nxtgroup.org/btcpay-webhook?token=YOUR_WEBHOOK_SECRET
```
Auth is the `token` query parameter (must match `WEBHOOK_SECRET`). BTCPays optional webhook signing secret is not used by this app.
## Disable quickly
1. **BTCPay** → Store → Settings → **Webhooks** → delete or disable the webhook.
2. **Portainer** → Stacks → `btcpaymailer` → stop/remove the stack.
No emails are sent while the webhook is disabled or the stack is down.
## Event subscribed
Only **`InvoiceReceivedPayment`** is processed. All other event types return `200` with `"ignored"` and are silent unless `DEBUG=true`.
## Webhook body (BTCPay → mailer)
Typical payload:
```json
{
"deliveryId": "abc123",
"webhookId": "wh_xyz",
"originalDeliveryId": "abc123",
"isRedelivery": false,
"type": "InvoiceReceivedPayment",
"timestamp": 1717843200,
"storeId": "your-store-id",
"invoiceId": "invoice-id-here"
}
```
Fields used by this app from the webhook:
| Field | Use |
|-------|-----|
| `type` | Must be `InvoiceReceivedPayment` |
| `storeId` | Fetch invoice from BTCPay API |
| `invoiceId` | Fetch invoice from BTCPay API |
The webhook does **not** include full payment amounts or buyer email. The app calls:
```
GET {BTCPAY_URL}/api/v1/stores/{storeId}/invoices/{invoiceId}
```
## Invoice fields used (BTCPay API response)
| Field | Use |
|-------|-----|
| `amount` | Invoice total |
| `amountPaid` | Paid so far |
| `currency` | e.g. `USD`, `BTC` |
| `status` | Must be `New` for partial-payment email |
| `metadata.buyerEmail` | Recipient (required to send) |
| `metadata.orderId` | Shown in email subject/body |
## Send conditions (all must pass)
1. `type == InvoiceReceivedPayment`
2. BTCPay API returns invoice successfully
3. `status == "New"`
4. `0 < amountPaid < amount` (partial payment, not zero or full)
5. `metadata.buyerEmail` is set
Otherwise the handler returns `200` with `"ignored"` or `400` if buyer email is missing on a qualifying partial payment.
## Logging
| `DEBUG` | What appears in container logs |
|---------|------------------------------|
| `false` (default) | Evaluation + send result for `InvoiceReceivedPayment` only |
| `true` | Every webhook, full payload, each decision step |
View logs in **Portainer → Containers → btcpay-mailer → Logs**.
## Response codes
| Code | Meaning |
|------|---------|
| `401` | Wrong or missing `token` |
| `400` | Partial payment but no `buyerEmail` |
| `500` | BTCPay API or Postmark failure |
| `200` | Ignored, or email sent successfully |