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>
91 lines
2.5 KiB
Markdown
91 lines
2.5 KiB
Markdown
# 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`). BTCPay’s 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 |
|