Skip to main content

Cryptofuse Webhook Flow Guide

This guide explains the complete webhook flow for payments and withdrawals, including all possible statuses and transitions.

Payment Webhook Flow

Payment Status Progression

graph TD
A[waiting] -->|Payment received| B[confirming]
A -->|Time expired| E[expired]
B -->|Confirmations complete| C[completed]
B -->|Partial & expired| D[partially_completed]
B -->|Payment failed| F[failed]

style C fill:#90EE90
style D fill:#FFE4B5
style E fill:#FFB6C1
style F fill:#FFB6C1

Webhook Events by Status

1. Payment Created (waiting)

When: After successful payment creation
Webhook: None (you already have the response)

2. Payment Received (confirming)

When: Customer sends cryptocurrency to the payment address
Webhook Event: payment_status_update

{
"transaction_id": "550e8400-e29b-41d4-a456-426614174000",
"event": "payment_status_update",
"status": "confirming",
"timestamp": "2024-12-27T10:05:00Z",
"data": {
"transaction_id": "550e8400-e89b-12d3-a456-426614174000",
"status": "confirming",
"amount_usd": "100.00",
"amount_crypto": "100.00000000",
"blockchain": "TRX",
"token": "USDT",
"currency": "USDTTRC20",
"address": "TXYZabc123...",
"transaction_hash": "0xabc123...",
"confirmations": 1,
"payment_history": [
{
"deposit_id": "dep_123",
"amount": "100.00000000",
"transaction_hash": "0xabc123...",
"confirmations": 1,
"timestamp": "2024-12-27T10:05:00Z"
}
],
"total_paid_amount": "100.00000000"
}
}

Key Fields:

  • confirmations: Current number of blockchain confirmations
  • payment_history: Array of all deposits (important for multiple payments)
  • total_paid_amount: Sum of all deposits received

3. Payment Completed (completed)

When: Required confirmations reached and full amount received
Webhook Event: payment_status_update

{
"transaction_id": "550e8400-e89b-12d3-a456-426614174000",
"event": "payment_status_update",
"status": "completed",
"timestamp": "2024-12-27T10:10:00Z",
"data": {
"transaction_id": "550e8400-e89b-12d3-a456-426614174000",
"status": "completed",
"amount_usd": "100.00",
"amount_crypto": "100.00000000",
"payment_type": "full",
"final_usd_value": 99.50,
"transaction_hash": "0xabc123...",
"confirmations": 20,
"payment_history": [...],
"total_paid_amount": "100.00000000"
}
}

Additional Fields:

  • payment_type: "full", "partial", or "overpayment"
  • final_usd_value: Actual USD value merchant receives after conversion

4. Partial Payment Completed (partially_completed)

When: Payment expires but received amount is above threshold
Webhook Event: payment_status_update

{
"status": "partially_completed",
"payment_type": "partial",
"total_paid_amount": "50.00000000",
"final_usd_value": 49.50,
"data": {
"metadata": {
"partial_conversion": {
"paid_percentage": 50,
"paid_amount": "50.00000000",
"converted_to": "USDT",
"reason": "Partial payment above threshold"
}
}
}
}

5. Overpayment (completed with overpayment)

When: Customer sends more than requested amount
Webhook Event: payment_status_update

{
"status": "completed",
"payment_type": "overpayment",
"amount_crypto": "100.00000000",
"total_paid_amount": "150.00000000",
"overpayment_amount": "50.00000000",
"overpayment_usd_value": "49.75",
"amount_limited": true,
"rejected_amount": "30.00000000",
"rejected_usd_value": "29.85"
}

Overpayment Fields:

  • overpayment_amount: Excess amount received
  • amount_limited: true if system capped the accepted amount
  • rejected_amount: Amount over the limit that was not credited

6. Payment Expired

When: Payment time limit reached without payment
Webhook Event: payment_status_update

