Typescripts SDK

Type-safe SDK for the BagelPay API – manage SaaS subscriptions, products, and revenue in TypeScript/Node.js environments.

The BagelPay Typescript SDK is available as an npm for seamless integration with your projects.

The SDK is also available on GitHub for those who prefer to explore the source code directly.

The API document is here.

📦 Installation Guide

npm install bagelpay

🚀 Quick Start

30-Second Quick Demo

import { BagelPayClient } from 'bagelpay';

async function main() {
  // Initialize the client
  const client = new BagelPayClient({
    apiKey: 'your-api-key'
  });

  try {
    // Create a product
    const product = await client.createProduct({
      name: `Premium Subscription ${Date.now()}`,
      description: 'Monthly premium subscription with unique identifier',
      price: 29.99,
      currency: 'USD',
      billingType: 'subscription',
      taxInclusive: false,
      taxCategory: 'digital_products',
      recurringInterval: 'monthly',
      trialDays: 7
    });

    console.log('✅ Product url:', product.productUrl);
  } catch (error) {
    console.error('❌ Error:', error);
  }
}

// Run the main function
main().catch(console.error);

Core Features

🛍️ Product Management

  • Create and manage products

  • Support for both subscriptions and one-time payments

  • Flexible pricing and billing intervals

  • Tax configuration options

💳 Checkout & Payments

  • Secure checkout session creation

  • Customizable success/cancel URLs

  • Metadata support for tracking

  • Real-time payment status

👥 Customer Management

  • Customer creation and retrieval

  • Subscription management

  • Payment history tracking

📊 Analytics & Reporting

  • Transaction listing and filtering

  • Subscription analytics

  • Customer insights

API Reference

Client Initialization

const client = new BagelPayClient({
  apiKey: string,        // Your BagelPay API key
  testMode?: boolean,    // Default: true
  timeout?: number,      // Default: 30000ms
  baseUrl?: string       // Default: https://test.bagelpay.io
});

Products

Create Product

const product = await client.createProduct({
  name: string,
  description: string,
  price: number,
  currency: string,
  billingType: 'subscription' | 'single_payment',
  taxInclusive: boolean,
  taxCategory: string,
  recurringInterval: 'daily' | 'weekly' | 'monthly' | '3months' | '6months',
  trialDays: number
});

List Products

const products = await client.listProducts(page, limit);

Get Product

const product = await client.getProduct(productId);

Checkout

Create Checkout Session

const checkout = await client.createCheckout({
  productId: string,
  requestId?: string,
  units?: string,
  customer?: { email: string },
  successUrl?: string,
  metadata?: Record<string, any>
});

Transactions

List Transactions

const transactions = await client.listTransactions(page, limit);

Subscriptions

List Subscriptions

const subscriptions = await client.listSubscriptions(page, limit);

Customers

List Customers

const customers = await client.listCustomers(page, limit);

Error Handling

The SDK provides specific error types for better error handling:

import { BagelPayAPIError, BagelPayError } from 'bagelpay';

try {
  const result = await client.createProduct(productData);
} catch (error) {
  if (error instanceof BagelPayAPIError) {
    console.error('API Error:', error.message);
    console.error('Status Code:', error.statusCode);
  } else if (error instanceof BagelPayError) {
    console.error('SDK Error:', error.message);
  } else {
    console.error('Unknown Error:', error);
  }
}

TypeScript Support

The SDK is written in TypeScript and provides full type definitions out of the box. No additional @types packages are required.

// All types are automatically inferred
const product: Product = await client.createProduct(productData);
const checkout: CheckoutResponse = await client.createCheckout(checkoutData);

Environment Configuration

Test Mode

Use test mode for development and testing:

const client = new BagelPayClient({
  apiKey: 'bagel_test_your_test_key',
  testMode: true
});

Production Mode

For production environments:

const client = new BagelPayClient({
  apiKey: 'bagel_live_your_live_key',
  testMode: false
});

🚀 Webhook Integration

import express from 'express';
import crypto from 'crypto';
import ngrok from 'ngrok';

const app = express();
const WEBHOOK_SECRET = 'your_webhook_key';

// Middleware to parse raw body for signature verification
app.use('/api/webhooks', express.raw({ type: 'application/json' }));

function verifyWebhookSignature(signatureData: Buffer, signature: string, secret: string): boolean {
  /**
   * Verify webhook signature for security
   */
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signatureData)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature, 'hex'),
    Buffer.from(signature, 'hex')
  );
}

app.post('/api/webhooks', async (req, res) => {
  /**
   * Handle BagelPay webhook notifications
   */
  const payload = req.body;
  const timestamp = req.headers['timestamp'] as string;
  const signature = req.headers['Bagelpay-Signature'] as string;
  
  // Combine payload and timestamp
  const signatureData = Buffer.concat([
    Buffer.from(timestamp, 'utf8'),
    Buffer.from('.', 'utf8'),
    payload
  ]);

  if (!verifyWebhookSignature(signatureData, signature, WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  try {
    const event = JSON.parse(payload.toString());
    const eventType = event.event_type;
    const data = event.object;

    switch (eventType) {
      case 'checkout.completed':
        // Handle checkout completed events
        console.log('Checkout completed:', event);
        break;
      
      case 'checkout.failed':
        // Handle checkout failed events
        console.log('Checkout failed:', event);
        break;
      
      case 'checkout.cancel':
        // Handle checkout cancelled events
        console.log('Checkout cancelled:', event);
        break;
      
      case 'subscription.trialing':
        // Handle subscription trialing events
        console.log('Subscription trialing:', event);
        break;
      
      case 'subscription.paid':
        // Handle subscription paid events
        console.log('Subscription paid:', event);
        break;
      
      case 'subscription.canceled':
        // Handle subscription cancelled events
        console.log('Subscription cancelled:', event);
        break;
      
      case 'refund.created':
        // Handle refund created events
        console.log('Refund created:', event);
        break;
      
      default:
        console.log(`Unhandled event type: ${eventType}`);
    }

    return res.status(200).json({ message: 'Success' });
  } catch (error) {
    console.error('Webhook processing error:', error);
    return res.status(500).json({ error: 'Processing failed' });
  }
});

// Start server with ngrok tunnel
async function startServer() {
  const listeningPort = 8000;
  
  try {
    // Set your ngrok auth token
    await ngrok.authtoken('your_ngrok_key');
    
    const publicUrl = await ngrok.connect({
      addr: listeningPort,
      proto: 'http',
      subdomain: 'stunning-crane-direct' // Optional: use your reserved subdomain
    });
    
    console.log(`ngrok Public URL: ${publicUrl}`);
    
    app.listen(listeningPort, '0.0.0.0', () => {
      console.log(`Server running on port ${listeningPort}`);
    });
  } catch (error) {
    console.error('Failed to start server:', error);
  }
}

if (require.main === module) {
  startServer();
}

Support

License

MIT License - see LICENSE file for details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Last updated