Blog
    Tutorial5 min read

    Using Webhooks for Real-Time Credit Updates

    How to leverage webhooks to keep your application in sync with credit transactions and purchases.

    By cred.diy Team

    Using Webhooks for Real-Time Credit Updates

    Webhooks are the backbone of real-time integrations. Instead of constantly polling for updates, webhooks push events to your application the moment they happen. This guide covers everything you need to know about implementing webhooks with cred.diy.

    What Are Webhooks?

    Webhooks are HTTP callbacks that notify your application when events occur. When a user purchases credits, for example, cred.diy sends a POST request to your specified URL with details about the purchase.

    Benefits of Webhooks

    • Real-time updates: No polling delays
    • Efficient: Only receive relevant events
    • Reliable: Built-in retry mechanisms
    • Decoupled: Your app doesn't need to track state

    Available Webhook Events

    cred.diy sends webhooks for these events:

    EventDescription
    credits.issuedCredits were issued to a user
    credits.deductedCredits were deducted from a user
    credits.lowUser balance fell below threshold
    purchase.completedCredit purchase was successful
    purchase.failedCredit purchase failed
    referral.completedReferral reward was issued

    Setting Up Webhooks

    Step 1: Create an Endpoint

    // Express.js example
    app.post('/webhooks/cred', express.raw({ type: 'application/json' }), (req, res) => {
      const payload = req.body;
      const signature = req.headers['x-cred-signature'];
      
      // Process the webhook
      handleWebhook(payload, signature);
      
      // Always respond quickly
      res.status(200).send('OK');
    });
    

    Step 2: Register in Dashboard

    1. Go to Webhooks in your dashboard
    2. Click Add Endpoint
    3. Enter your URL: https://yourapp.com/webhooks/cred
    4. Select events to subscribe to
    5. Copy the signing secret

    Step 3: Verify Signatures

    Always verify webhook signatures to ensure authenticity:

    const crypto = require('crypto');
    
    const verifySignature = (payload, signature, secret) => {
      const expectedSignature = crypto
        .createHmac('sha256', secret)
        .update(JSON.stringify(payload))
        .digest('hex');
      
      return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expectedSignature)
      );
    };
    
    app.post('/webhooks/cred', (req, res) => {
      const signature = req.headers['x-cred-signature'];
      
      if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
        return res.status(401).send('Invalid signature');
      }
      
      // Process verified webhook
      processWebhook(req.body);
      res.status(200).send('OK');
    });
    

    Handling Different Events

    Credits Issued

    const handleCreditsIssued = async (event) => {
      const { user_id, amount, new_balance, reason } = event;
      
      // Update local cache
      await cache.set(`balance:${user_id}`, new_balance);
      
      // Send notification
      await sendPushNotification(user_id, {
        title: 'Credits Received!',
        body: `You received ${amount} credits. New balance: ${new_balance}`
      });
      
      // Update analytics
      await analytics.track('credits_received', { user_id, amount });
    };
    

    Purchase Completed

    const handlePurchaseCompleted = async (event) => {
      const { user_id, amount, price_paid, pricing_tier } = event;
      
      // Send receipt email
      await sendEmail(user_id, 'receipt', {
        credits: amount,
        amount_paid: price_paid,
        tier_name: pricing_tier.name
      });
      
      // Log for accounting
      await recordRevenue({
        user_id,
        amount: price_paid,
        type: 'credit_purchase'
      });
    };
    

    Low Balance Warning

    const handleLowBalance = async (event) => {
      const { user_id, current_balance, threshold } = event;
      
      // Send warning email
      await sendEmail(user_id, 'low_balance', {
        balance: current_balance,
        refill_link: `https://yourapp.com/buy-credits`
      });
      
      // Show in-app notification
      await createNotification(user_id, {
        type: 'warning',
        message: 'Your credit balance is running low!'
      });
    };
    

    Best Practices

    1. Respond Quickly

    Return 200 immediately, process asynchronously:

    app.post('/webhooks/cred', (req, res) => {
      // Acknowledge immediately
      res.status(200).send('OK');
      
      // Process in background
      processWebhookAsync(req.body).catch(console.error);
    });
    

    2. Handle Duplicates

    Webhooks may be sent multiple times:

    const processWebhook = async (event) => {
      const eventId = event.id;
      
      // Check if already processed
      const processed = await redis.get(`webhook:${eventId}`);
      if (processed) return;
      
      // Process the event
      await handleEvent(event);
      
      // Mark as processed (expire after 24 hours)
      await redis.setex(`webhook:${eventId}`, 86400, 'true');
    };
    

    3. Handle Failures Gracefully

    Implement exponential backoff for your own retries:

    const processWithRetry = async (event, attempt = 1) => {
      try {
        await handleEvent(event);
      } catch (error) {
        if (attempt < 5) {
          const delay = Math.pow(2, attempt) * 1000;
          await sleep(delay);
          return processWithRetry(event, attempt + 1);
        }
        throw error;
      }
    };
    

    4. Log Everything

    const processWebhook = async (event) => {
      console.log('Webhook received:', {
        type: event.type,
        id: event.id,
        timestamp: new Date().toISOString()
      });
      
      try {
        await handleEvent(event);
        console.log('Webhook processed successfully:', event.id);
      } catch (error) {
        console.error('Webhook processing failed:', {
          eventId: event.id,
          error: error.message
        });
        throw error;
      }
    };
    

    Testing Webhooks

    Use the Dashboard

    cred.diy provides a webhook testing tool:

    1. Go to Webhooks > Test
    2. Select an event type
    3. Click Send Test Event
    4. Check your endpoint logs

    Local Development

    Use ngrok for local testing:

    ngrok http 3000
    # Use the ngrok URL in your webhook settings
    

    Troubleshooting

    Webhook Not Received

    • Verify the URL is publicly accessible
    • Check firewall rules
    • Confirm the endpoint returns 200
    • Review the delivery logs in dashboard

    Invalid Signature

    • Ensure you're using the raw body for verification
    • Check that the secret matches your dashboard
    • Verify no middleware is modifying the body

    Conclusion

    Webhooks transform your application from a polling-based system to a reactive, real-time platform. By implementing proper webhook handling, you'll provide a better user experience and maintain accurate credit balances.

    Configure your webhooks in the dashboard.

    Try it on a real project.