Revenue Examples#

Copy the example that matches your stack to start sending revenue events in minutes.

Stripe Checkout (Node.js + Express)#

// server/stripe-webhook.ts
import express from 'express';
import Stripe from 'stripe';
import { Revenue } from '@pulsora/revenue';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2023-10-16',
});

const revenue = new Revenue({
  apiToken: process.env.PULSORA_SECRET!,
  debug: process.env.NODE_ENV !== 'production',
});

const app = express();
app.post(
  '/webhooks/stripe',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    const signature = req.headers['stripe-signature']!;

    let event;
    try {
      event = stripe.webhooks.constructEvent(
        req.body,
        signature,
        process.env.STRIPE_WEBHOOK_SECRET!,
      );
    } catch (err) {
      console.error('Webhook signature verification failed', err);
      return res.sendStatus(400);
    }

    if (event.type === 'checkout.session.completed') {
      const session = event.data.object as Stripe.Checkout.Session;

      await revenue.track({
        visitor_fingerprint: session.metadata?.visitor_fingerprint ?? '',
        session_id: session.metadata?.session_id ?? '',
        customer_id: session.customer as string,
        amount: session.amount_total! / 100,
        currency: session.currency!.toUpperCase(),
        transaction_id: session.id,
        payment_processor: 'stripe',
        event_type: 'subscription',
        customer_subscription_id: session.subscription as string,
        is_recurring: true,
        metadata: {
          plan: session.metadata?.plan,
        },
      });
    }

    res.json({ received: true });
  },
);

Stripe Checkout (Laravel)#

// app/Http/Controllers/StripeWebhookController.php
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Pulsora\Revenue\Revenue;

class StripeWebhookController extends Controller
{
    public function __invoke(Request $request)
    {
        $payload = $request->all();

        if ($payload['type'] !== 'checkout.session.completed') {
            return response()->json(['ignored' => true]);
        }

        $session = $payload['data']['object'];

        $revenue = new Revenue([
            'apiToken' => config('services.pulsora.secret'),
        ]);

        $revenue->track([
            'visitor_fingerprint' => $session['metadata']['visitor_fingerprint'] ?? null,
            'session_id' => $session['metadata']['session_id'] ?? null,
            'customer_id' => $session['customer'],
            'amount' => $session['amount_total'] / 100,
            'currency' => strtoupper($session['currency']),
            'transaction_id' => $session['id'],
            'payment_processor' => 'stripe',
            'event_type' => 'purchase',
        ]);

        return response()->json(['success' => true]);
    }
}

Paddle Billing#

app.post('/webhooks/paddle', express.json(), async (req, res) => {
  const event = req.body;

  if (event.event_type === 'transaction.completed') {
    await revenue.track({
      visitor_fingerprint: event.data.custom_data?.visitor_fingerprint,
      session_id: event.data.custom_data?.session_id,
      customer_id: event.data.customer_id,
      amount: parseFloat(event.data.details.totals.total),
      currency: event.data.currency_code,
      transaction_id: event.data.id,
      payment_processor: 'paddle',
      event_type: event.data.status === 'refunded' ? 'refund' : 'purchase',
      metadata: {
        plan: event.data.items[0]?.price?.product?.name,
      },
    });
  }

  res.json({ success: true });
});

Manual Invoices#

await revenue.track({
  visitor_fingerprint: 'fp_manual_import',
  session_id: 'sess_manual_import',
  customer_id: 'cus_enterprise',
  amount: 5000,
  currency: 'USD',
  transaction_id: 'invoice-2024-05',
  payment_processor: 'manual',
  event_type: 'purchase',
  metadata: {
    notes: 'Annual enterprise invoice – recorded manually',
  },
});

Handling Errors Gracefully#

try {
  await revenue.track(payload);
} catch (error) {
  // Log to your observability stack
  logger.error('Pulsora revenue failed', { error });

  // Optionally retry or queue for later
  await queue.add('revenue-retry', payload, { delay: 5 * 60 * 1000 });
}

Need a refresher on payload fields? Jump back to the API reference →