Webhook Testing
Test your webhook handler during development to make sure it correctly processes payment events, verifies signatures, and handles edge cases before going live.
Local development with ngrok
Your webhook endpoint must be publicly accessible for Miracle to deliver events. During local development, use a tunneling tool like ngrok to expose your local server:
-
Install ngrok:
# macOS brew install ngrok # or via npm npm install -g ngrok -
Start your local server on the port it listens on (e.g., port 3000).
-
Start ngrok to create a public tunnel:
ngrok http 3000ngrok outputs a public URL like
https://a1b2c3d4.ngrok-free.app. -
Register the ngrok URL as your webhook endpoint in the Miracle Portal:
- Go to Settings > Webhooks.
- Click Add Endpoint.
- Enter your ngrok URL followed by your webhook path, e.g.,
https://a1b2c3d4.ngrok-free.app/webhooks/miracle. - Copy the signing secret (
whsec_...) for signature verification.
-
Run a test payment using a test card — the webhook event will be delivered to your local server through the ngrok tunnel.
ngrok URLs change every time you restart the tunnel (on the free plan). Update your webhook endpoint in the Portal when the URL changes.
Test webhooks from the Portal
You can send test webhook events directly from the Miracle Portal without creating a real transaction:
- Go to Settings > Webhooks in the Miracle Portal.
- Select the endpoint you want to test.
- Click Send Test Event.
- Choose the event type to simulate (e.g.,
payment.succeeded,payment.failed,refund.created). - The Portal sends a properly signed event to your endpoint.
This is useful for testing your handler's response to specific event types without going through the full payment flow.
You can also replay previous deliveries from the delivery log. Select any past delivery and click Replay to re-send the exact same event payload to your endpoint. This is useful for debugging failed deliveries without recreating the original transaction.
Verifying webhook delivery
The Miracle Portal provides a delivery log for every webhook endpoint:
- Go to Settings > Webhooks in the Portal.
- Select your endpoint.
- Open the Delivery Log tab.
Each delivery entry shows:
| Field | Description |
|---|---|
| Event type | The event that triggered the delivery (e.g., payment.succeeded) |
| Status | Success or failed |
| Response code | The HTTP status code your endpoint returned |
| Timestamp | When the delivery was attempted |
| Response body | The body your endpoint returned (for debugging) |
| Error details | If delivery failed — timeout, connection refused, non-2xx response |
Failed deliveries are retried automatically with exponential backoff. The log shows each retry attempt.
End-to-end testing checklist
Use this checklist to verify your webhook integration before going live:
- Successful payment (
payment.succeeded) — does your server fulfill the order? - Failed payment (
payment.failed) — does your server notify the customer? - 3DS required (
payment.requires_action) — does your server handle the redirect flow? - Refund (
refund.succeeded) — does your server update the order status? - Signature verification — does your server reject events with invalid signatures?
- Duplicate events — does your server handle receiving the same event twice?
- Unknown event types — does your server return
200 OKfor events it does not handle?
Troubleshooting
Webhook not received?
- Check that your endpoint URL is publicly accessible. Local URLs (
localhost,127.0.0.1) cannot receive webhooks — use ngrok or a similar tunnel. - Verify the endpoint is registered in the Portal and the URL is correct.
- Check the Delivery Log in the Portal for error details.
Getting 4xx errors?
400— check your signature verification code. Make sure you are using the raw request body (not a parsed JSON object) for signature computation.401/403— make sure your endpoint does not require authentication that blocks Miracle's requests.
Timeout errors?
- Your endpoint must respond within 10 seconds. Process webhook events asynchronously — acknowledge with
200 OKimmediately, then handle the business logic in a background job. - If your handler is slow, queue the event and return
200right away.
Duplicate events?
- Miracle guarantees at-least-once delivery. Use
event.idto deduplicate. If you have already processed an event with that ID, return200 OKand skip processing.
Related
- Sandbox — how the sandbox environment works
- Test Cards — magic card numbers for testing
- Handle Webhooks — webhook setup and signature verification
- Event Types — complete list of webhook events