Skip to main content
This guide defines the steps necessary to enable webhook delivery between your application and Push Cash’s API for payment authorizations. As a pre-requisite, please ensure that you have completed the initial integration using this guide.

Overview

Webhooks provide asynchronous updates about authorization results via HTTP callbacks, ensuring payment status is synchronized between your system and Push Cash. Without webhooks, ambiguous authorization results (e.g. due to timeouts or internal server errors) leave little recourse to recover and correct transaction status. We strongly recommend that all integrations enable webhooks to ensure stability and reliability.

Integration Steps

1. Update Authorization Request

Update yourPOST /authorizationrequest, following the format in the API Reference. Important fields to note for webhook enablement are:
FieldTypeDescription
webhook_urlstringHTTPS URL where Push will send webhook callbacks
webhook_secretstringSecret key for HMAC signature verification (minimum 32 characters, maximum 4096 characters)
tagstringYour internal transaction ID for matching webhooks to internal records (maximum 255 characters)

2. Implement Webhook Endpoint

Create an endpoint at yourwebhook_urlthat:
  1. Verifies webhook authenticity (see Security section below)
  2. Update internal transaction record based ondata.tagandtypefields (see Update Internal Transaction Record below)
Example Webhook Payload
{
 "type": "intent.approved", // webhook type
 "data": {
   "tag": "txn_12345", // your internal transaction ID
   // fields omitted for brevity
 },
 "timestamp": "2024-01-14T22:31:02.756096Z" // ISO 8601 timestamp
}
See full reference guide for more information.

Security

Signature Verification

If webhook_secret was provided, your webhook request will include an X-Webhook-Signature header containing an HMAC-SHA256 signature of the payload. You must verify this signature to ensure the request came from Push Cash and hasn’t been tampered with. Signature Format
X-Webhook-Signature: sha256=<hex_encoded_hmac>
Verification Steps
  1. Extract the signature from the X-Webhook-Signature header
  2. Read the raw request body as bytes (before parsing JSON)
  3. Compute HMAC-SHA256 using your webhook_secret and the raw body
  4. Compare signatures using constant-time comparison
  5. Reject requests with invalid signatures (return 401 Unauthorized)
const crypto = require('crypto');

function verifyWebhookSignature(secret, rawBody, signatureHeader) {
  // Parse signature from header
  if (!signatureHeader || !signatureHeader.startsWith('sha256=')) {
    return false;
  }
  
  const receivedSignature = signatureHeader.substring(7); // Remove 'sha256=' prefix
  
  // Compute expected signature
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(rawBody); // rawBody must be Buffer or string
  const expectedSignature = hmac.digest('hex');
  
  // Constant-time comparison to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(receivedSignature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

// Express.js example
app.post('/webhooks/push', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const rawBody = req.body; // Raw buffer from express.raw()
  
  if (!verifyWebhookSignature(WEBHOOK_SECRET, rawBody, signature)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Parse JSON after verification
  const payload = JSON.parse(rawBody.toString());
  
  // Verify timestamp (see below)
  // Process webhook...
  
  res.status(200).send('OK');
});

Timestamp Verification

The timestamp field in the webhook payload indicates when the webhook was created. To prevent replay attacks, verify that the timestamp is recent (within 10 minutes). Verification Steps
  1. Parse the timestamp field from the payload (ISO 8601format)
  2. Compare with current time
  3. Reject requests older than 10 minutes (return401 Unauthorized)
function verifyWebhookTimestamp(timestamp, maxAgeMinutes = 10) {
 const webhookTime = new Date(timestamp);
 const now = new Date();
 const ageMinutes = (now - webhookTime) / 1000 / 60;


 return ageMinutes <= maxAgeMinutes;
}


// In your webhook handler
if (!verifyWebhookTimestamp(payload.timestamp)) {
 return res.status(401).send('Webhook timestamp too old');
}

Update Internal Transaction Record

  1. Match webhook to internal transaction record
  • Parse the webhook payload for thedata.tagfield and match it to your internal transaction record in your database
You need to commit the transaction record in your database before calling the authorization endpoint. View Independent Ordering for more information.
  1. Update transaction status
  • Parse the webhook payload for thetypefield and update the transaction status in your database
You must ensure that you handle idempotency and do not perform a duplicate update. View Handle Duplicates (Idempotency) for more information.

Independent Ordering

Webhook delivery and authorization responses are independent and may arrive in any order. Your system must handle both scenarios by ensuring that the internal transaction record is committed to the database before the call to the authorization endpoint:
  1. Webhook arrives first (before authorization response)
  2. Authorization response arrives first (before webhook)
Sequence diagram illustrates the two possible scenarios of ordering between webhook delivery and authorization results. Sequence diagram illustrates the two possible scenarios of ordering between webhook delivery and authorization results.

Handle Duplicates (Idempotency)

Push provides at-least-once delivery for webhooks. Your application must handle duplicate webhook deliveries gracefully using database transactions to ensure idempotency. In the case of a duplicated webhook delivery from Push either due to an error or timeout from your callback handler, you should discard the request and return a200 OK. If Push does not receive a200 OKresponse from your webhook endpoint, delivery will be retried with exponential backoff.
EnvironmentRetry Behavior
SandboxUp to 3 attempts
ProductionUp to 40 attempts over 3 days, then marked as expired

Best Practices

  • Return200 OKonly after successfully processing the webhook
  • Return4xx/5xxif processing fails (triggers retry)
  • Log webhook payloads for debugging and audit trails
  • Monitor webhook failures to detect integration issues
I