Ceffu OpenAPI Documentation Platform
  • ChangeLog
  • Getting Started
    • Welcome to Ceffu OpenAPI
    • Introduction
    • General Info
    • Data Objects
    • Webhook Introduction
    • Use Case
    • Frequently Asked Questions
    • Error Codes
  • System Endpoints
  • Wallet Endpoints
  • Sub-Wallet Endpoints
  • MirrorX Endpoints
  • Webhook
  • Archive
logoPowered by Apifox
  1. Getting Started

Webhook Introduction

Webhook Introduction

Why need Webhook?

At present, our client retrieves the deposit and withdrawal records through polling. However, this approach is not server-friendly as it causes the client to consume significant server resources and is also not sufficiently timely. Webhooks are designed to tackle this issue. A webhook will push the deposit and withdrawal records, but there is no guarantee that the push will occur only once. Moreover, this push is not designed to be idempotent.

How to start with The Webhook?


  1. Webhook Setup Procedure on website

    • Navigate to Entity Management > Webhook Endpoint Management (after API key management) to set up a webhook endpoint

    • Click on the "Add Webhook Endpoint" button.

    • Provide an appropriate name for your webhook endpoint and specify the HTTPS URL.

    • Select the wallets linked to the webhook and indicate the types of events the webhook should monitor.

    • Click on the "Submit" button to create the webhook.

    Note:

    • You can create up to 30 webhooks.
    • When a new webhook is created, it is disabled by default. Enable it using the toggle switch once your service is ready.
    • For each event dispatched from the webhook, an HTTP status code 200 is expected to indicate successful delivery. If no response is received, the system will retry, with increasing delays between attempts: 30s, 120s, 480s, 1920s, and 7680s. If all retries fail, Ceffu will mark the delivery status as 'Failed'.
    • A webhook endpoint will be suspended after 500 failed notifications (this limit resets daily, UTC+0, or each time the webhook is re-enabled). You can manually re-enable it via the user interface to resume message reception.
    • Any response other than HTTP 200, including no response (“null”), is considered a failure.
    • There is a "Test" button on the website. Clicking it sends a POST request with empty data to the webhook endpoint. The test is successful if an HTTP 200 OK response is received within 10 seconds; otherwise, it fails. Failures from these tests will not be counted towards the suspension limit.

  1. Decode the data

    Content type of response is in application/json
    Response Example:

    {
       "entityId": "36981140216868864",
       "webhookId": "1788141873578217474",
       "sign": "gawAT2ndVvqfyGmFmQwjHXzCRorHbcxFmz6uE/SUJHLZjmwlqUZNH2t6WiBdRHEuk9fvEREWQwBl5nN6pzsX4Q==",
       "event": "1",
       "timestamp": 1720606148847,
       "data": {
         "orderViewId": "20400454368144883712",
         "txId": null,
         "transferType": 20,
         "direction": 10,
         "fromAddress": "1544594690749788161",
         "toAddress": "1572553622146764801",
         "network": null,
         "coinSymbol": "TRX",
         "amount": "1.33000000000000000000",
         "feeSymbol": null,
         "feeAmount": null,
         "status": 40,
         "confirmedBlockCount": null,
         "maxConfirmBlock": null,
         "unlockConfirm": null,
         "memo": null,
         "txTime": 1720606148703,
         "walletIdStr": "1572553622146764801"
       }
     }
    

    Response structure:

    Attributes Definition
    entityId Institution Id
    webhookId Webhook Id
    sign Signature of response
    event
    • "1" (DEPOSIT_SUCCESS)
    • "2" (DEPOSIT_FAILED)
    • "3" (WITHDRAWAL_SUCCESS)
    • "4" (WITHDRAWAL_FAILED)
    • "11" (DELEGATION_SUCCESS)
    • "12" (DELEGATION_FAILED)
    • "13" (UNDELEGATION_SUCCESS)
    • "14" (UNDELEGATION_FAILED)
    timestamp Unix time
    data Detail data of corresponding event

    Download Key for checking signature

    Java code example to verify signature:

    // data should be string representation of response data with fields "sign" omitted and keys sorted in ascending order
    public String verify(String data, String sign) throws Exception {
            // key for checking signature
            String publicKey = "";
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(publicKey);
            signature.update(data.getBytes());
            return signature.verify(Base64.getDecoder().decode(sign.getBytes()));
    }
    

    Javascript code example to verify signature:

    const NodeRSA = require("node-rsa");
    
    // key for checking signature
    const signPublicKeyBase64 = "...";
    
    const sortObjectKeys = (obj) => {
      if (typeof obj !== "object" || obj === null) {
        return obj;
      }
    
      if (Array.isArray(obj)) {
        return obj.map(sortObjectKeys);
      }
    
      return Object.keys(obj)
        .sort()
        .reduce((result, key) => {
          result[key] = sortObjectKeys(obj[key]);
          return result;
        }, {});
    };
    const stringify = (data) => JSON.stringify(sortObjectKeys(data));
    
    const verify = (data) => {
      const publicKey = new NodeRSA();
      publicKey.importKey(
        Buffer.from(signPublicKeyBase64, "base64"),
        "pkcs8-public-der"
      );
      const { encoded, sign, ...rest } = data;
      return publicKey.verify(
        Buffer.from(stringify(rest), "utf-8"),
        Buffer.from(sign, "base64")
      );
    };
    
    // webhook response data
    const data = ...;
    console.log("verified:", verify(data));
    

    Python code example to verify signature:

    from cryptography.exceptions import InvalidSignature
    from cryptography.hazmat.primitives import serialization, hashes
    from cryptography.hazmat.primitives.asymmetric import padding
    from cryptography.hazmat.backends import default_backend
    import base64
    import json
    
    # key for checking signature
    signPublicKeyBase64 = "..."
    
    def sort_keys(o):
        if isinstance(o, dict):
            return {k: sort_keys(v) for k, v in sorted(o.items())}
        if isinstance(o, list):
            return [sort_keys(v) for v in o]
        else:
            return o
    
    def stringify(data):
        sorted_data = sort_keys(data)
        return json.dumps(sorted_data, separators=(',', ':'))
    
    def verify(data):
        sign = data.pop('sign', None)
        encoded = data.pop('encoded', None)
        data_string = stringify(data)
        publicKey = serialization.load_der_public_key(
            base64.b64decode(signPublicKeyBase64),
            default_backend()
        )
        try:
            publicKey.verify(
                base64.b64decode(sign),
                data_string.encode(),
                padding.PKCS1v15(),
                hashes.SHA256()
            )
            return True
        except InvalidSignature:
            return False
    
    # webhook response data
    data = "..."
    print('verified:', verify(data))
    
Last modified: 14 days ago