Callbacks

Basics

  The callback sends a POST request with data in the request body(data types). A 200 status is expected in response as confirmation of successful callback processing. There is also a time processing limit for the callback, currently set to 2 seconds. If a 200 status is not received within this time, the callback will be marked as unsuccessful and resent.

  Additionally, there is an interval for retrying callback delivery in case of a previous unsuccessful attempt, with a total of 12 attempts. The first attempt is made after 2 seconds, followed by subsequent attempts at intervals of 4 seconds, 8 seconds, and so on, with each attempt occurring after a period of 2 raised to the power of the attempt number.
For example, if it's the 10th attempt, it will occur after 1024 seconds (approximately 17 minutes).

  Each callback in the request contains a header: "kun-signature," which holds the signature of the callback body. The callback signature is generated using the private key issued along with the API key being used.
How to authenticate a callback is described in this section.


Callback types

In this section, the typification of all possible callbacks in the system is presented.

Withdraw

Callback about the status of a money withdrawal from the system.

{
  event: 'Withdraw';
  data: {
    id: string;
    type: 'Withdraw';
    companyId: string;
    paymentCode: string;
    asset: string;
    amount: string; // amount with fee
    fee: string | null;
    processedAmount: string | null; // amount without fee
    status: 'Processed' | 'PartiallyProcessed' | 'Canceled';
    address: string | null;
    memo: string | null;
    txId: string | null;
    callbackId?: string | null;
    createdAt: Date;
    updatedAt: Date;
  };
}

Invoice deposit

Callback about the status of a deposit for an invoice. Only available when choosing transactional callbacks for an invoice (described in the section about invoices).

{
  event: 'InvoiceDeposit';
  data: {
    id: string;
    type: 'InvoiceDeposit';
    companyId: string;
    paymentCode: string;
    asset: string;
    amount: string; // amount without fee
    fee: string | null;
    processedAmount: string | null; // amount with fee
    status: 'Processed' | 'PartiallyProcessed' | 'Canceled';
    address: string | null;
    memo: string | null;
    txId: string | null;
    invoiceExternalOrderId?: string | null;
    invoiceId: string;
    callbackId?: string | null;
    createdAt: Date;
    updatedAt: Date;
  };
}

Invoice

Callback about the status of an invoice.

{
  event: 'Invoice';
  data: {
    id: string;
    companyId: string;
    invoiceAssetCode: string;
    invoiceAmount: string;
    paymentAssetCode: string | null;
    paymentAmount: string | null;
    paymentFee: string | null;
    
    paymentMethod?: {
        code: string;
        network: string | null;
    };
    payment: {
        paidAmount: string;
        paidFee: string;
        leftAmount: string;

        paidAmountInInvoiceAsset: string;
        leftAmountInInvoiceAsset: string;
    };
    transactions: {
      id: string;
      type: 'InvoiceDeposit';
      companyId: string;
      paymentCode: string;
      asset: string;
      amount: string; // amount without fee
      fee: string | null;
      processedAmount: string | null; // amount with fee
      status: 'Processed' | 'PartiallyProcessed' | 'Canceled';
      address: string | null;
      memo: string | null;
      txId: string | null;
      invoiceExternalOrderId?: string | null;
      invoiceId: string;
      callbackId?: string | null;
      createdAt: Date;
      updatedAt: Date;
    }[];
    address: string | null;
    memo: string | null;
    status: 'PAID' | 'PARTIALLY_PAID' | 'SUSPENDED' | 'ARRESTED' | 'TIMEOUT';;
    isPaymentAfterTimeout: boolean; // if payment send to blockchain after expire time
    externalOrderId: string | null;
    productDescription: string | null;
    productCategory: string | null;
    isCreatedByApi: boolean;
    completedAt: Date | null;
    expireAt: Date | null;
    createdAt: Date;
    updatedAt: Date;
  };


Payout

Callback about the status of Payout.

{
  event: 'Payout';
  data: {
    id: string;
    externalId: string |null;
    title: string |null;
    description: string |null;
    status: 'Processing' | 'Processed' | 'Failed';
    completedAt: string |null;
    launchedAt: string |null;
    createdAt: string;
    updatedAt: string;
  };
}

Payout Withdraw

Callback about the status of PayoutWithdraw.

{
  event: 'PayoutWithdraw';
  data: {
    id: string;
    address: string;
    asset: string;
    paymentCode: string;
    companyId: string;
    type: 'PayoutWithdraw';
    status: 'Processed';
    fee: string;
    amount: string;
    processedAmount: string;
    callbackId: string | null;
    createdAt: string;
    updatedAt: string;
    PayoutContractor: {
      name: string;
    };
    Payout: {
      id: string;
      externalId: string | null;
    };
  };
}


Security (signature)

To verify the authenticity of a callback, you need to generate a signature and compare it with the one received in the callback(header: "kun-signature). To generate the signature, convert the callback body to JSON, then generate an HMAC SHA384 hash using the private key provided for the API key. Ensure the hash is converted to a hex string.

Example of signature generation:

const crypto = require('crypto');

// Assuming callbackBody is the JSON representation of the callback body

const callbackBody = {
  // Your callback body properties here
};

// Assuming privateKey is the private key provided for the API key
const privateKey = 'your_private_key';

// Convert the callback body to JSON
const jsonCallbackBody = JSON.stringify(callbackBody);

// Generate HMAC SHA384
const signature = crypto
  .createHmac('sha384', privateKey)
  .update(jsonCallbackBody)
  .digest('hex');

console.log('Generated Signature:', signature);

Make sure to replace 'your_private_key' with the actual private key provided for your API key.