Tylt: API Documentation
  • Introduction
    • Introduction
    • Getting Started
    • Generating API Keys
    • Signing API Payloads
    • Important Concepts
    • Merchant Verification
    • Tylt Prime (UPI to Crypto Solution)
      • API Reference
        • Create a Pay-in Instance
        • Create a Pay-out Instance
        • Webhook for Tylt Prime
        • Get Instance Information
        • Get Pay-In Transaction Information
        • Get Pay-Out Transaction Information
    • Tylt Prime (UPI to Crypto Solution - H2H)
      • API Reference ( Pay-In)
        • Create a Pay-in Instance
        • Buyer Confirms Payment
        • Webhook for Tylt Prime
        • Get Instance Details
        • Get Pay-In Transaction Information
        • Get List of Fiat Currency and Supported Payment Methods
        • Get List of Supported Crypto Currency for Settlement
        • Get Conversion Rates
  • Tylt CPG (Crypto Payment Gateway)
    • API Reference
      • Accept Crypto Payments
        • Creating a Pay-in Request
        • Get Pay-In Transaction History
        • Get Pay-In Transaction Information
      • Make Crypto Payouts
        • Creating a Payout Request
        • Get Pay-Out Transaction History
        • Get Pay-Out Transaction Information
      • Supporting API's
        • Get Supported Crypto Currencies List
        • Get Supported Fiat Currencies
        • Get Account Balance / Holdings
        • Get Supported Crypto Networks
        • Supported Base Currency List
      • Webhook
    • Use Cases
      • E-commerce Flow
      • Withdrawal Flow
Powered by GitBook
On this page
  1. Introduction
  2. Tylt Prime (UPI to Crypto Solution - H2H)
  3. API Reference ( Pay-In)

Webhook for Tylt Prime

Overview

Tylt provides a webhook mechanism for merchants to receive real-time updates on the status of their payment instance, whether for pay-ins or for pay-outs. Merchants can specify a callBackUrl in their API requests, and Tylt will send notifications to this URL whenever there is a status change in the transaction.

Setting Up the Webhook

  1. Implement a Callback Endpoint: Merchants must set up an HTTP POST endpoint that can receive JSON payloads. This endpoint should be capable of processing the incoming webhook data and verifying its authenticity using HMAC-SHA256 signature validation.

  2. Insert the Callback URL: While calling the Create Pay-in or Create Pay-out instance API's , insert your endpoint URL in the callBackUrl field. Tylt will send updates to this URL whenever the transaction status changes.

  3. Status Updates: The life cycle of a payment instance is tracked via eventId. Below is the list of possible eventId values and their meanings:

    eventId

    Description

    0

    The instance is created. User is yet to interact with the instance.

    1

    Trade initiated.

    2

    Waiting for the buyer to make and confirm payment via UPI.

    3

    Buyer confirms making payment. Seller is verifying the payment.

    4

    Payment acknowledged and trade completed.

    5

    Payment disputed. Trade moved to dispute.

    6

    Payment acknowledged and trade completed by the system.

    9

    Trade expired as action or payment was not completed prior to the deadline.

Instance Information

The response related to an instance information contains two primary objects:

1. Trade Object

This object contains all the information about the customer buying or selling USDT from the counterparty. It includes fields like:

  • Trade lifecycle details (eventId, deadlines, description).

  • Fiat and cryptocurrency details (currency name, symbol, amount, etc.).

  • Payment method information (e.g., UPI).

2. Transaction Object

This object contains all the information about the financial debit or credit carried out on the merchant's account. It is relevant to merchants for crediting or debiting a consumer for the transaction. The transaction object is updated only when the eventId is 4, representing the completion of the trade

  1. Callback Validation: To ensure the integrity and authenticity of the callback, Tylt signs each callback payload using HMAC-SHA256 with the merchant’s API secret key. This signature is sent in the HTTP header X-TLP-SIGNATURE.

  2. Acknowledge the Callback: Upon receiving the callback, merchants must respond with an HTTP 200 status code and the text "ok" in the response body. This acknowledges the successful receipt of the callback. If the acknowledgment is not received, the webhook will not be retried automatically. Merchants can manually resend webhooks from their Tylt dashboard.

Validating Callbacks

Merchants should validate the HMAC signature included in the X-TLP-SIGNATURE header to ensure the callback is from Tylt and has not been tampered with. The HMAC signature is generated using the raw POST data and the MERCHANT_API_SECRET as the shared key.

