# Webhook Management 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 Name | Purpose | | --- | --- | | **X-Theropay-Signature** | HMAC SHA-256 signature used to verify the request | | **X-Theropay-Timestamp** | Timestamp (milliseconds) when the event was sent | You must verify both values before trusting the payload. ## 3. Example Verification Code (C#) ```csharp [HttpPost("ReceiveWebhook")] public async Task 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(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; } ``` ## 4. 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