npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@streamsdk/express

v1.0.2

Published

Express.js adapter for Stream SDK - Declarative handlers for checkout and webhooks

Readme

streamsdk-express

Express.js adapter for Stream SDK - Declarative handlers for checkout and webhooks

npm version TypeScript License


📚 Table of Contents


Overview

The Stream SDK Express adapter provides a clean, declarative way to integrate Stream payments into your Express.js applications. Built on top of @streamsdk/typescript, it offers drop-in handlers for checkout flows and webhook processing.

Key Benefits:

  • 🚀 Simple Integration - Add payment processing in minutes
  • 🔒 Type-Safe - Full TypeScript support with type definitions
  • 🎯 Event-Driven - Clean event handlers for webhook processing
  • 🔄 Flexible - Supports guest and authenticated checkout flows
  • 📦 Lightweight - Minimal dependencies (~7KB)
  • 🔗 Auto-Updates - Inherits stream-sdk updates automatically

Installation

npm install @streamsdk/express

Or install from GitHub:

npm install github:streampayments/streamsdk-express#v1.0.0

Note: This package requires @streamsdk/typescript as a dependency, which will be installed automatically.


Quick Start

import express from "express";
import { Checkout, Webhooks } from "@streamsdk/express";

const app = express();
app.use(express.json());

// Checkout handler
app.get(
  "/checkout",
  Checkout({
    apiKey: process.env.STREAM_API_KEY!,
    successUrl: "https://myapp.com/success",
    returnUrl: "https://myapp.com/cancel",
  })
);

// Webhook handler
app.post(
  "/webhooks/stream",
  Webhooks({
    apiKey: process.env.STREAM_API_KEY!,
    onPaymentSucceeded: async (data) => {
      console.log("Payment succeeded:", data);
      // Update database, send emails, etc.
    },
  })
);

app.listen(3000);

Usage:

/checkout?products=prod_123&customerPhone=%2B966501234567&customerName=Ahmad%20Ali

Features

  • 🚀 Drop-in Handlers - Simple Express middleware
  • 🔒 Secure - HMAC-SHA256 webhook signature verification
  • 🎯 Event Routing - Specific handlers for each event type
  • 🔄 Auto Resource Management - Consumer and product creation/lookup
  • 📝 Full TypeScript - Complete type definitions
  • Fast - Lightweight with minimal overhead
  • 🛡️ Production Ready - Error handling and validation built-in

Usage

Checkout Handler

The Checkout handler creates payment links and redirects users to the Stream checkout page.

Basic Example

import { Checkout } from "@streamsdk/express";

app.get(
  "/checkout",
  Checkout({
    apiKey: process.env.STREAM_API_KEY!,
    successUrl: "https://myapp.com/payment/success",
    returnUrl: "https://myapp.com/payment/cancelled",
    defaultName: "My Store Checkout",
  })
);

Query Parameters

| Parameter | Type | Required | Description | | --------------- | ------ | -------- | ---------------------------------------------------- | | products | string | Yes | Product ID(s), comma-separated for multiple | | name | string | No | Custom name for payment link (overrides defaultName) | | customerId | string | No | Existing customer/consumer ID | | customerEmail | string | No | Customer email for new customers | | customerName | string | No | Customer name for new customers | | customerPhone | string | No | Customer phone for new customers | | metadata | string | No | URL-encoded JSON metadata |

Usage Examples

Single Product with New Customer:

/checkout?products=prod_123&customerPhone=%2B966501234567&customerName=Mohammad%20Ahmad

Multiple Products:

/checkout?products=prod_123,prod_456&customerId=cons_789

With Metadata:

/checkout?products=prod_123&metadata=%7B%22orderId%22%3A%22ORD-123%22%7D

Webhook Handler

The Webhooks handler processes webhook events from Stream.

Basic Example

import { Webhooks } from "@streamsdk/express";

app.post(
  "/webhooks/stream",
  Webhooks({
    apiKey: process.env.STREAM_API_KEY!,
    webhookSecret: process.env.STREAM_WEBHOOK_SECRET,

    onPaymentSucceeded: async (data) => {
      // Update your database
      await db.orders.update({
        where: { paymentId: data.id },
        data: { status: "paid", paidAt: new Date() },
      });

      // Send confirmation email
      await sendEmail({
        to: data.customer_email,
        subject: "Payment Confirmed",
        template: "payment-confirmation",
        data: { amount: data.amount, orderId: data.metadata?.orderId },
      });
    },

    onPaymentFailed: async (data) => {
      console.log("Payment failed:", data.failure_reason);
      // Handle failed payment
    },
  })
);

Supported Events

