Webhooks

Webhooks allow you to receive real-time notifications about payment events. Instead of polling the API, Yugo sends HTTP POST requests to your server when events occur.

Overview

When you create a transaction with a webhookUrl, Yugo automatically sends status updates to that endpoint.

Setting Up Webhooks

1. Create a Webhook Endpoint

Your endpoint must:

  • Accept HTTP POST requests
  • Parse JSON request bodies
  • Return HTTP 200 status code
  • Respond within 30 seconds

2. Include Webhook URL in Requests

When creating a transaction, include your webhook URL:

json
{
  "amount": "100.00",
  "names": "John Doe",
  "userId": "user-123",
  "webhookUrl": "https://yourdomain.com/webhooks/yugo",
  "webhookPayload": "order-12345"
}

Webhook Events

A2A Payments

Webhook payload for A2A payment status updates:

json
{
  "status": "AUTHORIZED",
  "webhookPayload": "your-transaction-id",
  "payerBankAccount": {
    "iban": "DE89370400440532013000",
    "holderNames": "John Doe",
    "bank": "Revolut"
  }
}
FieldDescription
statusPayment status (INTENT, STARTED, AUTHORIZED, FAILED_INTENT)
webhookPayloadThe string you provided when creating the transaction
payerBankAccountBank details (included when payment is authorized)

Merchant On-ramp

Webhook payload for merchant on-ramp events:

json
{
  "eventType": "merchant_on_ramp_success",
  "transactionId": "5d141fb0-dea0-4499-8361-0915807d1151",
  "amount": 100.0,
  "currency": "EUR",
  "timestamp": "2025-04-17T10:14:22.001Z",
  "webhookPayload": "internal-order-id-xyz"
}
FieldDescription
eventTypeType of event (merchant_on_ramp_success)
transactionIdYugo transaction ID
amountTransaction amount
currencyTransaction currency
timestampEvent timestamp (ISO 8601)
webhookPayloadThe string you provided when creating the transaction

Implementing Your Webhook Handler

Node.js Example

javascript
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhooks/yugo', async (req, res) => {
  const { status, webhookPayload, eventType, transactionId } = req.body;

  // A2A Payment webhook
  if (status) {
    console.log(`Payment ${webhookPayload} status: ${status}`);
    
    if (status === 'AUTHORIZED') {
      // Handle successful authorization
      await processAuthorizedPayment(webhookPayload);
    } else if (status === 'FAILED_INTENT') {
      // Handle failed payment
      await handleFailedPayment(webhookPayload);
    }
  }

  // Merchant On-ramp webhook
  if (eventType === 'merchant_on_ramp_success') {
    console.log(`On-ramp success for transaction: ${transactionId}`);
    
    // Verify the transaction status
    const transaction = await verifyTransaction(transactionId);
    
    if (transaction.bankDepositStatus === 'SUCCESS') {
      await processSuccessfulDeposit(webhookPayload);
    }
  }

  // Always return 200 to acknowledge receipt
  res.status(200).json({ 
    message: 'Webhook processed successfully' 
  });
});

app.listen(3000);

Python Example

python
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhooks/yugo', methods=['POST'])
def handle_webhook():
    data = request.json
    
    # A2A Payment webhook
    if 'status' in data:
        status = data['status']
        webhook_payload = data.get('webhookPayload')
        
        if status == 'AUTHORIZED':
            process_authorized_payment(webhook_payload)
        elif status == 'FAILED_INTENT':
            handle_failed_payment(webhook_payload)
    
    # Merchant On-ramp webhook
    if data.get('eventType') == 'merchant_on_ramp_success':
        transaction_id = data['transactionId']
        # Verify and process
        verify_and_process(transaction_id)
    
    return jsonify({'message': 'Webhook processed successfully'}), 200

if __name__ == '__main__':
    app.run(port=3000)

Best Practices

1. Verify Transactions

Always verify webhook data by calling the Get Transaction endpoint:

bash
curl https://api.yugo.finance/transaction/?transactionId={transactionId} \
  -H "x-api-key: YOUR_API_KEY"

2. Handle Idempotency

Webhooks may be sent multiple times. Ensure your handler is idempotent:

javascript
app.post('/webhooks/yugo', async (req, res) => {
  const { transactionId } = req.body;
  
  // Check if already processed
  const existing = await db.findTransaction(transactionId);
  if (existing && existing.processed) {
    return res.status(200).json({ message: 'Already processed' });
  }
  
  // Process and mark as handled
  await processWebhook(req.body);
  await db.markAsProcessed(transactionId);
  
  res.status(200).json({ message: 'Processed' });
});

3. Respond Quickly

Return HTTP 200 immediately, then process asynchronously:

javascript
app.post('/webhooks/yugo', (req, res) => {
  // Acknowledge immediately
  res.status(200).json({ message: 'Received' });
  
  // Process in background
  processWebhookAsync(req.body).catch(console.error);
});

4. Log Everything

Keep detailed logs for debugging:

javascript
app.post('/webhooks/yugo', (req, res) => {
  console.log('Webhook received:', JSON.stringify(req.body));
  // ... process
});

Retry Policy

If your endpoint doesn't return HTTP 200:

IntegrationRetry Policy
Merchant On-rampUp to 5 retries with exponential backoff
A2A PaymentsRetries on failure

Testing Webhooks

Local Development

Use tools like ngrok to expose your local server:

bash
ngrok http 3000

Then use the ngrok URL as your webhookUrl:

json
{
  "webhookUrl": "https://abc123.ngrok.io/webhooks/yugo"
}

Webhook Debugging

  1. Log all incoming webhook payloads
  2. Verify the transaction via API after receiving webhook
  3. Check your server logs for errors
  4. Ensure your endpoint returns HTTP 200

Security Considerations

  • Use HTTPS for your webhook endpoint
  • Validate the webhook payload by verifying the transaction
  • Don't trust webhook data alone - always verify via API
  • Consider implementing webhook signature verification (contact Yugo for details)
Was this page helpful?