{
"transaction_id": "550e8400-e89b-12d3-a456-426614174000",
"event": "payment_status_update",
"status": "expired",
"timestamp": "2024-12-27T10:30:00Z",
"data": {
"status": "expired",
"metadata": {
"expiry_details": {
"reason": "No payment received within time limit",
"expired_at": "2024-12-27T10:30:00Z",
"received_amount": "0"
}
}
}
}

Multiple Deposits Scenario

Some customers may send multiple transactions to pay:

{
"status": "completed",
"payment_history": [
{
"deposit_id": "dep_123",
"amount": "60.00000000",
"transaction_hash": "0xabc123...",
"confirmations": 20,
"timestamp": "2024-12-27T10:05:00Z"
},
{
"deposit_id": "dep_124",
"amount": "40.00000000",
"transaction_hash": "0xdef456...",
"confirmations": 15,
"timestamp": "2024-12-27T10:08:00Z"
}
],
"total_paid_amount": "100.00000000"
}

Withdrawal Webhook Flow

Withdrawal Status Progression

graph TD
A[pending] -->|Processing started| B[processing]
B -->|Blockchain broadcast| C[confirming]
C -->|Confirmations complete| D[completed]
A -->|Validation failed| E[failed]
B -->|Processing failed| E[failed]

style D fill:#90EE90
style E fill:#FFB6C1

Withdrawal Webhook Events

1. Withdrawal Created (pending)

When: After successful withdrawal creation
Webhook: None (you already have the response)

2. Processing Started

When: System begins processing the withdrawal
Webhook Event: withdrawal_status_update

{
"withdrawal_id": "660e8400-e29b-12d3-a456-426614174000",
"event": "withdrawal_status_update",
"status": "processing",
"timestamp": "2024-12-27T11:05:00Z",
"data": {
"withdrawal_id": "660e8400-e29b-12d3-a456-426614174000",
"status": "processing",
"amount": "98.50000000",
"requested_amount": "100.00000000",
"fee": "1.500000",
"blockchain": "TRX",
"token": "USDT",
"currency": "USDTTRC20",
"address": "TXYZabc123..."
}
}

3. Blockchain Broadcast (confirming)

When: Transaction broadcast to blockchain
Webhook Event: withdrawal_status_update

{
"withdrawal_id": "660e8400-e29b-12d3-a456-426614174000",
"event": "withdrawal_status_update",
"status": "confirming",
"timestamp": "2024-12-27T11:07:00Z",
"data": {
"status": "confirming",
"transaction_hash": "0xdef456...",
"confirmations": 1,
"required_confirmations": 20
}
}

4. Withdrawal Completed

When: Transaction confirmed on blockchain
Webhook Event: withdrawal_status_update

{
"withdrawal_id": "660e8400-e29b-12d3-a456-426614174000",
"event": "withdrawal_status_update",
"status": "completed",
"timestamp": "2024-12-27T11:10:00Z",
"data": {
"status": "completed",
"transaction_hash": "0xdef456...",
"confirmations": 20,
"completed_at": "2024-12-27T11:10:00Z"
}
}

5. Withdrawal Failed

When: Any error during processing
Webhook Event: withdrawal_status_update

{
"withdrawal_id": "660e8400-e29b-12d3-a456-426614174000",
"event": "withdrawal_status_update",
"status": "failed",
"timestamp": "2024-12-27T11:06:00Z",
"data": {
"status": "failed",
"error_message": "Insufficient balance in hot wallet",
"processing_notes": "Will retry when wallet is refilled"
}
}

Webhook Best Practices

1. Webhook Security

// Verify webhook signature
function verifyWebhook($payload, $signature, $secret) {
$expected = hash_hmac('sha256', $payload, $secret);
return hash_equals($signature, $expected);
}

// In your webhook handler
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'];
if (!verifyWebhook($rawBody, $signature, $webhookSecret)) {
http_response_code(401);
exit();
}

2. Idempotency Handling

// Track processed webhooks
const processedWebhooks = new Set();

