Webhooks

Learn how to use webhooks to receive real-time updates on important events related to your TGmembership-powered Telegram bot.

TGmembership offers support for webhooks, allowing you to receive real-time updates on crucial events related to your Telegram bot. Webhooks provide push API calls that can notify your application when specific events occur, such as new payments, renewals, cancellations, donations, and other relevant data. By configuring your webhook URL, you can start receiving real-time updates in JSON format and stay informed about critical events.

Setting Up a Webhook

To learn how to generate add a webhook endpoint, please refer to our guide on Setting Up a Webhook.

Payload Schema

The webhook payload for TGmembership has a simple schema with three elements: "event", "debug_id", and "data". The "event" element specifies the type of event that has occurred, while "debug_id" provides an identifier for debugging purposes. The "data" element contains element-specific data related to the event. Each event has its own data structure, which will be detailed separately in the documentation.

Verifying the Signature

To generate the signature for the webhook request, several steps are taken. First, a string is created by concatenating the nonce (provided in the header), timestamp, and the payload data using a period as a delimiter. The resulting string is then passed through a hashing algorithm called sha512 along with a secret key to generate a unique hash. The hash is then transformed to uppercase characters. Finally, the timestamp and the resulting hash are combined into a signature string using the format t=<timestamp>,v1=<hash>. This signature string is then included as a header with the webhook request to authenticate and validate the request.

Python
import re
import hashlib
import hmac

nonce = '53ed4554ef588'
signature = 't=1684096282,v1=F7866D2B2560641C5E33A60485B53CB0848C94BB4B1D727BB60678DDA4000A556E4AAC49354F10E0EFA8708A73BD30E49F8AC1C7451661E11255622131127413'
payload = '{"event":"membership_terminated","debug_id":"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN","data":{"member_id":1111111111}}'
secretKey = 'your_secret_key'

def verifySignature(nonce, signature, payload, secretKey):
    match = re.match(r'^t=(\d+),v1=[A-Z0-9]+$', signature)
    if not match:
        return False
    timestamp = match.group(1)
    expectedSignature = generateSignature(nonce, timestamp, payload, secretKey)
    return hmac.compare_digest(expectedSignature, signature)

def generateSignature(nonce, timestamp, post, secretKey):
    signature_string = f"{nonce}.{timestamp}.{payload}"
    signature = hmac.new(secretKey.encode(), signature_string.encode(), hashlib.sha512).hexdigest().upper()
    return f"t={timestamp},v1={signature}"

print(verifySignature(nonce, signature, payload, secretKey))
Node.js
const crypto = require('crypto');

const nonce = '53ed4554ef588';
const signature = 't=1684096282,v1=F7866D2B2560641C5E33A60485B53CB0848C94BB4B1D727BB60678DDA4000A556E4AAC49354F10E0EFA8708A73BD30E49F8AC1C7451661E11255622131127413';
const payload = '{"event":"membership_terminated","debug_id":"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN","data":{"member_id":1111111111}}';
const secretKey = 'your_secret_key';

function verifySignature(nonce, signature, payload, secretKey) {
    const signatureParts = signature.match(/^t=(\d+),v1=[A-Z0-9]+$/);
    if (!signatureParts)
        return false;
    const timestamp = signatureParts[1];
    return generateSignature(nonce, timestamp, payload, secretKey) === signature;
}

function generateSignature(nonce, timestamp, payload, secretKey) {
    const signatureString = nonce + '.' + timestamp + '.' + payload;
    const signature = crypto
        .createHmac('sha512', secretKey)
        .update(signatureString)
        .digest('hex')
        .toUpperCase();
    return `t=${timestamp},v1=${signature}`;
}

console.log(verifySignature(nonce, signature, payload, secretKey));
PHP
<?php
$nonce = '53ed4554ef588';
$signature = 't=1684096282,v1=F7866D2B2560641C5E33A60485B53CB0848C94BB4B1D727BB60678DDA4000A556E4AAC49354F10E0EFA8708A73BD30E49F8AC1C7451661E11255622131127413';
$payload = '{"event":"membership_terminated","debug_id":"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN","data":{"member_id":1111111111}}';
$secretKey = 'your_secret_key';

function verifySignature($nonce, $signature, $payload, $secretKey) {
    if (!preg_match('/^t=(\d+),v1=[A-Z0-9]+$/', $signature, $signatureParts))
        return false;
    $timestamp = $signatureParts[1];
    return hash_equals(generateSignature($nonce, $timestamp, $payload, $secretKey), $signature);
}

function generateSignature($nonce, $timestamp, $payload, $secretKey) {
    $signature = $nonce . '.' . $timestamp . '.' . $payload;
    $signature = strtoupper(hash_hmac('sha512', $signature, $secretKey));
    $signature = sprintf('t=%d,v1=%s', $timestamp, $signature);
    return $signature;
}

var_dump(verifySignature($nonce, $signature, $payload, $secretKey));

Webhook Delivery

TGmembership sends webhook updates to your endpoint as soon as they happen. Once an update is sent, TGmembership expects a response with http code 200 to confirm successful delivery.

If the first delivery attempt fails, TGmembership will automatically try to redeliver the update up to seven times, using the following schedule:

  • 2nd attempt: approximately 2 minutes after the first attempt

  • 3rd attempt: approximately 20 minutes after the second attempt

  • 4th attempt: approximately 6 hours after the third attempt

  • 5th attempt: approximately 14 hours after the fourth attempt

  • 6th attempt: approximately 30 hours after the fifth attempt

  • 7th attempt: approximately 2 days after the sixth attempt

If after the seventh attempt, the update still cannot be delivered, it will be considered unsuccessful, and TGmembership may delete the webhook endpoint to prevent further delivery attempts.

Request Headers

When a webhook event is sent to your server, it will include three custom headers to help you verify the authenticity of the request and understand its delivery status.

Event: order_completed

This event is triggered when an order has been successfully completed and payment has been received.

{
    "event": "order_completed",
    "debug_id": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN",
    "data": {
        "amount": "10",
        "currency": "EUR",
        "is_donation": false,
        "is_renewal": true,
        "member_id": 1111111111,
        "order_date": 1684080114,
        "order_key": "abcdefghijklmnopqrstuvwxyz",
        "payment_method": "stripe",
        "plan_id": 1,
        "project_id": 1,
        "referred_by": 2222222222,
        "subscription_id": "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    }
}

Note: The elements currency, order_key, plan_id, project_id, is_renewal, referred_by, and subscription_id may not be included in the webhook payload if they are not relevant or their value is null.

Event: membership_terminated

This event is triggered when a membership has been terminated.

{
    "event": "membership_terminated",
    "debug_id": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN",
    "data": {
        "manually_terminated": true,
        "member_id": 1111111111,
        "termination_date": 1702276627,
        "plan_id": 1,
        "project_id": 1
    }
}

Last updated