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/ultrapayContent-Type: application/jsonX-UltraPay-Event: payment.completedX-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
| Field | Description |
|---|---|
event | Event type (see below) |
transactionId | UltraPay transaction ID |
status | Current payment status |
amount | Amount in cents |
currency | 3-letter currency code |
customerEmail | Customer's email (if available) |
stripePaymentId | Stripe Payment Intent ID |
metadata | Your custom metadata (if provided) |
completedAt | When payment completed |
timestamp | When webhook was sent |
Event Types
| Event | Description |
|---|---|
| payment.completed | Payment succeeded |
| payment.failed | Payment failed or cancelled |
| payment.expired | Checkout session expired |
| payment.refunded | Payment refunded |
| payment.disputed | Chargeback 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 handlerapp.post('/api/webhooks/ultrapay', express.json(), (req, res) => {const { event, transactionId, status, amount, metadata } = req.body;switch (event) {case 'payment.completed':// Fulfill the orderconsole.log(`Payment ${transactionId} completed for $${amount / 100}`);fulfillOrder(metadata.orderId);break;case 'payment.failed':// Handle failureconsole.log(`Payment ${transactionId} failed`);notifyCustomerOfFailure(metadata.orderId);break;case 'payment.refunded':// Handle refundconsole.log(`Payment ${transactionId} refunded`);processRefund(metadata.orderId);break;case 'payment.disputed':// Handle chargebackconsole.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 orderfulfillOrder($metadata['orderId']);break;case 'payment.refunded':// Handle refundprocessRefund($metadata['orderId']);break;case 'payment.disputed':// Handle chargebackhandleDispute($metadata['orderId']);break;}// Acknowledge receipthttp_response_code(200);echo json_encode(['received' => true]);