
Company News
Socket Joins New OpenJS Program to Fund Node.js Security Work
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.
WebSocket implementation of X402 protocol for pay-as-you-go digital resources with automatic refunds
WebSocket implementation of X402 protocol for pay-as-you-go digital resources with automatic refunds
WS402 extends the X402 protocol to WebSocket connections, enabling true pay-as-you-go pricing for streaming resources. Users pay upfront for estimated usage, the backend tracks actual consumption in real-time, and unused balance is automatically refunded when the session ends.
Perfect for:
npm install ws402
Additional dependencies for blockchain providers:
# For Base blockchain
npm install ethers
# For Solana blockchain
npm install @solana/web3.js @solana/pay bignumber.js
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const { WS402, MockPaymentProvider } = require('ws402');
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
// Initialize payment provider (mock for development)
const paymentProvider = new MockPaymentProvider();
// Create WS402 instance
const ws402 = new WS402(
{
updateInterval: 3000, // Send updates every 3 seconds
pricePerSecond: 10, // Price per second in wei
currency: 'wei',
maxSessionDuration: 600, // Max 10 minutes per session
onPaymentVerified: (session) => {
console.log(`✅ Payment verified: ${session.sessionId}`);
},
onRefundIssued: (session, refund) => {
console.log(`💰 Refund issued: ${refund.amount} wei`);
},
onSessionEnd: (session) => {
console.log(`🔚 Session ended: ${session.sessionId}`);
},
},
paymentProvider
);
// Attach to WebSocket server
ws402.attach(wss);
// Serve WS402 schema
app.get('/ws402/schema/:resourceId', (req, res) => {
const schema = ws402.generateSchema(
req.params.resourceId,
300 // estimated duration in seconds
);
res.json(schema);
});
server.listen(4028);
// 1. Fetch WS402 schema
const response = await fetch('/ws402/schema/my-resource?duration=300');
const schema = await response.json();
// 2. Make payment (using blockchain or other method)
// ... payment process ...
// 3. Connect to WebSocket
const ws = new WebSocket(`wss://your-server.com?userId=alice`);
// 4. Send payment proof
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'payment_proof',
proof: {
txHash: '0x123...', // Blockchain transaction hash
reference: schema.paymentDetails.reference,
senderAddress: '0xYourAddress...',
amount: schema.pricing.totalPrice,
}
}));
};
// 5. Receive usage updates
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'session_started') {
console.log('✅ Session active!', data.sessionId);
}
if (data.type === 'usage_update') {
console.log(`⏱️ Time: ${data.elapsedSeconds}s`);
console.log(`💰 Remaining: ${data.remainingBalance} wei`);
}
if (data.type === 'balance_exhausted') {
console.log('⚠️ Balance exhausted');
}
};
// 6. Disconnect to trigger automatic refund
ws.close();
WS402 supports multiple payment architectures:
const { WS402, BasePaymentProvider } = require('ws402');
const baseProvider = new BasePaymentProvider({
rpcEndpoint: 'https://mainnet.base.org',
merchantWallet: '0xYourWalletAddress',
merchantPrivateKey: process.env.MERCHANT_PRIVATE_KEY, // For automatic refunds
network: 'base', // 'base' | 'base-goerli' | 'base-sepolia'
autoRefund: true,
});
const ws402 = new WS402(config, baseProvider);
Features:
const { WS402, SolanaPaymentProvider } = require('ws402');
const solanaProvider = new SolanaPaymentProvider({
rpcEndpoint: 'https://api.mainnet-beta.solana.com',
merchantWallet: 'YourSolanaPublicKey',
network: 'mainnet-beta',
conversionRate: 1000000000, // wei to lamports
label: 'WS402 Payment',
message: 'Pay for WebSocket access',
});
const ws402 = new WS402(config, solanaProvider);
Features:
Perfect for scaling and security:
const { WS402, ProxyPaymentProvider } = require('ws402');
// WS402 Server (no private keys needed!)
const proxyProvider = new ProxyPaymentProvider({
gatewayUrl: 'https://payment-gateway.example.com',
apiKey: process.env.GATEWAY_API_KEY,
timeout: 30000,
retryAttempts: 3,
});
const ws402 = new WS402(config, proxyProvider);
Architecture:
[Client] → [WS402 Server A] ⎤
[Client] → [WS402 Server B] ⎥→ [Payment Gateway] → [Blockchain]
[Client] → [WS402 Server C] ⎦ (has private keys)
Benefits:
Gateway Server:
// Centralized gateway with private keys
node examples/payment-gateway-server.js
See: PROXY_ARCHITECTURE.md for details
Implement the PaymentProvider interface:
interface PaymentProvider {
generatePaymentDetails(amount: number): any;
verifyPayment(proof: any): Promise<PaymentVerification>;
issueRefund(proof: any, amount: number): Promise<void>;
}
Example:
class MyPaymentProvider {
async generatePaymentDetails(amount) {
return {
invoiceUrl: 'https://pay.me/invoice-123',
amount,
currency: 'USD',
};
}
async verifyPayment(proof) {
const isValid = await this.checkWithAPI(proof);
return {
valid: isValid,
amount: proof.amount,
};
}
async issueRefund(proof, amount) {
await this.refundAPI(proof, amount);
}
}
interface WS402Config {
updateInterval?: number; // Update frequency (ms) - default: 3000
pricePerSecond?: number; // Price per second - default: 1
currency?: string; // Currency unit - default: 'wei'
maxSessionDuration?: number; // Max session time (seconds) - default: 3600
userIdExtractor?: (req) => string; // Extract user ID from request
onPaymentVerified?: (session) => void;
onRefundIssued?: (session, refund) => void;
onSessionEnd?: (session) => void;
}
When a client requests a protected resource, return a WS402 schema:
{
"protocol": "ws402",
"version": "0.1.0",
"resourceId": "video-123",
"websocketEndpoint": "wss://api.example.com/ws402/video-123",
"pricing": {
"pricePerSecond": 10,
"currency": "wei",
"estimatedDuration": 300,
"totalPrice": 3000
},
"paymentDetails": {
"type": "base",
"network": "base",
"chainId": 8453,
"recipient": "0x...",
"amountETH": "0.000003",
"reference": "base_123_abc"
},
"maxSessionDuration": 600
}
Payment Proof
{
"type": "payment_proof",
"proof": {
"txHash": "0x123...",
"reference": "base_123_abc",
"senderAddress": "0xUser...",
"amount": 3000
}
}
Session Started
{
"type": "session_started",
"sessionId": "ws402_123_abc",
"balance": 3000,
"pricePerSecond": 10
}
Usage Update
{
"type": "usage_update",
"sessionId": "ws402_123_abc",
"elapsedSeconds": 45,
"consumedAmount": 450,
"remainingBalance": 2550,
"bytesTransferred": 1024000,
"messageCount": 15
}
Balance Exhausted
{
"type": "balance_exhausted",
"message": "Prepaid balance has been fully consumed"
}
Payment Rejected
{
"type": "payment_rejected",
"reason": "Invalid payment proof"
}
See the /examples directory for complete working examples:
basic-server.js - Mock payment provider for testingbase-server.js - Base blockchain integrationsolana-server.js - Solana blockchain integrationproxy-server.js - Proxy architecture with gatewaypayment-gateway-server.js - Centralized payment gatewaybase-client.html - Web client with MetaMask integrationsolana-client.html - Web client with Solana Payproxy-client.html - Web client for proxy architecture# Development with mock payments
npm run example
# Base blockchain
npm run example:base
# Solana blockchain
npm run example:solana
# Proxy architecture (run gateway first)
node examples/payment-gateway-server.js # Terminal 1
node examples/proxy-server.js # Terminal 2
Then open http://localhost:4028 in your browser.
1. Client requests resource → Server returns WS402 schema
2. Client makes payment → Blockchain transaction
3. Client connects via WebSocket → Sends payment proof
4. Server verifies payment on-chain → Starts session
5. Real-time metering → Server sends periodic updates
6. Client disconnects → Server calculates consumed amount
7. Automatic refund → Unused balance returned on-chain
| Feature | X402 | WS402 |
|---|---|---|
| Protocol | HTTP | WebSocket |
| Payment timing | Pay-per-request | Pay upfront, refund unused |
| Connection | Stateless | Stateful |
| Real-time updates | No | Yes |
| Refunds | No | Automatic |
| Use case | API endpoints | Streaming resources |
| Provider | Fees | Speed | Best For |
|---|---|---|---|
| Base | ~$0.01 | 2-5 sec | Ethereum users, DeFi apps |
| Solana | ~$0.0001 | <1 sec | High volume, low fees |
| Proxy | Provider dependent | Provider dependent | Enterprise, multi-server |
| Mock | Free | Instant | Development, testing |
WS402Main class for WS402 protocol implementation.
new WS402(config: WS402Config, paymentProvider: PaymentProvider)
attach(wss: WebSocket.Server) - Attach to WebSocket servergenerateSchema(resourceId, estimatedDuration) - Generate WS402 schemagetSessionByUserId(userId) - Get active session by user IDgetActiveSessions() - Get all active sessionssession_end - Emitted when session endsrefund - Emitted when refund is issuedrefund_error - Emitted when refund failserror - Emitted on errorsSee detailed documentation:
# Base Blockchain
BASE_RPC=https://mainnet.base.org
MERCHANT_WALLET=0xYourWalletAddress
MERCHANT_PRIVATE_KEY=your_private_key_here
# Solana Blockchain
SOLANA_RPC=https://api.mainnet-beta.solana.com
SOLANA_WALLET=YourSolanaPublicKey
# Proxy Gateway
GATEWAY_URL=https://payment-gateway.example.com
GATEWAY_API_KEY=your-secret-api-key
# Server
PORT=4028
See .env.example for full configuration.
⚠️ CRITICAL: Never expose private keys in code or repositories!
// ❌ NEVER DO THIS
const provider = new BasePaymentProvider({
merchantPrivateKey: '0x123abc...'
});
// ✅ ALWAYS USE ENVIRONMENT VARIABLES
const provider = new BasePaymentProvider({
merchantPrivateKey: process.env.MERCHANT_PRIVATE_KEY
});
Best Practices:
See SECURITY.md for complete security guide.
[WS402 Server] → [Blockchain]
[Load Balancer]
↓
[WS402 Servers] → [Payment Gateway] → [Blockchain]
(private keys here)
See PROXY_ARCHITECTURE.md for deployment guide.
Contributions welcome! This is an open-source project.
git checkout -b feature/amazing)git commit -m 'Add amazing feature')git push origin feature/amazing)MIT License - see LICENSE file
Inspired by the X402 protocol and the need for fair, pay-as-you-go pricing for WebSocket resources.
Built with ❤️ for the open web and blockchain ecosystem
FAQs
WebSocket implementation of X402 protocol for pay-as-you-go digital resources with automatic refunds
The npm package ws402 receives a total of 0 weekly downloads. As such, ws402 popularity was classified as not popular.
We found that ws402 demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.