Skip to content
Last updated

Theropay uses webhooks to notify partners whenever a payment status changes.
This allows your system to stay updated without needing to constantly poll the API.


What Webhooks Are Used For

  • Receive payment status updates in real time
  • Keep your system in sync with Theropay
  • Automatically update your internal records
  • Reduce the need for repeated API calls

1. Webhook Setup

Partners configure their webhook inside the Theropay Partner Portal.

When setting up a webhook, you receive:

  • Webhook URL — where we send all events
  • Webhook Secret — used to verify that the request really came from Theropay

2. Webhook Headers

Every webhook request contains two important headers:

Header NamePurpose
X-Theropay-SignatureHMAC SHA-256 signature used to verify the request
X-Theropay-TimestampTimestamp (milliseconds) when the event was sent

You must verify both values before trusting the payload.


3. How Signature Verification Works

To verify the request:

  1. Read the X-Theropay-Timestamp header

  2. Combine it with the raw request body: {timestamp}.{requestBody}

  3. Hash this using HMAC-SHA256 with your webhook secret

  4. Compare your hash with the value in X-Theropay-Signature

  5. If they match → webhook is valid

  6. If not → reject the request


4. Example Verification Code (C#)

[HttpPost("ReceiveWebhook")]
public async Task<IActionResult> ReceiveWebhook()
{
     var requestBody = await new StreamReader(Request.Body).ReadToEndAsync();
 
     var theropaySignature = Request.Headers["X-Theropay-Signature"].FirstOrDefault();
     var theropayTimestamp = Request.Headers["X-Theropay-Timestamp"].FirstOrDefault();
 
     var partnerSecret = "OIviKHaZVXcHg9QB0j8Dzq/1bf4y7CgKmIu9iQktMJ4="; // Base64 from Theropay on webhook creation
 
     var isValid = VerifySignature(
         requestBody,
         theropaySignature,
         theropayTimestamp,
         partnerSecret);
 
     if (!isValid)
     {
         return Unauthorized("Invalid webhook signature.");
     }
 
     // Process webhook event
     var json = JsonSerializer.Deserialize<object>(requestBody);
 
     return Ok();
}
 
 
 
public static bool VerifySignature(string requestBody, string theropaySignatureHeader, string theropayTimestamp, string partnerSecretPlainText)
{
     // remove sha256=
     var expectedSignatureHex = theropaySignatureHeader.StartsWith("sha256=")
         ? theropaySignatureHeader.Substring(7)
         : theropaySignatureHeader;
 
     // DO NOT Base64 decode — match sender logic
     var secretBytes = Encoding.UTF8.GetBytes(partnerSecretPlainText);
 
     var signingString = $"{theropayTimestamp}.{requestBody}";
     var signingBytes = Encoding.UTF8.GetBytes(signingString);
 
     using var hmac = new HMACSHA256(secretBytes);
     var hash = hmac.ComputeHash(signingBytes);
 
     var computedHex = Convert.ToHexString(hash).ToLower();
 
     return SlowEquals(computedHex, expectedSignatureHex);
}
 
 
private static bool SlowEquals(string a, string b)
{
     // Constant-time comparison to avoid timing attacks
     var aBytes = Encoding.UTF8.GetBytes(a);
     var bBytes = Encoding.UTF8.GetBytes(b);
 
     if (aBytes.Length != bBytes.Length)
         return false;
 
     int diff = 0;
     for (int i = 0; i < aBytes.Length; i++)
         diff |= aBytes[i] ^ bBytes[i];
 
     return diff == 0;
}

5. Responding to Webhooks

After you finish processing the webhook, your endpoint must return the correct HTTP status code.

HTTP StatusMeaning
200–299The webhook was received successfully.
4xxYour system could not process the webhook (we may retry in the future).
Other codesTreated as a failed webhook delivery.

Tip: Always return 200 OK after your system successfully handles the webhook.


6. Retry Behavior

Theropay currently does not have a fixed retry mechanism for failed webhook deliveries.
Make sure your webhook endpoint is reliable, stable, and responds quickly to avoid missing events.


7. Event Payloads

Every webhook event includes the essential details you need to update your system:

  • The event type
  • Information about the payment or payee
  • The updated status
  • A timestamp for when the event was created