Validating the Callback Notification

Node.js Example

const { createHash, timingSafeEqual } = require('node:crypto');

const YOUR_SECRET = 'replace-with-your-secret';

const CALLBACK_BODY = {
  signature: 'e1ca4421621c4d795fe3612b1044634595c52ebe1c52225573c310741c9d47b6',
  data: {
    client: {
      name: 'Optional Customer Name',
      email: '[email protected]',
      phone: '+18001234567',
      referenceID: 'order-10001'
    },
    paidAmount: 0.1,
    subTotal: 0.1,
    total: 0.1,
    partialPaymentsEnabled: false,
    currency: 'USDC',
    completed: true,
    description: 'Optional Order Description',
    transactions: [
      {
        tx: '0xd1c7fe2821a9df5240f3d31e570a760322fa979f84c0655a8f9f857023e7064f',
        sender: '0x2F67f1426f33E25A920F0b2139cd460fDfb8997C',
        amount: 0.1,
        cryptoAmount: 100000,
        timestamp: '2024-09-05T21:00:27.000Z',
        network: 'MATIC_POLYGON'
      }
    ],
    createdAt: '2024-09-05T20:59:47.429Z',
    merchant: {
      name: 'Business Name',
      initiator: 'POS API Key Name'
    },
    id: '66da1bc34f03a871430c1a71'
  }
};

function createSignature(data, secret) {
  const payload = JSON.stringify(data) + secret;
  return createHash('sha256').update(payload).digest('hex');
}

const expected = createSignature(CALLBACK_BODY.data, YOUR_SECRET);
const received = CALLBACK_BODY.signature;

const isValid = timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(received, 'hex'));

if (isValid) {
  console.log('Valid callback');
} else {
  console.log('Invalid callback');
}

Implementation Notes

  • Compute signature from the exact parsed callback data object you received.

  • Compare signatures using constant-time comparison (timingSafeEqual) to reduce side-channel risk.

  • Acknowledge quickly with 2xx after validation, then process asynchronously.

Last updated