Webhooks

Receive real-time notifications when payment status changes. Webhooks enable you to respond to payment events as they happen.

Setup

Include webhookUrl in your payment request to receive notifications:

json
{
"amount": 5000,
"currency": "usd",
"successUrl": "https://yoursite.com/success",
"cancelUrl": "https://yoursite.com/cancel",
"webhookUrl": "https://yoursite.com/api/webhooks/ultrapay"
}

Webhook Payload

When a payment event occurs, we'll send a POST request to your webhook URL:

http
POST https://yoursite.com/api/webhooks/ultrapay
Content-Type: application/json
X-UltraPay-Event: payment.completed
X-UltraPay-Transaction: 550e8400-e29b-41d4-a716-446655440000
json
{
"event": "payment.completed",
"transactionId": "550e8400-e29b-41d4-a716-446655440000",
"status": "succeeded",
"amount": 5000,
"currency": "usd",
"customerEmail": "customer@example.com",
"stripePaymentId": "pi_3ABC123...",
"metadata": {
"orderId": "12345"
},
"completedAt": "2025-12-23T14:30:00.000Z",
"timestamp": "2025-12-23T14:30:05.000Z"
}

Payload Fields

FieldDescription
eventEvent type (see below)
transactionIdUltraPay transaction ID
statusCurrent payment status
amountAmount in cents
currency3-letter currency code
customerEmailCustomer's email (if available)
stripePaymentIdStripe Payment Intent ID
metadataYour custom metadata (if provided)
completedAtWhen payment completed
timestampWhen webhook was sent

Event Types

EventDescription
payment.completedPayment succeeded
payment.failedPayment failed or cancelled
payment.expiredCheckout session expired
payment.refundedPayment refunded
payment.disputedChargeback filed

Your Response

Return any 2xx status code to acknowledge receipt:

json
{ "received": true }

Retry Policy

  • 3 attempts: immediately, then at 5s and 30s
  • Stops on 4xx errors (your code issue)
  • Retries on 5xx or network errors

Example Handler (Node.js)

javascript
// Express.js webhook handler
app.post('/api/webhooks/ultrapay', express.json(), (req, res) => {
const { event, transactionId, status, amount, metadata } = req.body;
switch (event) {
case 'payment.completed':
// Fulfill the order
console.log(`Payment ${transactionId} completed for $${amount / 100}`);
fulfillOrder(metadata.orderId);
break;
case 'payment.failed':
// Handle failure
console.log(`Payment ${transactionId} failed`);
notifyCustomerOfFailure(metadata.orderId);
break;
case 'payment.refunded':
// Handle refund
console.log(`Payment ${transactionId} refunded`);
processRefund(metadata.orderId);
break;
case 'payment.disputed':
// Handle chargeback
console.log(`Payment ${transactionId} disputed`);
handleDispute(metadata.orderId);
break;
}
res.json({ received: true });
});

Example Handler (PHP)

php
<?php
// Get the webhook payload
$payload = json_decode(file_get_contents('php://input'), true);
$event = $payload['event'];
$transactionId = $payload['transactionId'];
$metadata = $payload['metadata'] ?? [];
switch ($event) {
case 'payment.completed':
// Fulfill the order
fulfillOrder($metadata['orderId']);
break;
case 'payment.refunded':
// Handle refund
processRefund($metadata['orderId']);
break;
case 'payment.disputed':
// Handle chargeback
handleDispute($metadata['orderId']);
break;
}
// Acknowledge receipt
http_response_code(200);
echo json_encode(['received' => true]);
navigate select