SubLoop JavaScript SDK

Official JavaScript SDK for SubLoop's payment analytics API. Works in both Node.js and browser environments.

Installation

npm install subloop-js

Quick Start

const SubLoop = require('subloop-js');

// Initialize the client
const subloop = new SubLoop('sk_your_api_key_here');

// Send payment data
const payment = await subloop.payments.createSuccessful(
    'cust_001',    // customerId
    29.99,         // amount
    'USD',         // currency
    'sub_001'      // subscriptionId
);

// Get analytics
const overview = await subloop.analytics.getOverview();
const mrr = await subloop.analytics.getMRR();

Authentication

Get your API key from your SubLoop dashboard:

const subloop = new SubLoop('sk_your_api_key_here');

// For development/testing
const subloop = new SubLoop('sk_your_api_key_here', 'http://localhost:8000');

Payment Events

Create Payment Event

// Manual payment creation
const payment = await subloop.payments.create({
    customer_id: 'cust_001',
    subscription_id: 'sub_001', // Optional for recurring payments
    amount: 29.99,
    currency: 'USD',
    status: 'succeeded', // succeeded, failed, pending, refunded
    payment_date: '2025-07-31T00:00:00Z',
    payment_method: 'card',
    metadata: {
        gateway_transaction_id: 'txn_123',
        customer_email: '[email protected]'
    }
});

// Helper methods for common scenarios
const successfulPayment = await subloop.payments.createSuccessful(
    'cust_001',
    29.99,
    'USD',
    'sub_001'
);

const failedPayment = await subloop.payments.createFailed(
    'cust_002',
    19.99,
    'USD'
);

const refundedPayment = await subloop.payments.createRefunded(
    'cust_003',
    39.99,
    'USD',
    'sub_002'
);

List Payment Events

// Get all payments
const payments = await subloop.payments.list();

// With pagination
const payments = await subloop.payments.list({ page: 2 });

// Get specific payment
const payment = await subloop.payments.get(123);

Analytics

Dashboard Overview

const overview = await subloop.analytics.getOverview();

console.log(`MRR: $${overview.mrr}`);
console.log(`Total Revenue (30d): $${overview.total_revenue_30d}`);
console.log(`Active Subscriptions: ${overview.active_subscriptions}`);
console.log(`Success Rate: ${overview.payment_success_rate}%`);

Monthly Recurring Revenue

const mrr = await subloop.analytics.getMRR();

console.log(`Current MRR: $${mrr.mrr}`);
console.log(`Previous MRR: $${mrr.previous_mrr}`);
console.log(`Growth: ${mrr.growth_percentage}%`);

Revenue Analytics

// Get revenue for different periods
const revenue30d = await subloop.analytics.getRevenue('30days');
const revenue7d = await subloop.analytics.getRevenueLast7Days();
const revenue90d = await subloop.analytics.getRevenueLast90Days();

// Custom date range
const customRevenue = await subloop.analytics.getRevenueForDateRange(
    '2025-06-01',
    '2025-07-31'
);

Payment Activity

// Get recent payments
const recentPayments = await subloop.analytics.getPayments(10);

// Get only successful payments
const successfulPayments = await subloop.analytics.getSuccessfulPayments(20);

// Get only recurring payments
const recurringPayments = await subloop.analytics.getRecurringPayments(15);

// Filter by status
const failedPayments = await subloop.analytics.getFailedPayments(10);

Integration Examples

Express.js Webhook Handler

const express = require('express');
const SubLoop = require('subloop-js');

const app = express();
const subloop = new SubLoop(process.env.SUBLOOP_API_KEY);

app.use(express.json());

