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

# Webhook

> Receive POST requests to your URL automatically when an event occurs.

Merchants can configure webhook endpoints through their dashboard to receive notifications when specific events occur. When configured events happen, Payvra will send notifications in JSON format with all required information about the transaction, such as the amount and status.

### Setting Up Webhooks

1. Navigate to the Webhooks section in your Payvra dashboard
2. Click "Add Webhook Endpoint"
3. Enter your endpoint URL where you want to receive notifications
4. Select the events you want to subscribe to (e.g., PAYMENT\_CREATED, PAYMENT\_COMPLETED)
5. Save the webhook configuration to receive your unique Webhook Secret Key

Each webhook endpoint can be configured independently with its own secret key and event subscriptions. You can add multiple endpoints and manage them through the dashboard.

The webhook endpoint must return an HTTP Response 200 with content: ok for Payvra API to confirm the callback as successful. The system will try to deliver a webhook notification up to 5 times or until a successful delivery occurs, whichever happens first. If the first attempt fails, the second one is triggered after approximately 1 minute. The third one is delayed for 3 more minutes, the fourth for 30 minutes, and the last one for 3 hours.

### Webhook Security

Each webhook endpoint has its own `Webhook Secret Key` that's generated when you create the endpoint. This key should be used to validate incoming webhooks using HMAC(sha512) signatures. Payvra sends the signature in the `HMAC` HTTP header of each request.

### Validating Callbacks

Merchants must validate the signature of callbacks coming from Payvra using their `Webhook Secret Key` created when the webhook was set up in the dashboard. Payvra uses your `Webhook Secret Key` as the HMAC shared secret key to generate an HMAC(sha512) signature of the raw POST data. The HMAC signature is sent as an HTTP header called `HMAC`. The body of the request contains the callback data in JSON format, similar to the Payment Information API response body.

### Testing Webhook

For payment callback handling, you can use tools like requestcatcher.com to create a test `endpoint URL` and inspect the callback data sent from Payvra. Note that Payvra payment callbacks will not be sent to private networks (e.g., localhost).

