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

# Webhooks

> Technical reference for CeyPay WordPress Plugin webhook integration and signature verification.

Webhooks allow CeyPay to notify your WooCommerce store in real-time when payment events occur. This ensures orders are updated immediately when customers complete their payments.

## Webhook Endpoint

CeyPay sends webhook notifications to your store's WooCommerce API endpoint:

```
https://yoursite.com/?wc-api=WC_Gateway_CeyPay
```

<Note>
  The webhook URL is generated automatically by WooCommerce. You can find the
  exact URL by checking the webhook configuration in your CeyPay dashboard or by
  examining the `webhookUrl` parameter in API requests.
</Note>

## Webhook Events

CeyPay sends webhooks for the following payment status changes:

| Status    | Description                           | WooCommerce Action           |
| --------- | ------------------------------------- | ---------------------------- |
| `SUCCESS` | Payment completed successfully        | Order marked as "Processing" |
| `PAID`    | Payment confirmed (alias for SUCCESS) | Order marked as "Processing" |
| `EXPIRED` | Payment window expired                | Order marked as "Failed"     |
| `FAILED`  | Payment was declined or errored       | Order marked as "Failed"     |

## Webhook Payload

### Request Headers

Each webhook request includes these headers for verification:

| Header                | Description                              |
| --------------------- | ---------------------------------------- |
| `X-Webhook-Signature` | HMAC-SHA256 signature of the payload     |
| `X-Webhook-Timestamp` | Unix timestamp when the webhook was sent |
| `Content-Type`        | Always `application/json`                |

### Request Body

The webhook payload is a JSON object containing payment details:

<ResponseField name="paymentId" type="string" required>
  Unique identifier for the payment transaction. This is stored in the order
  metadata as `_ceypay_transaction_id`.
</ResponseField>

<ResponseField name="transactionId" type="string">
  Alternative field name for the payment ID. The plugin accepts either
  `paymentId` or `transactionId`.
</ResponseField>

<ResponseField name="status" type="string" required>
  Current payment status. One of: `SUCCESS`, `PAID`, `EXPIRED`, `FAILED`.
</ResponseField>

### Example Payload

```json theme={null}
{
  "paymentId": "pay_abc123xyz789",
  "status": "SUCCESS",
  "amount": 100.0,
  "currency": "USD",
  "provider": "BINANCE"
}
```

## Signature Verification

All webhook requests are signed using HMAC-SHA256 to ensure authenticity. The plugin automatically verifies signatures before processing any webhook.

### How Signatures Work

1. CeyPay concatenates the timestamp and JSON payload
2. The combined string is hashed using HMAC-SHA256 with your webhook secret
3. The resulting signature is included in the `X-Webhook-Signature` header

### Verification Process

The plugin verifies signatures using this logic:

```php theme={null}
// Construct signature payload
$signature_payload = $timestamp . $json_body;

// Compute expected signature
$expected_signature = hash_hmac('sha256', $signature_payload, $webhook_secret);

// Compare signatures (timing-safe)
if (hash_equals($expected_signature, $received_signature)) {
    // Signature valid - process webhook
} else {
    // Signature invalid - reject request
}
```

<Warning>
  If signature verification fails, the webhook returns a `401 Unauthorized`
  response and the request is logged. Check your webhook secret configuration if
  you see signature mismatch errors.
</Warning>

### Test Mode Signatures

When Test Mode is enabled, the plugin uses a predefined secret (`mock_secret_key`) for signature verification. This allows the test server to send valid webhooks without configuring your production credentials.

## Order Matching

The plugin matches incoming webhooks to WooCommerce orders using the transaction ID stored in order metadata.

### Lookup Process

1. Extract `paymentId` or `transactionId` from webhook payload
2. Query WooCommerce orders for meta key `_ceypay_transaction_id`
3. If found, update the order status based on payment status
4. If not found, log the error (order may have been deleted)

### Stored Order Metadata

The following metadata is stored on each order:

| Meta Key                 | Description                                        |
| ------------------------ | -------------------------------------------------- |
| `_ceypay_transaction_id` | CeyPay transaction identifier                      |
| `_ceypay_provider`       | Selected payment provider (BYBIT, BINANCE, KUCOIN) |
| `_ceypay_qr_code_url`    | QR code content URL                                |
| `_ceypay_deep_link`      | Deep link URL for mobile wallets                   |

**Available Providers:**