| Event | Handler | Description | | -------------------------- | --------------------------- | ------------------------------ | | PAYMENT_SUCCEEDED | onPaymentSucceeded | Payment successfully processed | | PAYMENT_FAILED | onPaymentFailed | Payment failed | | PAYMENT_CANCELED | onPaymentCanceled | Payment cancelled | | PAYMENT_REFUNDED | onPaymentRefunded | Payment refunded | | PAYMENT_MARKED_AS_PAID | onPaymentMarkedAsPaid | Payment marked as paid | | INVOICE_CREATED | onInvoiceCreated | Invoice generated | | INVOICE_SENT | onInvoiceSent | Invoice sent to customer | | INVOICE_ACCEPTED | onInvoiceAccepted | Invoice accepted | | INVOICE_REJECTED | onInvoiceRejected | Invoice rejected | | INVOICE_COMPLETED | onInvoiceCompleted | Invoice completed | | INVOICE_CANCELED | onInvoiceCanceled | Invoice cancelled | | INVOICE_UPDATED | onInvoiceUpdated | Invoice updated | | SUBSCRIPTION_CREATED | onSubscriptionCreated | New subscription created | | SUBSCRIPTION_UPDATED | onSubscriptionUpdated | Subscription modified | | SUBSCRIPTION_CANCELED | onSubscriptionCanceled | Subscription cancelled |

Catch-All Handler

Use onWebhook to handle all events:

app.post(
  "/webhooks/stream",
  Webhooks({
    apiKey: process.env.STREAM_API_KEY!,

    onWebhook: async (event, data) => {
      console.log(`Webhook: ${event}`, data);
      await logWebhookEvent(event, data);
    },
  })
);

Advanced Usage

Custom Error Handling

app.get(
  "/checkout",
  Checkout({
    apiKey: process.env.STREAM_API_KEY!,
    successUrl: "https://myapp.com/success",
    returnUrl: "https://myapp.com/cancel",
  })
);

// Add error handler after checkout route
app.use((error, req, res, next) => {
  console.error("Checkout error:", error);
  res.status(500).json({
    error: "Failed to create checkout session",
    message: error.message,
  });
});

Dynamic Configuration

app.get(
  "/checkout/:tier",
  (req, res, next) => {
    const { tier } = req.params;

    // Get product ID based on tier
    const productId = getProductIdForTier(tier);

    // Add product to query
    req.query.products = productId;

    next();
  },
  Checkout({
    apiKey: process.env.STREAM_API_KEY!,
    successUrl: `https://myapp.com/success?tier=${req.params.tier}`,
    returnUrl: "https://myapp.com/cancel",
  })
);

Testing Webhooks Locally

Use ngrok to expose your local webhook endpoint:

# Start ngrok
ngrok http 3000

# Update your Stream webhook URL to:
# https://your-ngrok-url.ngrok.io/webhooks/stream

Configuration

CheckoutConfig

interface CheckoutConfig {
  apiKey: string; // Stream API key (required)
  successUrl: string; // Redirect URL after successful payment (required)
  returnUrl?: string; // Redirect URL on cancellation (optional)
  baseUrl?: string; // Custom Stream API base URL (optional)
  defaultName?: string; // Default name for payment links (optional)
}

WebhookConfig

interface WebhookConfig {
  apiKey: string;
  webhookSecret?: string; // For signature verification (recommended)

  // Payment event handlers
  onPaymentSucceeded?: (data: any) => void | Promise<void>;
  onPaymentFailed?: (data: any) => void | Promise<void>;
  onPaymentCanceled?: (data: any) => void | Promise<void>;
  onPaymentRefunded?: (data: any) => void | Promise<void>;
  onPaymentMarkedAsPaid?: (data: any) => void | Promise<void>;

  // Invoice event handlers
  onInvoiceCreated?: (data: any) => void | Promise<void>;
  onInvoiceSent?: (data: any) => void | Promise<void>;
  onInvoiceAccepted?: (data: any) => void | Promise<void>;
  onInvoiceRejected?: (data: any) => void | Promise<void>;
  onInvoiceCompleted?: (data: any) => void | Promise<void>;
  onInvoiceCanceled?: (data: any) => void | Promise<void>;
  onInvoiceUpdated?: (data: any) => void | Promise<void>;

  // Subscription event handlers
  onSubscriptionCreated?: (data: any) => void | Promise<void>;
  onSubscriptionUpdated?: (data: any) => void | Promise<void>;
  onSubscriptionCanceled?: (data: any) => void | Promise<void>;

  // Catch-all handler
  onWebhook?: (event: string, data: any) => void | Promise<void>;
}

Examples

See the usage examples throughout this README for common patterns:


Best Practices

  1. Always use HTTPS in production for webhook endpoints
  2. Validate webhook signatures using the webhookSecret option
  3. Handle webhook failures gracefully with retry logic
  4. Use idempotency keys in your webhook handlers to prevent duplicate processing
  5. Log all webhook events for debugging and audit purposes
  6. Return 200 OK quickly from webhook handlers (process async operations in background)

TypeScript Support

The adapter is fully typed and exports all necessary types:

import type {
  CheckoutConfig,
  CheckoutQuery,
  CheckoutRequest,
  WebhookConfig,
  WebhookPayload,
} from "@streamsdk/typescript";

API Reference

Checkout(config: CheckoutConfig)

Creates an Express middleware that handles checkout requests.

Returns: Express middleware function

Webhooks(config: WebhookConfig)

Creates an Express middleware that processes webhook events.

Returns: Express middleware function


Contributing

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

Development Setup

# Clone the repository
git clone https://github.com/streampayments/streamsdk-express.git
cd streamsdk-express

# Install dependencies
npm install

# Build the adapter
npm run build

# Run tests
npm test

Support

Documentation

Help & Issues


License

MIT License - see LICENSE for details.