TuxoPay Docs
API

Webhook Events

Documentation for webhook events sent to your endpoints when payment status changes

Overview

When you include a webhook_endpoint parameter during payment creation, TuxoPay will send webhook notifications to your endpoint when payment status changes.

All webhooks are signed with HMAC-SHA256 for security. Always verify the signature before processing webhook data.

Receiving Webhooks

TuxoPay will POST webhook notifications to your specified endpoint with the following structure:

Endpoint

POST https://your-domain.com/webhooks/tuxopay

Headers

Content-Type: application/json
Signature: {hmac_sha256_signature}

Webhook Payload Structure

{
  "event": "payment.status_changed",
  "timestamp": "2025-06-30T18:56:32Z",
  "payment": {
    "id": 123,
    "payment_session_id": "sess_abc123",
    "provider_transaction_id": "txn_xyz789",
    "amount": 100.00,
    "currency": "USD",
    "status": "completed",
    "country": "US",
    "payment_provider": "blackpay",
    "flow_id": 1,
    "created_at": "2025-06-30T18:50:00Z",
    "updated_at": "2025-06-30T18:56:32Z",
    "expires_at": "2025-06-30T19:50:00Z"
  },
  "customer": {
    "id": 456,
    "email": "[email protected]",
    "phone": "+1234567890",
    "first_name": "John",
    "last_name": "Doe",
    "full_name": "John Doe",
    "address": "123 Main St",
    "city": "New York",
    "region": "NY",
    "postal_code": "10001",
    "country_code": "US",
    "date_of_birth": "1990-01-01",
    "gender": "male",
    "company": "Acme Corp",
    "vat_number": "US123456789"
  },
  "shop": {
    "id": 789,
    "name": "My Shop"
  },
  "metadata": {
    "order_id": "ORDER-123",
    "custom_field": "custom_value"
  },
  "provider_metadata": {},
  "original_webhook": {
    "provider": "blackpay",
    "data": {
      "order_token": "ord_abc123",
      "siteb_status": "completed"
    },
    "processed_at": "2025-06-30T18:56:32Z"
  }
}

Event Types

payment.status_changed

Sent whenever a payment status changes. Possible status values:

  • pending - Payment initiated but not yet processed
  • processing - Payment is being processed
  • completed - Payment successfully completed
  • failed - Payment failed
  • cancelled - Payment was cancelled
  • refunded - Payment was refunded

Payload Fields

Webhook Security

Always verify webhook signatures to ensure requests are from TuxoPay and haven't been tampered with.

All webhooks include a Signature header containing an HMAC-SHA256 signature of the request body. Verify it using your shop's webhook secret.

Signature Verification

<?php

$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_SIGNATURE'] ?? '';
$webhookSecret = 'your-shop-webhook-secret';

$expectedSignature = hash_hmac('sha256', $payload, $webhookSecret);

if (!hash_equals($signature, $expectedSignature)) {
    http_response_code(401);
    exit('Invalid signature');
}

// Signature is valid, process the webhook
$data = json_decode($payload, true);
const crypto = require('crypto');

function verifyWebhook(req, webhookSecret) {
  const signature = req.headers['signature'];
  const payload = JSON.stringify(req.body);

  const expectedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(payload)
    .digest('hex');

  if (signature !== expectedSignature) {
    throw new Error('Invalid signature');
  }

  return true;
}

// Usage in Express
app.post('/webhooks/tuxopay', (req, res) => {
  try {
    verifyWebhook(req, process.env.WEBHOOK_SECRET);

    // Process webhook
    const { event, payment, customer } = req.body;

    res.json({ success: true });
  } catch (error) {
    res.status(401).json({ error: 'Invalid signature' });
  }
});
import hmac
import hashlib

def verify_webhook(payload, signature, webhook_secret):
    expected_signature = hmac.new(
        webhook_secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, expected_signature)

# Usage in Flask
@app.route('/webhooks/tuxopay', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('Signature')
    payload = request.get_data(as_text=True)

    if not verify_webhook(payload, signature, WEBHOOK_SECRET):
        return {'error': 'Invalid signature'}, 401

    data = request.json
    # Process webhook

    return {'success': True}

Retry Logic

TuxoPay automatically retries failed webhook deliveries with exponential backoff.

AttemptDelayTimeout
1stImmediate3 seconds
2nd10 second3 seconds
3rd100 seconds3 seconds
4th1000 seconds3 seconds

Maximum retries: 3 attempts

Examples

Completed Payment

{
  "event": "payment.status_changed",
  "timestamp": "2025-06-30T18:56:32Z",
  "payment": {
    "id": 123,
    "payment_session_id": "sess_abc123",
    "provider_transaction_id": "txn_xyz789",
    "amount": 100.00,
    "currency": "USD",
    "status": "completed",
    "country": "US",
    "payment_provider": "blackpay",
    "flow_id": 1
  },
  "customer": {
    "id": 456,
    "email": "[email protected]",
    "first_name": "John",
    "last_name": "Doe"
  },
  "shop": {
    "id": 789,
    "name": "My Shop"
  }
}

Failed Payment

{
  "event": "payment.status_changed",
  "timestamp": "2025-06-30T18:58:15Z",
  "payment": {
    "id": 124,
    "payment_session_id": "sess_def456",
    "provider_transaction_id": "txn_fail999",
    "amount": 50.00,
    "currency": "EUR",
    "status": "failed",
    "country": "DE",
    "payment_provider": "stripe",
    "flow_id": 2
  },
  "customer": {
    "id": 457,
    "email": "[email protected]",
    "first_name": "Jane",
    "last_name": "Smith"
  },
  "shop": {
    "id": 789,
    "name": "My Shop"
  }
}

Best Practices

  1. Respond Quickly - Return a 2xx status code within 5 seconds to acknowledge receipt
  2. Process Asynchronously - Queue webhooks for background processing to avoid timeouts
  3. Handle Duplicates - Implement idempotency using the payment.id to handle duplicate deliveries
  4. Log Everything - Log all webhook receipts for debugging and audit purposes
  5. Verify Signatures - Always verify the signature before processing webhook data

Testing Your Webhook Endpoint

Use the TuxoPay API to test your webhook endpoint:

curl -X POST https://orchestrator.tuxopay.com/api/webhooks/shop/{shopId}/test \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_endpoint": "https://your-shop.com/webhooks/test"
  }'

Getting Webhook Statistics

Monitor your webhook delivery success rate:

curl -X GET https://orchestrator.tuxopay.com/api/webhooks/shop/{shopId}/stats \
  -H "Authorization: Bearer YOUR_TOKEN"

Response:

{
  "success": true,
  "data": {
    "shop_id": 789,
    "shop_name": "My Shop",
    "total_payments": 150,
    "payments_with_webhooks": 148,
    "webhook_coverage_percentage": 98.67,
    "recent_webhooks": [],
    "webhook_secret": "your-webhook-secret"
  }
}

Troubleshooting