<CardGroup cols={3}>
  <Card>
    <div
      style={{
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    padding: "20px",
  }}
    >
      <img className="block dark:hidden" src="https://mintcdn.com/ceypay/KFIMU-H1dWfr-NU1/images/BybitPay.svg?fit=max&auto=format&n=KFIMU-H1dWfr-NU1&q=85&s=c81f99463ba12a30034bbe4d72f04898" alt="Bybit" style={{ height: "40px" }} width="563" height="121" data-path="images/BybitPay.svg" />

      <img className="hidden dark:block" src="https://mintcdn.com/ceypay/KFIMU-H1dWfr-NU1/images/BybitPay-Dark.svg?fit=max&auto=format&n=KFIMU-H1dWfr-NU1&q=85&s=f90ec6d33cfd43da3b49a805625d3064" alt="Bybit" style={{ height: "40px" }} width="563" height="121" data-path="images/BybitPay-Dark.svg" />
    </div>
  </Card>

  <Card>
    <div
      style={{
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    padding: "20px",
  }}
    >
      <img className="block dark:hidden" src="https://mintcdn.com/ceypay/KFIMU-H1dWfr-NU1/images/BinancePay.svg?fit=max&auto=format&n=KFIMU-H1dWfr-NU1&q=85&s=e1e5e2b01136035288029ffc73024797" alt="Binance Pay" style={{ height: "40px" }} width="577" height="85" data-path="images/BinancePay.svg" />

      <img className="hidden dark:block" src="https://mintcdn.com/ceypay/KFIMU-H1dWfr-NU1/images/BinancePay-Dark.svg?fit=max&auto=format&n=KFIMU-H1dWfr-NU1&q=85&s=103e27848a3df6a49a9a696b951e2bbe" alt="Binance Pay" style={{ height: "40px" }} width="577" height="85" data-path="images/BinancePay-Dark.svg" />
    </div>
  </Card>

  <Card>
    <div
      style={{
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    padding: "20px",
  }}
    >
      <img className="block dark:hidden" src="https://mintcdn.com/ceypay/KFIMU-H1dWfr-NU1/images/Kucoin.svg?fit=max&auto=format&n=KFIMU-H1dWfr-NU1&q=85&s=81ac1c74bacb8348ce37871b984084ba" alt="KuCoin" style={{ height: "40px" }} width="491" height="80" data-path="images/Kucoin.svg" />

      <img className="hidden dark:block" src="https://mintcdn.com/ceypay/KFIMU-H1dWfr-NU1/images/Kucoin-Dark.svg?fit=max&auto=format&n=KFIMU-H1dWfr-NU1&q=85&s=cae73a1ade1d1932fe37358c379d7178" alt="KuCoin" style={{ height: "40px" }} width="491" height="80" data-path="images/Kucoin-Dark.svg" />
    </div>
  </Card>
</CardGroup>

## Handling Webhook Responses

### Success Response

Return HTTP `200 OK` to acknowledge successful webhook processing:

```http theme={null}
HTTP/1.1 200 OK
```

### Error Responses

| Status Code                 | Meaning                       |
| --------------------------- | ----------------------------- |
| `400 Bad Request`           | Invalid or malformed payload  |
| `401 Unauthorized`          | Signature verification failed |
| `405 Method Not Allowed`    | Non-POST request received     |
| `500 Internal Server Error` | Webhook secret not configured |

## Debugging Webhooks

### Enable Logging

In Test Mode, webhook activity is automatically logged to WooCommerce logs. Access logs at:

**WooCommerce > Status > Logs** → Select files prefixed with `ceypay-`

### Log Entries

Typical log entries include:

```
Webhook received: {"paymentId":"pay_123","status":"SUCCESS"}
Signature Check: Received: abc... Computed: abc... Match: YES
Processing webhook for Transaction ID: pay_123, Status: SUCCESS
Order #456 marked as paid.
```

### Common Issues

<AccordionGroup>
  <Accordion title="Signature Mismatch">
    **Cause:** Webhook secret doesn't match between CeyPay dashboard and plugin settings.

    **Solution:** Copy the webhook secret directly from your CeyPay dashboard and paste it into the plugin settings. Avoid adding extra spaces or characters.
  </Accordion>

  <Accordion title="Order Not Found">
    **Cause:** The order was deleted, or the customer abandoned checkout before the QR code was generated.

    **Solution:** This is usually expected behavior for abandoned checkouts. No action required.
  </Accordion>

  <Accordion title="Webhooks Not Received">
    **Cause:** Server firewall blocking incoming requests, or site not accessible from internet.

    **Solution:** Ensure your site has a valid SSL certificate and is publicly accessible. Check server firewall rules.
  </Accordion>
</AccordionGroup>

## Security Best Practices

<CardGroup cols={2}>
  <Card title="Keep Secret Secure" icon="key">
    Never expose your webhook secret in client-side code, version control, or
    public logs.
  </Card>

  <Card title="Use HTTPS" icon="lock">
    Always use HTTPS for your webhook endpoint. HTTP webhooks should be
    rejected.
  </Card>

  <Card title="Verify Signatures" icon="shield-check">
    Always verify webhook signatures. The plugin does this automatically.
  </Card>

  <Card title="Idempotency" icon="rotate">
    Handle duplicate webhooks gracefully. The plugin checks order status before
    updating.
  </Card>
</CardGroup>

## Retry Policy

If your webhook endpoint is unavailable, CeyPay may retry the webhook. The plugin handles this by checking if the order is already processed before making changes.

<Tip>
  The plugin includes fallback payment status polling, so even if a webhook
  fails, payments will still be confirmed when the customer's browser polls for
  status updates.
</Tip>

## Next Steps

<CardGroup cols={2}>
  <Card title="Troubleshooting" icon="wrench" href="/integrations/wordpress/troubleshooting">
    Solutions for common webhook and integration issues.
  </Card>

  <Card title="Configuration" icon="gear" href="/integrations/wordpress/configuration">
    Review all available plugin settings.
  </Card>
</CardGroup>
