Skip to main content

Goal

Receive asynchronous updates for payment authorizations so your internal transaction state remains accurate and reliable.

Steps

Step 1: Configure webhook delivery on authorization requests

What you need to do

Include webhook configuration when submitting authorization requests so Push can notify your system of final payment results.

How to do it

When calling authorize-payment, provide:
  • A webhook_url where Push will deliver webhook events
  • A webhook_secret used to verify webhook authenticity
  • A tag that maps webhook events back to your internal transaction record
This ensures your system can recover final payment outcomes even if the authorization request times out or returns an ambiguous response.

Step 2: Receive and process webhook events

What you need to do

Expose an HTTPS endpoint that can receive webhook events from Push, verify their authenticity, and update your internal transaction state.

How to do it

  1. Create a HTTP endpoint at the configured webhook_url capable of receiving POST requests.
  2. Verify the webhook signature and timestamp for each request (see security).
  3. Parse the webhook payload and update the transaction record in your database accordingly. Please refer to the reference guide for the structure of the webhook payload.
Due to the asynchronous nature of webhook delivery, your integration must support out-of-order delivery and handle duplicate events idempotently. See Independent Ordering and Idempotency for more information.

Security

Webhooks deliver data directly to an endpoint you control over the public internet. Because they are invoked automatically by Push, webhook endpoints must be explicitly secured to prevent unauthorized requests, data tampering, and replay attacks. Without proper verification, a malicious actor could spoof webhook requests and falsely mark payments as approved or declined in your system.
If your cloud data environment restricts network access from external IPs via a firewall, you may need to allow inbound traffic from Push IP addresses in order to receive webhook requests.
Production 44.238.180.175Sandbox 34.209.246.44

Signature verification

When a webhook_secret is provided, Push signs each webhook request using an HMAC-SHA256 signature derived from the raw request payload. This allows your application to verify that:
  • The request was sent by Push
  • The payload has not been modified in transit
Every webhook request must be verified before it is processed. 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 an HMAC-SHA256 signature using your webhook_secret and the raw body
  4. Compare the computed signature to the received signature using constant-time comparison
  5. Reject requests with invalid signatures (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');
}
curl --request POST \
  --url https://sandbox.pushcash.com/authorize \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "user_id": "user_lVpbPL0K1XIiHx0DxipRbD",
  "amount": 2500,
  "currency": "USD",
  "direction": "cash_in",
  "token": "token_mbDRHFi3dxIZEtykHsgUGC",
  "webhook_url": "https://yourapp.com/webhooks/push",
  "webhook_secret": "whsec_32_characters_minimum",
  "tag": "txn_12345"
}
'

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.

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

Integration checklist

  • Payment requests include the webhook_url, webhook_secret, and tag parameters
  • Webhook endpoint is reachable over HTTPS from the Push IP addresses
  • Webhook signatures and timestamps are verified before processing
  • Duplicate webhook deliveries are handled idempotently