Webhooks
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
Setting up a webhook is a crucial step in receiving real-time updates for events that occur in your Telegram bot. To set up a webhook, you'll need to provide an endpoint URL to your own website or server where updates can be sent. There are two ways to do this: you can use the webhook API method, or you can register it directly from within your bot (follow the instructions below).
Once your webhook is set up, you'll start receiving real-time updates for events such as new payments or terminated memberships in your bot. Stay up-to-date with your bot's activity by registering a webhook today!
Setting Up a Webhook in Your Telegram Bot
To set up a webhook in your Telegram bot, follow these steps:
To set up a webhook from within your Telegram bot, open your bot's chat window and type /settings
to access the settings menu. From there, select 'Developers'.
Next, select 'Add Webhook' and enter the URL endpoint where you want to receive updates.
Make sure to enter a URL starting with 'https://' and ensure that the URL responds with an HTTP status code of 200 in order for it to be valid.
Congratulations! You have successfully added your webhook and TGmembership will start sending updates to the URL you provided. The bot will now generate a 'Webhook Secret' for you, which you will need to use to verify the signatures of the updates you receive. Make sure to keep this secret key safe and secure!
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.
Key | Description |
---|---|
event | A string representing the type of event that triggered the webhook, such as "order_completed" or "membership_terminated". |
debug_id | A string identifier for the webhook event, which is not guaranteed to be unique and is used solely for debugging purposes. If you have any questions or issues related to an event, please provide this ID when contacting TGmembership support team. |
data | An object containing data related to the event that triggered the webhook. The structure and contents of this object will vary depending on the type of event. |
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
- JavaScript
- PHP
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))
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
$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.
Header | Description |
---|---|
TGmembership-NONCE | This header contains a random string that is generated for each request. |
TGmembership-SIGNATURE | This header contains a cryptographic signature that is generated based on the request payload and a secret key shared between TGmembership and your server. You can use this signature to verify that the request came from TGmembership and has not been modified in transit. |
TGmembership-DELIVERY-ATTEMPT | This header contains a number that indicates how many times TGmembership has attempted to deliver this event to your server. In case of delivery failures, TGmembership will try up to 7 times to deliver the event before giving up. The header value will be incremented for each attempt. |
Webhook Events
Event: order_completed
This event is triggered when an order has been successfully completed and payment has been received.
Key | Description |
---|---|
amount | A string representing the amount of payment made. |
currency | A string representing the currency code for the payment. |
is_donation | A boolean indicating whether the payment was a donation or not. |
is_renewal | A boolean indicating whether the payment was a renewal or not. |
member_id | An integer representing the Telegram user ID of the member. |
member_name | An integer representing the Telegram display name of the member. |
member_email | A string representing the email address of the member, if available. |
membership_end_date | An integer representing the date and time (in Unix timestamp format) of the expected end date for the membership. |
order_date | An integer representing the date and time (in Unix timestamp format) when the payment was made. |
order_key | A string representing a unique identifier for the payment order. |
payment_method | A lower-cased string representing the payment method used (e.g., stripe, paypal, etc.). |
plan_id | An integer representing the plan ID associated with the payment. |
project_id | An integer representing the project ID associated with the payment. |
referred_by | An integer representing the Telegram user ID of the member who referred the paying member. |
subscription_id | A string representing a unique identifier for the subscription. |
{
"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.
Key | Description |
---|---|
manually_terminated | A boolean indicating whether the membership was manually terminated by an admin or not. |
member_id | An integer representing the Telegram user ID of the member. |
termination_date | Unix timestamp representing the date and time when the termination occurred. |
plan_id | An integer representing the plan the member was subscribed to before termination. |
project_id | An integer representing the ID of the project the user was associated with. |
{
"event": "membership_terminated",
"debug_id": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN",
"data": {
"manually_terminated": true,
"member_id": 1111111111,
"termination_date": 1702276627,
"plan_id": 1,
"project_id": 1
}
}