@streamsdk/express
v1.0.2
Published
Express.js adapter for Stream SDK - Declarative handlers for checkout and webhooks
Maintainers
Readme
streamsdk-express
Express.js adapter for Stream SDK - Declarative handlers for checkout and webhooks
📚 Table of Contents
- Overview
- Installation
- Quick Start
- Features
- Usage
- Configuration
- Examples
- Best Practices
- TypeScript Support
- API Reference
- Contributing
- Support
- License
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/expressOr install from GitHub:
npm install github:streampayments/streamsdk-express#v1.0.0Note: 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%20AliFeatures
- 🚀 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%20AhmadMultiple Products:
/checkout?products=prod_123,prod_456&customerId=cons_789With Metadata:
/checkout?products=prod_123&metadata=%7B%22orderId%22%3A%22ORD-123%22%7DWebhook 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/streamConfiguration
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:
- Quick Start - Basic setup
- Checkout Handler - Payment link creation
- Webhook Handler - Event processing
- Advanced Usage - Custom configurations
- Express Example Repository - Sample implementation
Best Practices
- Always use HTTPS in production for webhook endpoints
- Validate webhook signatures using the
webhookSecretoption - Handle webhook failures gracefully with retry logic
- Use idempotency keys in your webhook handlers to prevent duplicate processing
- Log all webhook events for debugging and audit purposes
- 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 testSupport
Documentation
Help & Issues
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
License
MIT License - see LICENSE for details.