// Stripe webhook handler
app.post('/webhooks/stripe', async (req, res) => {
    const event = req.body;

    if (event.type === 'payment_intent.succeeded') {
        const paymentIntent = event.data.object;

        try {
            await subloop.payments.createSuccessful(
                paymentIntent.customer,
                paymentIntent.amount_received / 100, // Convert from cents
                paymentIntent.currency.toUpperCase(),
                paymentIntent.metadata.subscription_id,
                {
                    stripe_payment_intent_id: paymentIntent.id,
                    stripe_charge_id: paymentIntent.charges.data[0]?.id
                }
            );

            console.log('Payment tracked in SubLoop');
        } catch (error) {
            console.error('SubLoop error:', error.message);
        }
    }

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

app.listen(3000);

Next.js API Route

// pages/api/webhooks/payment.js
import SubLoop from 'subloop-js';

const subloop = new SubLoop(process.env.SUBLOOP_API_KEY);

export default async function handler(req, res) {
    if (req.method !== 'POST') {
        return res.status(405).json({ error: 'Method not allowed' });
    }

    try {
        const { customerId, amount, currency, subscriptionId } = req.body;

        const payment = await subloop.payments.createSuccessful(
            customerId,
            amount,
            currency,
            subscriptionId
        );

        res.status(200).json({ success: true, payment });
    } catch (error) {
        console.error('SubLoop error:', error);
        res.status(500).json({ error: 'Failed to track payment' });
    }
}

React Dashboard Component

import React, { useState, useEffect } from 'react';
import SubLoop from 'subloop-js';

const subloop = new SubLoop(process.env.REACT_APP_SUBLOOP_API_KEY);

function Dashboard() {
    const [analytics, setAnalytics] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        async function fetchAnalytics() {
            try {
                const [overview, mrr, recentPayments] = await Promise.all([
                    subloop.analytics.getOverview(),
                    subloop.analytics.getMRR(),
                    subloop.analytics.getPayments(5)
                ]);

                setAnalytics({ overview, mrr, recentPayments });
            } catch (error) {
                console.error('Failed to fetch analytics:', error);
            } finally {
                setLoading(false);
            }
        }

        fetchAnalytics();
    }, []);

    if (loading) return <div>Loading...</div>;

    return (
        <div className="dashboard">
            <h1>Revenue Analytics</h1>

            <div className="metrics">
                <div className="metric">
                    <h3>MRR</h3>
                    <p>${analytics.overview.mrr}</p>
                </div>

                <div className="metric">
                    <h3>Total Revenue (30d)</h3>
                    <p>${analytics.overview.total_revenue_30d}</p>
                </div>

                <div className="metric">
                    <h3>Active Subscriptions</h3>
                    <p>{analytics.overview.active_subscriptions}</p>
                </div>
            </div>

            <div className="recent-payments">
                <h3>Recent Payments</h3>
                {analytics.recentPayments.data.map(payment => (
                    <div key={payment.id} className="payment">
                        <span>${payment.amount}</span>
                        <span>{payment.status}</span>
                        <span>{payment.customer_id}</span>
                    </div>
                ))}
            </div>
        </div>
    );
}

export default Dashboard;

Browser Usage (CDN)

<!DOCTYPE html>
<html>
<head>
    <title>SubLoop Analytics</title>
</head>
<body>
    <div id="analytics"></div>

    <script src="https://unpkg.com/[email protected]/dist/subloop.min.js"></script>
    <script>
        const subloop = new SubLoop('sk_your_api_key_here');

        async function loadAnalytics() {
            try {
                const overview = await subloop.analytics.getOverview();

                document.getElementById('analytics').innerHTML = `
                    <h2>Revenue Overview</h2>
                    <p>MRR: $${overview.mrr}</p>
                    <p>Total Revenue: $${overview.total_revenue_30d}</p>
                    <p>Active Subscriptions: ${overview.active_subscriptions}</p>
                `;
            } catch (error) {
                console.error('Error loading analytics:', error);
            }
        }

        loadAnalytics();
    </script>
</body>
</html>

Error Handling

const { SubLoopError } = require('subloop-js');

try {
    const payment = await subloop.payments.create({
        customer_id: 'cust_001',
        amount: 29.99,
        currency: 'USD',
        status: 'succeeded',
        payment_date: '2025-07-31T00:00:00Z'
    });
} catch (error) {
    if (error instanceof SubLoopError) {
        console.log('SubLoop Error:', error.message);
        console.log('Status Code:', error.getStatusCode());

        if (error.isValidationError()) {
            console.log('Validation errors:', error.getDetails());
        } else if (error.isAuthenticationError()) {
            console.log('Authentication failed - check your API key');
        } else if (error.isRateLimitError()) {
            console.log('Rate limit exceeded - please try again later');
        }
    } else {
        console.log('General Error:', error.message);
    }
}

TypeScript Support

The SDK includes TypeScript definitions:

import SubLoop from 'subloop-js';

interface PaymentData {
    customer_id: string;
    amount: number;
    currency: string;
    status: 'succeeded' | 'failed' | 'pending' | 'refunded';
    payment_date: string;
    subscription_id?: string;
    metadata?: Record<string, any>;
}

const subloop = new SubLoop('sk_your_api_key_here');

const payment: PaymentData = await subloop.payments.create({
    customer_id: 'cust_001',
    amount: 29.99,
    currency: 'USD',
    status: 'succeeded',
    payment_date: new Date().toISOString()
});

Requirements

  • Node.js 14.0.0 or higher
  • Modern browser with fetch support

Support

License

MIT License. See LICENSE for more information.