> ## Documentation Index
> Fetch the complete documentation index at: https://docs.settlx.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Verify your integration before going live

# Testing

Before going live, test every part of your integration — webhook handling, signature verification, idempotency, and error cases.

***

## Test webhook endpoint

Use the [Test Webhook](/api-reference/webhooks/test) endpoint to send a simulated event to your endpoint immediately, without waiting for a real blockchain transaction:

```bash theme={null}
curl -X POST https://api.settlx.io/api/v1/webhooks/test \
  -H "Authorization: Bearer pk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "webhookUrl": "https://yoursite.com/webhooks/settlx",
    "eventType": "invoice.settled"
  }'
```

The test delivery includes a real `X-Webhook-Signature` header, so your signature verification logic is tested end-to-end.

***

## Local development with ngrok

Expose your local server to receive webhook deliveries during development:

```bash theme={null}
ngrok http 3000
```

Use the ngrok URL as your `webhookUrl` when creating invoices or testing:

```bash theme={null}
curl -X POST https://api.settlx.io/api/v1/webhooks/test \
  -H "Authorization: Bearer pk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "webhookUrl": "https://abc123.ngrok.io/webhooks/settlx",
    "eventType": "invoice.settled"
  }'
```

***

## Test every event type

Run through each event your integration handles:

| Event               | What to verify                                   |
| ------------------- | ------------------------------------------------ |
| `invoice.confirmed` | Payment confirmed on-chain, order status updated |
| `invoice.settled`   | Order is fulfilled, customer is notified         |
| `invoice.overpaid`  | Overpayment is logged, customer is notified      |
| `invoice.underpaid` | Customer receives remaining balance notice       |
| `invoice.expired`   | Order marked expired, inventory released         |

***

## Test the full checkout flow

1. Create an invoice using your API key
2. Open `invoice.paymentUrl` in your browser
3. Select Ethereum + USDT — verify a deposit address is shown
4. Use the Test Webhook endpoint to simulate `invoice.confirmed`, then `invoice.settled`
5. Verify your webhook handler fulfills the order on `invoice.settled`

***

## Checklist before going live

<Steps>
  <Step title="Handle all relevant event types">
    At minimum: `invoice.settled` (fulfill), `invoice.expired` (mark failed), `invoice.underpaid` (notify customer).
  </Step>

  <Step title="Webhook signature verification">
    Set `SETTLX_WEBHOOK_SECRET` in your environment and verify `X-Webhook-Signature` on every incoming request.
  </Step>

  <Step title="Idempotency on eventId">
    Store processed `eventId` values and skip duplicates to prevent double-fulfillment.
  </Step>

  <Step title="Respond within 30 seconds">
    Return `200` immediately and process the event asynchronously. Slow responses trigger retries.
  </Step>

  <Step title="Settlement wallet configured">
    Set your settlement wallet address in the dashboard before going live. Double-check the chain matches your wallet.
  </Step>

  <Step title="Expiry times reviewed">
    Set `expiresInMinutes` appropriate for your use case. Use 60+ minutes for Bitcoin payments.
  </Step>

  <Step title="Rate limit handling">
    Implement retry with exponential backoff for API calls. Use webhooks instead of polling where possible.
  </Step>

  <Step title="Verify live API key is set">
    Confirm `pk_live_` key is active in your production environment variables.
  </Step>
</Steps>

***

## Common mistakes

<AccordionGroup>
  <Accordion title="Fulfilling orders on invoice.confirmed instead of invoice.settled">
    `invoice.confirmed` means on-chain confirmation — but funds are not yet in your wallet. Always fulfill on `invoice.settled`, which fires once settlement is complete and funds have landed.
  </Accordion>

  <Accordion title="Not verifying webhook signatures">
    Without signature verification, anyone can POST a fake payload to your endpoint. Always verify `X-Webhook-Signature` before processing any event.
  </Accordion>

  <Accordion title="Processing duplicate webhooks">
    Webhooks can be delivered more than once during retries. Store processed `eventId` values and skip events you've already handled.
  </Accordion>

  <Accordion title="Not handling invoice.expired">
    If an invoice expires and you don't handle the event, the order stays in a pending state indefinitely. Always handle expiry explicitly.
  </Accordion>

  <Accordion title="Exposing your API key in frontend code">
    Your API key is for server-to-server calls only. Never include it in browser JavaScript, mobile apps, or any client-side code.
  </Accordion>

  <Accordion title="Parsing the webhook body before verifying">
    HMAC signature verification must run on the **raw request body** before JSON parsing. If you parse first, the comparison will fail due to whitespace and key ordering differences.
  </Accordion>
</AccordionGroup>
