
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
stream402-sdk
Advanced tools
A TypeScript SDK for integrating Stream402 payment protocol into your applications. Stream402 enables monetization of digital content using Solana payments via the x402 protocol.
npm install @stream402/sdk
Check if an asset requires payment:
import { discover } from "@stream402/sdk";
const result = await discover("https://example.com/api/asset/123");
if (result.type === "free") {
// Asset is free, use the URL directly
console.log("Free asset:", result.url);
} else {
// Payment required
console.log("Payment required:", result.challenge);
console.log("Amount:", result.challenge.amount, result.challenge.currency);
}
Complete payment flow and get access to the resource:
import { payAndFetch } from "@stream402/sdk";
import { useWallet } from "@solana/wallet-adapter-react";
function MyComponent() {
const wallet = useWallet();
const handlePayAndFetch = async () => {
try {
const result = await payAndFetch(
"https://example.com/api/asset/123",
wallet
);
console.log("Download URL:", result.downloadUrl);
console.log("Expires at:", new Date(result.expiresAt));
// Use the download URL to access the resource
window.open(result.downloadUrl, "_blank");
} catch (error) {
console.error("Payment failed:", error);
}
};
return <button onClick={handlePayAndFetch}>Pay & Download</button>;
}
Upload an asset as a content provider:
import { uploadAsset } from "@stream402/sdk";
import { useWallet } from "@solana/wallet-adapter-react";
function UploadComponent() {
const wallet = useWallet();
const handleUpload = async (file: File) => {
try {
const result = await uploadAsset(
file,
{
title: "My Image",
price: 0.01, // USDC
tags: ["nature", "landscape"],
description: "A beautiful landscape image",
},
wallet
);
console.log("Asset uploaded:", result.assetId);
console.log("Asset URL:", result.url);
} catch (error) {
console.error("Upload failed:", error);
}
};
return (
<input
type="file"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) handleUpload(file);
}}
/>
);
}
discover(assetUrl: string): Promise<DiscoverResult>Fetches an asset URL and returns either free access or a payment challenge.
Parameters:
assetUrl - Full URL to the asset (e.g., "https://example.com/api/asset/123")Returns:
{ type: "free", url: string } - Asset is free to access{ type: "payment_required", challenge: PaymentChallenge } - Payment requiredExample:
const result = await discover("https://example.com/api/asset/123");
payAndFetch(assetUrl: string, walletAdapter: WalletAdapter, connection?: Connection): Promise<PaymentResult>Completes the full payment flow and returns the resource.
Parameters:
assetUrl - Full URL to the assetwalletAdapter - Solana wallet adapter from @solana/wallet-adapter-reactconnection - Optional Solana connection (defaults to devnet/mainnet based on challenge)Returns:
{ accessToken: string, downloadUrl: string, expiresAt: number }Example:
const result = await payAndFetch(assetUrl, wallet);
// Use result.downloadUrl to access the resource
uploadAsset(file: File, meta: AssetMetadata, walletAdapter?: WalletAdapter, baseUrl?: string): Promise<UploadResponse>Uploads an asset to Stream402.
Parameters:
file - File to uploadmeta - Asset metadata:
title: string - Asset titleprice: number | string - Price in USDCrecipient?: string - Recipient wallet address (optional, uses wallet if not provided)tags?: string[] - Tags for searchabilitydescription?: string - Asset descriptionwalletAdapter - Optional wallet adapter (used for recipient if not provided)baseUrl - Optional base URL of Stream402 API (defaults to current origin)Returns:
{ assetId: string, title: string, price: number, url: string, thumbnailUrl?: string }Example:
const result = await uploadAsset(file, {
title: "My Image",
price: 0.01,
tags: ["nature"],
}, wallet);
PaymentChallengeinterface PaymentChallenge {
version: string;
network: string;
currency: string;
decimals: number;
amount: number;
mint: string;
recipient: string;
expiresAt: number;
assetId: string;
paymentRequestToken: string;
description?: string;
metadata?: Record<string, unknown>;
}
PaymentResultinterface PaymentResult {
accessToken: string;
downloadUrl: string;
expiresAt: number;
}
AssetMetadatainterface AssetMetadata {
title: string;
price: number | string;
recipient?: string;
tags?: string[];
description?: string;
}
import React, { useState } from "react";
import { useWallet } from "@solana/wallet-adapter-react";
import { discover, payAndFetch } from "@stream402/sdk";
function AssetViewer({ assetUrl }: { assetUrl: string }) {
const wallet = useWallet();
const [status, setStatus] = useState<"loading" | "free" | "payment_required" | "paid">("loading");
const [downloadUrl, setDownloadUrl] = useState<string>("");
useEffect(() => {
checkAsset();
}, [assetUrl]);
const checkAsset = async () => {
try {
const result = await discover(assetUrl);
if (result.type === "free") {
setStatus("free");
setDownloadUrl(result.url);
} else {
setStatus("payment_required");
}
} catch (error) {
console.error("Failed to discover asset:", error);
}
};
const handlePay = async () => {
if (!wallet.connected) {
await wallet.connect();
}
try {
const result = await payAndFetch(assetUrl, wallet);
setStatus("paid");
setDownloadUrl(result.downloadUrl);
} catch (error) {
console.error("Payment failed:", error);
}
};
return (
<div>
{status === "loading" && <p>Loading...</p>}
{status === "free" && <a href={downloadUrl}>Download</a>}
{status === "payment_required" && (
<button onClick={handlePay}>Pay to Access</button>
)}
{status === "paid" && <a href={downloadUrl}>Download</a>}
</div>
);
}
import { discover, payAndFetch } from "@stream402/sdk";
import { Connection, Keypair } from "@solana/web3.js";
import { NodeWalletAdapter } from "@solana/wallet-adapter-wallets";
// Create a wallet adapter from a keypair
const keypair = Keypair.fromSecretKey(/* your secret key */);
const wallet = new NodeWalletAdapter(keypair);
const connection = new Connection("https://api.devnet.solana.com");
// Discover and pay
const result = await discover("https://example.com/api/asset/123");
if (result.type === "payment_required") {
const paymentResult = await payAndFetch(
"https://example.com/api/asset/123",
wallet,
connection
);
console.log("Download URL:", paymentResult.downloadUrl);
}
MIT
For issues and questions, please visit Stream402 GitHub.
FAQs
Stream402 SDK - Integrate x402 payment protocol into your applications
We found that stream402-sdk 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.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.