async function handleWebhook(data) {
// Create unique key
const webhookKey = `${data.transaction_id}-${data.status}-${data.timestamp}`;

// Skip if already processed
if (processedWebhooks.has(webhookKey)) {
return { status: 'already_processed' };
}

// Process webhook
await processPaymentUpdate(data);

// Mark as processed
processedWebhooks.add(webhookKey);

// Store in database for persistence
await db.webhookLog.create({
key: webhookKey,
processed_at: new Date()
});
}

3. Status Transition Validation

# Valid status transitions
VALID_TRANSITIONS = {
'waiting': ['confirming', 'expired'],
'confirming': ['completed', 'partially_completed', 'failed'],
'partially_paid': ['partially_completed', 'expired'],
# Final states cannot transition
'completed': [],
'partially_completed': [],
'failed': [],
'expired': []
}

def validate_status_transition(current_status, new_status):
"""Ensure status transition is valid"""
valid_next = VALID_TRANSITIONS.get(current_status, [])
if new_status not in valid_next:
raise ValueError(f"Invalid transition: {current_status} -> {new_status}")

4. Retry Logic

// Exponential backoff for failed webhook processing
async function processWebhookWithRetry(data, attempt = 1) {
try {
await processWebhook(data);
} catch (error) {
if (attempt < 5) {
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s, 16s
await sleep(delay);
return processWebhookWithRetry(data, attempt + 1);
}
throw error;
}
}

5. Webhook Response

Always respond quickly to webhooks:

from threading import Thread

def webhook_handler(request):
# Verify signature
if not verify_signature(request):
return HttpResponse(status=401)

# Parse payload
data = json.loads(request.body)

# Process async to respond quickly
Thread(target=process_webhook_async, args=(data,)).start()

# Respond immediately
return HttpResponse(status=200)

Common Scenarios

Scenario 1: Standard Payment Flow

  1. Customer initiates payment → status: waiting
  2. Customer sends exact amount → webhook: confirming
  3. Confirmations complete → webhook: completed

Scenario 2: Overpayment with Limit

  1. Overpayment threshold: $500
  2. Customer sends $800 extra
  3. System accepts $500, rejects $300
  4. Webhook shows amount_limited: true, rejected_amount: "300"

Scenario 3: Multiple Partial Payments

  1. Customer sends 60% → webhook: confirming
  2. Customer sends 40% → webhook: confirming (updated)
  3. Total reaches 100% → webhook: completed

Scenario 4: Withdrawal with Retry

  1. Create withdrawal → status: pending
  2. Hot wallet empty → webhook: failed
  3. Admin refills wallet
  4. System retries → webhook: processing
  5. Success → webhook: completed

Troubleshooting Webhooks

Missing Webhooks

  1. Check callback_url is publicly accessible
  2. Verify webhook configuration in /user/webhooks/
  3. Check firewall/security groups
  4. Look for 4xx/5xx responses in logs

Duplicate Webhooks

  • Normal behavior for reliability
  • Always implement idempotency
  • Use transaction_id + status as unique key

Out-of-Order Webhooks

  • Possible due to network delays
  • Always check current status via API
  • Validate status transitions

Debugging Tips

# Test webhook endpoint
curl -X POST https://your-site.com/webhook \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: test" \
-d '{"test": true}'

# Use webhook testing service
https://webhook.site

# Monitor webhook logs
tail -f /var/log/webhooks.log

Webhook Data Reference

Payment Webhook Fields

  • transaction_id: Unique payment identifier
  • status: Current payment status
  • amount_usd: Original USD amount
  • amount_crypto: Expected crypto amount
  • total_paid_amount: Actual amount received
  • payment_type: full/partial/overpayment
  • final_usd_value: USD value after conversion
  • payment_history: Array of all deposits
  • confirmations: Blockchain confirmations
  • transaction_hash: Blockchain transaction ID

Withdrawal Webhook Fields

  • withdrawal_id: Unique withdrawal identifier
  • status: Current withdrawal status
  • amount: Amount user receives
  • requested_amount: Original requested amount
  • fee: Total fee in USD
  • transaction_hash: Blockchain transaction ID
  • confirmations: Current confirmations
  • completed_at: Completion timestamp
  • error_message: Error details if failed