Example Web-hook Handling Code

const express = require('express');
const crypto = require('crypto');

const app = express();
const PORT = 3000;
const apiSecretKey = 'YOUR_TLP_API_SECRET_KEY'; // Replace with your actual API secret key

// Middleware to parse incoming JSON requests
app.use(express.json());

// Callback endpoint
app.post('/callback', (req, res) => {
    const data = req.body;

    // Calculate HMAC signature
    const tlpSignature = req.headers['x-tlp-signature'];
    const calculatedHmac = crypto
        .createHmac('sha256', apiSecretKey)
        .update(JSON.stringify(data)) // Use raw body string for HMAC calculation
        .digest('hex');

    if (calculatedHmac === tlpSignature) {
        // Signature is valid
        if (data.accounts.transactionType === 'pay-in') {
            console.log('Received pay-in callback:', data);
            // Process pay-in data here
        } 
        // Return HTTP Response 200 with content "ok"
        res.status(200).send('ok');
    } else {
        // Invalid HMAC signature
        res.status(400).send('Invalid HMAC signature');
    }
});

// Start the server
app.listen(PORT, () => {
    console.log(`Server listening on port ${PORT}`);
});
from flask import Flask, request, jsonify
import hmac
import hashlib
import json


app = Flask(__name__)

# Your TL Pay API Secret Key
TLP_API_SECRET_KEY = 'YOUR_TLP_API_SECRET_KEY'  # Replace with your actual API secret key

@app.route('/callback', methods=['POST'])
def callback():
    # Get the raw request data for HMAC calculation
    raw_data = json.dumps(request.get_json(), separators=(',', ':'), ensure_ascii=False)

    # Parse JSON data from the request
    data = request.get_json()

    # Retrieve the signature from the request headers
    tlp_signature = request.headers.get('X-TLP-SIGNATURE')

    # Calculate the HMAC SHA-256 signature
    calculated_hmac = hmac.new(
        key=TLP_API_SECRET_KEY.encode(),
        msg=raw_data,
        digestmod=hashlib.sha256
    ).hexdigest()

    # Compare the calculated HMAC signature with the one in the request header
    if hmac.compare_digest(calculated_hmac, tlp_signature):
        # Signature is valid
        if data['accounts']['transactionType'] == 'pay-in':
            print('Received pay-in callback:', data)
            # Process pay-in data here
            
        # Return HTTP Response 200 with content "ok"
        return jsonify({"message": "ok"}), 200
    else:
        # Invalid HMAC signature
        return jsonify({"error": "Invalid HMAC signature"}), 400

if __name__ == '__main__':
    app.run(port=3000, debug=True)

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

Example of Web-hook Responses

{
    "data": {
        "trade": {
            "isBuyTrade":1,
            "event": {
                "id": null,
                "deadline": null,
                "description": null
            },
            "createdAt": null,
            "updatedAt": null,
            "fiatCurrency": {
                "name": null,
                "symbol": null
            },
            "priceDetails": {
                "price": null,
                "amount": null,
                "paymentAmount": null
            },
            "paymentMethod": {
                "details": null
            },           },
            "cryptoCurrency": {
                "name": null,
                "symbol": null
            }
        },
        "transaction": {
            "amount": null,
            "status": null,
            "isFinal": null,
            "orderId": null,
            "createdAt": null,
            "isCredited": null,
            "updatedAt": null,
            "merchantOrderId": null
        },
         "accounts": {
            "transactionType": "pay-in",
            "amountPaidInLocalCurrency": 196.35,
            "localCurrency": "INR",
            "conversionRate": 93.5,
            "amountPaidInCryptoCurrency": 2.1,
            "cryptoCurrencySymbol": "USDT",
            "MDR_Rate": 4,
            "merchantAccountCredited": null,
            "merchantAccountDebited": null
        }
        "user": {}
    }
}

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

{
    "data": {
        "trade": {
            "event": {
                "id": 1,
                "deadline": "2025-02-12T05:27:37Z",
                "description": "Trade Initiated. Finding Seller."
            },
            "createdAt": "2025-02-12T05:24:37Z",
            "updatedAt": "2025-02-12T05:24:37Z",
            "fiatCurrency": {
                "name": "Indian Rupee",
                "symbol": "INR"
            },
            "priceDetails": {
                "price": 93.5,
                "amount": 1,
                "paymentAmount": 93.5
            },
            "paymentMethod": {
                "details": null
            },
            "cryptoCurrency": {
                "name": "Tether",
                "symbol": "USDT"
            }
        },
        "transaction": {
            "amount": null,
            "status": null,
            "isFinal": null,
            "orderId": null,
            "createdAt": null,
            "isCredited": null,
            "updatedAt": null,
            "merchantOrderId": null
        },
        "user": {}
    }
}

