The payment-adapter-webhook module provides a servlet-based webhook handler that dispatches incoming payment notifications to registered handlers via the WebhookHandler SPI.
The webhook service exposes a simple health endpoint for container orchestration:
GET /health→ returns200 OKwith bodyOK.
Docker Compose uses this endpoint for health checks.
POST /webhook/phoenixd processes payment notifications from phoenixd.
| Name | Required | Description |
|---|---|---|
type |
Yes | Expected value payment_received. |
amountSat |
Yes | Payment amount in satoshis. |
paymentHash |
Yes | Lightning payment hash. |
externalId |
Yes | Lightning invoice identifier used to look up the quote. |
The webhook expects parameters in an application/x-www-form-urlencoded payload.
POST /webhook/phoenixd
type=payment_received&amountSat=1000&paymentHash=<hash>&externalId=<invoice>
- Retrieve the quote by
externalIdand ensure it exists and has directionRECEIVE. - Load the payment linked to the quote.
- Confirm
paymentHashandamountSatmatch the stored payment. - Verify the payment state is
PAIDand the webhooktypeispayment_received.
If validation succeeds, the payment is marked CONFIRMED and a 201 Created response is returned. Any failure results in 401 Unauthorized.
POST /webhook/cash processes Nostr kind 5201 CashIntent events from customers.
The webhook expects a JSON body containing the Nostr event:
{
"id": "<event-id>",
"pubkey": "<customer-pubkey>",
"created_at": 1712345650,
"kind": 5201,
"tags": [
["ref", "6f2c1d"]
],
"content": "<NIP-44 encrypted payload>",
"sig": "<signature>",
"decrypted_content": "{\"ref\":\"6f2c1d\",\"from\":\"<customer-pubkey>\",\"proof\":\"4821\",\"ts\":1712345650}"
}The decrypted_content field is optional; if provided (e.g., by a relay proxy), it is used to extract the proof code and customer timestamp.
- Event kind must be
5201(CashIntent). reftag must be present and be a 4–24 character hex string.- Event timestamp must not be more than 300 seconds in the future.
- Event must not have been previously processed (duplicate detection by event ID).
On success, the associated cash invoice transitions to INTENT_RECEIVED and a 201 Created response is returned.
The webhook module uses a Service Provider Interface (SPI) pattern for extensibility. Each payment type registers a WebhookHandler implementation:
| Handler | Payment Type | Endpoint | Module |
|---|---|---|---|
PhoenixWebhookHandler |
phoenixd |
/webhook/phoenixd |
payment-adapter-ln-webhook |
CashWebhookHandler |
cash |
/webhook/cash |
payment-adapter-cash-webhook |
Handlers are discovered via META-INF/services/xyz.tcheeric.payment.adapter.webhook.spi.WebhookHandler.
The MintWebhookForwarder (added in v0.8.0) enables push-based payment notifications to cashu-mint. When a payment is confirmed via any webhook handler, the forwarder sends a PaymentNotification to the mint's webhook endpoint.
- HMAC-SHA256 signature authentication via
X-Webhook-Signatureheader - Retry logic with exponential backoff (configurable attempts and delays)
- Idempotency via
X-Idempotency-Keyheader - Supports both Lightning (
bolt11) and cash payment methods
| Property | Default | Description |
|---|---|---|
mint.webhook.enabled |
true |
Enable/disable forwarding. |
mint.webhook.url |
http://localhost:7777/webhook/payment |
Mint webhook endpoint URL. |
mint.webhook.secret |
(empty) | HMAC secret for signature authentication. |
mint.webhook.timeout-ms |
5000 |
HTTP request timeout in milliseconds. |
mint.webhook.retry.max-attempts |
3 |
Maximum retry attempts on failure. |
mint.webhook.retry.initial-delay-ms |
1000 |
Initial retry delay in milliseconds. |
mint.webhook.retry.multiplier |
2.0 |
Backoff multiplier for retry delay. |
{
"quoteId": "Q123",
"paymentMethod": "bolt11",
"amount": 1000,
"preimage": "<payment-preimage>",
"paidAt": "2026-02-01T12:00:00Z"
}For cash payments, paymentMethod is "cash" and receiptId is included instead of preimage.