For local debugging, you can use services like [https://ngrok.io](https://ngrok.io) to expose your local server to the internet and receive webhook callbacks.

**To utilize the payment webhook, please follow these steps:**

1. Create a web server and define an endpoint to handle POST requests.

   * Ensure that the firewall software on your server (e.g., Cloudflare) allows incoming requests from Payvra. You may need to whitelist Payvra's IP addresses on your side. Please reach out to [support@payvra.com](mailto:support@payvra.com) to obtain the list of IP addresses.

2. Configure your server to receive POST requests at the specified endpoint URL.

   * The POST request body will contain the necessary parameters sent by Payvra in JSON format.

3. Validate the HMAC signature of the request to ensure the authenticity of the callback.

   * Use your `MERCHANT_API_KEY` to calculate the HMAC signature and compare it with the received HMAC header.

4. Process the callback data accordingly based on the status and other parameters provided.

### Sample Payment IPN data

In this section, we will provide examples of the data Payvra send to your system at various stages of the payment process.

<CodeGroup>
  ```json Payment Created theme={null}
  {
    "eventType": "PAYMENT_CREATED",
    "id": "cm2m00tok2221w6pp7mmabhn7",
    "merchantId": "cm1av4q2l60002638vzvdzweu",
    "paymentUrl": "https://payvra.com/payment/cm2m00tok2221w6pp7mmabhn7",
    "status": "PENDING",
    "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    "amount": 100.0,
    "amountCurrency": "USD",
    "rate": 0.014285,
    "payAmount": 1.428571,
    "cryptoCurrency": {
      "symbol": "LTC"
    },
    "network": {
      "network": "Litecoin"
    },
    "feePaidByPayer": true,
    "underPaidCover": 5,
    "isWhiteLabel": false,
    "returnUrl": "https://your-return-url.com",
    "expiresAt": "2024-10-24T14:57:49.123Z",
    "createdAt": "2024-10-23T14:57:49.124Z"
  }
  ```

  ```json Payment Confirming theme={null}
  {
    "eventType": "PAYMENT_CONFIRMING",
    "id": "cm2m00tok2221w6pp7mmabhn7",
    "merchantId": "cm1av4q2l60002638vzvdzweu",
    "paymentUrl": "https://payvra.com/payment/cm2m00tok2221w6pp7mmabhn7",
    "status": "CONFIRMING",
    "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    "amount": 100.0,
    "amountCurrency": "USD",
    "rate": 0.014285,
    "payAmount": 1.428571,
    "cryptoCurrency": {
      "symbol": "LTC"
    },
    "network": {
      "network": "Litecoin"
    },
    "feePaidByPayer": true,
    "underPaidCover": 5,
    "isWhiteLabel": false,
    "returnUrl": "https://your-return-url.com",
    "expiresAt": "2024-10-24T14:57:49.123Z",
    "createdAt": "2024-10-23T14:57:49.124Z"
  }
  ```

  ```json Payment Completed theme={null}
  {
    "eventType": "PAYMENT_COMPLETED",
    "id": "cm2m00tok2221w6pp7mmabhn7",
    "merchantId": "cm1av4q2l60002638vzvdzweu",
    "paymentUrl": "https://payvra.com/payment/cm2m00tok2221w6pp7mmabhn7",
    "status": "PAID",
    "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    "txHash": "2b3a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b",
    "amount": 100.0,
    "amountCurrency": "USD",
    "rate": 0.014285,
    "payAmount": 1.428571,
    "paidAmount": 1.428571,
    "cryptoCurrency": {
      "symbol": "LTC"
    },
    "network": {
      "network": "Litecoin"
    },
    "feePaidByPayer": true,
    "underPaidCover": 5,
    "isWhiteLabel": false,
    "returnUrl": "https://your-return-url.com",
    "paidAt": "2024-10-24T14:57:49.123Z",
    "expiresAt": "2024-10-24T14:57:49.123Z",
    "createdAt": "2024-10-23T14:57:49.124Z"
  }
  ```

  ```json Payment Expired theme={null}
  {
    "eventType": "PAYMENT_EXPIRED",
    "id": "cm2m00tok2221w6pp7mmabhn7",
    "merchantId": "cm1av4q2l60002638vzvdzweu",
    "paymentUrl": "https://payvra.com/payment/cm2m00tok2221w6pp7mmabhn7",
    "status": "EXPIRED",
    "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    "amount": 100.0,
    "amountCurrency": "USD",
    "rate": 0.014285,
    "payAmount": 1.428571,
    "cryptoCurrency": {
      "symbol": "LTC"
    },
    "network": {
      "network": "Litecoin"
    },
    "feePaidByPayer": true,
    "underPaidCover": 5,
    "isWhiteLabel": false,
    "returnUrl": "https://your-return-url.com",
    "expiresAt": "2024-10-24T14:57:49.123Z",
    "createdAt": "2024-10-23T14:57:49.124Z"
  }
  ```

  ```json Payment Failed theme={null}
  {
    "eventType": "PAYMENT_FAILED",
    "id": "cm2m00tok2221w6pp7mmabhn7",
    "merchantId": "cm1av4q2l60002638vzvdzweu",
    "paymentUrl": "https://payvra.com/payment/cm2m00tok2221w6pp7mmabhn7",
    "status": "FAILED",
    "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    "amount": 100.0,
    "amountCurrency": "USD",
    "rate": 0.014285,
    "payAmount": 1.428571,
    "paidAmount": 1.242345,
    "cryptoCurrency": {
      "symbol": "LTC"
    },
    "network": {
      "network": "Litecoin"
    },
    "feePaidByPayer": true,
    "underPaidCover": 5,
    "isWhiteLabel": false,
    "returnUrl": "https://your-return-url.com",
    "paidAt": "2024-10-24T14:57:49.123Z",
    "expiresAt": "2024-10-24T14:57:49.123Z",
    "createdAt": "2024-10-23T14:57:49.124Z"
  }
  ```
</CodeGroup>

### Sample Payout IPN data

In this section, we will provide examples of the data Payvra send to your system at various stages of the payment process.

<CodeGroup>
  ```json Payout Created theme={null}
  {
    "eventType": "PAYOUT_CREATED",
    "id": "cm2m00tok2221w6pp7mmabhn7",
    "wallet": {
      "merchantId": "cm1av4q2l60002638vzvdzweu",
      "currency": {
        "symbol": "USDT"
      }
    },
    "network": {
      "network": "TRC20"
    },
    "status": "PROCESSING",
    "address": "TRx2m00tok2221w6pp7mmabhn7USDT",
    "txHash": null,
    "amount": 1000.0,
    "createdAt": "2024-10-23T14:57:49.124Z"
  }
  ```

  ```json Payout Confirming theme={null}
  {
    "eventType": "PAYOUT_CONFIRMING",
    "id": "cm2m00tok2221w6pp7mmabhn7",
    "wallet": {
      "merchantId": "cm1av4q2l60002638vzvdzweu",
      "currency": {
        "symbol": "USDT"
      }
    },
    "network": {
      "network": "TRC20"
    },
    "status": "CONFIRMING",
    "address": "TRx2m00tok2221w6pp7mmabhn7USDT",
    "txHash": "2b3a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b",
    "amount": 1000.0,
    "createdAt": "2024-10-23T14:57:49.124Z"
  }
  ```

  ```json Payout Completed theme={null}
  {
    "eventType": "PAYOUT_COMPLETED",
    "id": "cm2m00tok2221w6pp7mmabhn7",
    "wallet": {
      "merchantId": "cm1av4q2l60002638vzvdzweu",
      "currency": {
        "symbol": "USDT"
      }
    },
    "network": {
      "network": "TRC20"
    },
    "status": "COMPLETED",
    "address": "TRx2m00tok2221w6pp7mmabhn7USDT",
    "txHash": "2b3a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b",
    "amount": 1000.0,
    "createdAt": "2024-10-23T14:57:49.124Z"
  }
  ```

  ```json Payout Rejected theme={null}
  {
    "eventType": "PAYOUT_REJECTED",
    "id": "cm2m00tok2221w6pp7mmabhn7",
    "wallet": {
      "merchantId": "cm1av4q2l60002638vzvdzweu",
      "currency": {
        "symbol": "USDT"
      }
    },
    "network": {
      "network": "TRC20"
    },
    "status": "REJECTED",
    "address": "TRx2m00tok2221w6pp7mmabhn7USDT",
    "txHash": null,
    "amount": 1000.0,
    "createdAt": "2024-10-23T14:57:49.124Z"
  }
  ```
</CodeGroup>

### Example codes

<CodeGroup>
  ```php PHP theme={null}
  <?php
  // Get the request data
  $postData = file_get_contents('php://input');
  $data = json_decode($postData, true);

  $hmacHeader = $_SERVER['HTTP_HMAC'];
  $calculatedHmac = hash_hmac('sha512', $postData, 'YOUR_WEBHOOK_SECRET_KEY');

  if ($calculatedHmac === $hmacHeader) {
      // HMAC signature is valid
      // Process the callback data
      if ($data['eventType'] === 'PAYMENT_CREATED') {
          echo 'Received payment callback: ' . json_encode($data);
          // Process payment data here
      } elseif ($data['eventType'] === 'PAYMENT_COMPLETED') {
          echo 'Received payment callback: ' . json_encode($data);
          // Process payment data here
      }

      // Return HTTP Response 200 with content "OK"
      http_response_code(200);
      echo 'OK';
  } else {
      // HMAC signature is not valid
      // Handle the error accordingly
      http_response_code(400);
      echo 'Invalid HMAC signature';
  }
  ?>
  ```

  ```javascript Node.js theme={null}
  const http = require("http");
  const crypto = require("crypto");

  const server = http.createServer((req, res) => {
    if (req.url === "/callback" && req.method === "POST") {
      // Validate HMAC signature
      let postData = "";

      req.on("data", (chunk) => {
        postData += chunk;
      });

      req.on("end", () => {
        // Parse the JSON data
        let data = null;
        try {
          data = JSON.parse(postData);
        } catch (error) {
          res.statusCode = 400;
          res.end("Invalid JSON data");
          return;
        }

        const hmacHeader = req.headers["hmac"];
        const calculatedHmac = crypto
          .createHmac("sha512", "YOUR_WEBHOOK_SECRET_KEY")
          .update(postData)
          .digest("hex");

        if (calculatedHmac === hmacHeader) {
          // HMAC signature is valid
          // Process the callback data based on the type
          if (data.type === "payment") {
            console.log("Received payment callback:", data);
            // Process payment data here
          } else if (data.type === "payout") {
            console.log("Received payout callback:", data);
            // Process payout data here
          }

          // Return HTTP Response 200 with content "OK"
          res.statusCode = 200;
          res.end("OK");
        } else {
          // HMAC signature is not valid
          // Handle the error accordingly
          res.statusCode = 400;
          res.end("Invalid HMAC signature");
        }
      });
    } else {
      // Invalid path or method
      res.statusCode = 404;
      res.end("Not Found");
    }
  });

  server.listen(3000, () => {
    console.log("Server listening on port 3000");
  });
  ```

  ```python Python theme={null}
  from flask import Flask, request
  import hmac
  import hashlib
  import json

  app = Flask(__name__)

  @app.route('/callback', methods=['POST'])
  def handle_callback():
      post_data = request.get_data(as_text=True)
      data = json.loads(post_data)

      # Validate HMAC signature
      api_secret_key = 'YOUR_WEBHOOK_SECRET_KEY'
      hmac_header = request.headers.get('HMAC')
      post_data = request.get_data()
      calculated_hmac = hmac.new(api_secret_key.encode(), post_data, hashlib.sha512).hexdigest()

      if calculated_hmac == hmac_header:
          # HMAC signature is valid
          # Process the callback data
          if data['eventType'] == 'PAYMENT_CREATED':
              print('Received payment callback:', data)
              # Process payment data here
          elif data['eventType'] == 'PAYMENT_COMPLETED':
              print('Received payment callback:', data)
              # Process payment data here
              # Return HTTP Response 200 with content "OK"
          return 'OK', 200
      else:
          # HMAC signature is not valid
          # Handle the error accordingly
          return 'Invalid HMAC signature', 400


  if __name__ == '__main__':
      app.run(host='YOUR_HOST_ADDRESS',port=3000)
  ```
</CodeGroup>

Again, please note that these code snippets serve as examples and may require modifications based on your specific implementation and framework.

## Troubleshooting

Here are some common issues you might encounter when implementing webhooks and how to solve them:

<AccordionGroup>
  <Accordion title="Not receiving webhook notifications">
    * Ensure your `endpoint URL` is correctly set and accessible from the
      internet. - Check your server's firewall settings to allow incoming requests
      from Payvra. - Verify that your server is responding with a 200 OK status
      and 'ok' message.
  </Accordion>

  <Accordion title="Invalid HMAC signature">
    * Double-check that you're using the correct `MERCHANT_API_KEY` or
      `PAYOUT_API_KEY`. - Ensure you're calculating the HMAC signature on the raw
      POST data, not the parsed JSON. - Verify that you're using the SHA-512
      algorithm for HMAC calculation.
  </Accordion>

  <Accordion title="Webhook timeouts">
    * Optimize your webhook handling code to process requests quickly. -
      Implement asynchronous processing for time-consuming tasks. - Ensure your
      server can handle concurrent webhook requests.
  </Accordion>

  <Accordion title="Duplicate webhook notifications">
    * Implement idempotency in your webhook handler to safely process duplicate
      notifications. - Store processed webhook IDs to detect and ignore
      duplicates.
  </Accordion>
</AccordionGroup>

If you're still experiencing issues after trying these solutions, please contact our support team at [support@payvra.com](mailto:support@payvra.com) for further assistance.