{
    "data": {
        "trade": {
            "event": {
                "id": 2,
                "deadline": "2025-02-12T05:30:03Z",
                "description": "Seller Found, waiting for Buyer to make and confirm payment."
            },
            "createdAt": "2025-02-12T05:24:37Z",
            "updatedAt": "2025-02-12T05:25:03Z",
            "fiatCurrency": {
                "name": "Indian Rupee",
                "symbol": "INR"
            },
            "priceDetails": {
                "price": 93.5,
                "amount": 1,
                "paymentAmount": 93.5
            },
            "paymentMethod": {
                "details": {
                    "upiAddress": "p2ptrade@upi",
                    "qrString": "upi://pay?pa=p2ptrade@upi&pn=&am=215.04999999999998&cu=INR"
                }
            },
            "cryptoCurrency": {
                "name": "Tether",
                "symbol": "USDT"
            }
        },
        "transaction": {
            "amount": null,
            "status": null,
            "isFinal": null,
            "orderId": null,
            "createdAt": null,
            "isDebited": null,
            "updatedAt": null,
            "merchantOrderId": null
        },
        "user": {}
    }
}

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

{
    "data": {
        "trade": {
            "event": {
                "id": 3,
                "deadline": "2025-02-12T05:30:25Z",
                "description": "Payment Confirmed by Buyer. Seller Verifying Payment."
            },
            "createdAt": "2025-02-12T05:24:37Z",
            "updatedAt": "2025-02-12T05:25:25Z",
            "fiatCurrency": {
                "name": "Indian Rupee",
                "symbol": "INR"
            },
            "priceDetails": {
                "price": 93.5,
                "amount": 1,
                "paymentAmount": 93.5
            },
            "paymentMethod": {
                "details": {
                    "upiAddress": "p2ptrade@upi",
                    "qrString": "upi://pay?pa=p2ptrade@upi&pn=&am=215.04999999999998&cu=INR"
                }
            },
            "cryptoCurrency": {
                "name": "Tether",
                "symbol": "USDT"
            }
        },
        "transaction": {
            "amount": null,
            "status": null,
            "isFinal": null,
            "orderId": null,
            "createdAt": null,
            "isDebited": null,
            "updatedAt": null,
            "merchantOrderId": null
        },
        "user": {}
    }
}

{
    "data": {
        "trade": {
            "event": {
                "id": 4,
                "deadline": "2025-02-12T05:25:45Z",
                "description": "Seller Acknowledges Payment Receipt. Trade Completed."
            },
            "createdAt": "2025-02-12T05:24:37Z",
            "updatedAt": "2025-02-12T05:25:45Z",
            "fiatCurrency": {
                "name": "Indian Rupee",
                "symbol": "INR"
            },
            "priceDetails": {
                "price": 93.5,
                "amount": 1,
                "paymentAmount": 93.5
            },
            "paymentMethod": {
                "details": {
                    "upiAddress": "p2ptrade@upi",
                    "qrString": "upi://pay?pa=p2ptrade@upi&pn=&am=215.04999999999998&cu=INR"
                }
            },
            "cryptoCurrency": {
                "name": "Tether",
                "symbol": "USDT"
            }
        },
        "transaction": {
            "amount": 0.998,
            "status": "Completed",
            "isFinal": 1,
            "orderId": "b61fedfd-e901-11ef-830e-02d8461243e9",
            "createdAt": "2025-02-12T05:25:45Z",
            "updatedAt": "2025-02-12T05:25:45Z",
            "isCredited": 1,
            "merchantOrderId": "b73b73b-87wtbc-q36gbc-331n3"
        },
        "user": {}
    }
}

Important Considerations

  • Security: Always verify the X-TLP-SIGNATURE header to ensure the callback originates from Tylt.

  • Response: Always return an HTTP 200 response with "ok" in the body to acknowledge successful receipt of the webhook.

  • Manual Retry: In case of missed callbacks, use the tylt.monry dashboard to manually resend the webhook.

PreviousBuyer Confirms PaymentNextGet Instance Details

Last updated 2 months ago