@ziongateway/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Zion SDK
2
+
3
+ The **Zion SDK** allows AI Agents and developers to easily monetize their APIs using the **Zion X-402 Facilitator**. It handles the **HTTP 402 Payment Required** protocol, standardizing payment requests, verification, and settlement on the Solana blockchain.
4
+
5
+ ## Features
6
+ - 🛡️ **Middleware Protection**: Automatically blocks unpaid requests with a `402` status.
7
+ - 💰 **Flexible Pricing**: Set prices in USDC (or other assets).
8
+ - ⚡ **Instant Settlement**: Verifies and settles payments via the Facilitator.
9
+ - 🔗 **Framework Agnostic**: Works with Express, Next.js, NestJS, etc.
10
+
11
+ ---
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @zion/sdk
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Usage (Server-Side)
22
+
23
+ ### 1. Express Middleware
24
+ Protect your API routes by adding `ZionGate` middleware.
25
+
26
+ ```typescript
27
+ import express from "express";
28
+ import { ZionGate } from "@zion/sdk";
29
+
30
+ const app = express();
31
+
32
+ // Initialize Zion Gate
33
+ const zion = new ZionGate({
34
+ apiKey: "YOUR_DEVELOPER_API_KEY", // Get this from Zion Dashboard
35
+ price: 0.1, // Price per request in USDC
36
+ network: "solana-devnet", // or "solana-mainnet"
37
+ facilitatorUrl: "https://your-facilitator-url.com" // Optional if using public Zion Cloud
38
+ resource: "ai-agent-api" // Optional, default: "api-access"
39
+ });
40
+
41
+ // Protect a specific route
42
+ app.get("/ai-agent-api", zion.express(), (req, res) => {
43
+ res.json({
44
+ success: true,
45
+ message: "You have paid for this content! 🚀",
46
+ data: "SECRET_DATA"
47
+ });
48
+ });
49
+
50
+ app.listen(3000, () => console.log("Server running on port 3000"));
51
+ ```
52
+
53
+ ### 2. Next.js (App Router)
54
+ Use the `nextServer` helper in your route handlers.
55
+
56
+ ```typescript
57
+ import { ZionGate } from "@zion/sdk";
58
+
59
+ const zion = new ZionGate({ /* config */ });
60
+
61
+ export async function GET(req: Request) {
62
+ const authorized = await zion.nextServer(req);
63
+
64
+ // If not authorized, 'authorized' is a 402 Response object. Return it.
65
+ if (authorized !== true) return authorized;
66
+
67
+ return Response.json({ success: true, data: "Paid Content" });
68
+ }
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Usage (Client-Side)
74
+
75
+ When a client receives a `402 Payment Required` response, they must perform the payment on-chain and send the proof back in the `Authorization` header.
76
+
77
+ ### 1. Handling the 402 Response
78
+ The server returns payment requirements:
79
+
80
+ ```json
81
+ {
82
+ "recipient": "FjK...", // Developer's Wallet
83
+ "amount": "100000", // Atomic units (0.1 USDC)
84
+ "asset": "EPj...", // USDC Mint Address
85
+ "treasury": "AgH...", // Zion Treasury (for 1% fee)
86
+ "message": "Payment Required"
87
+ }
88
+ ```
89
+
90
+ ### 2. Creating the Payment Header
91
+ Use `PaymentBuilder` to generate the correct header after signing the transaction.
92
+
93
+ ```typescript
94
+ import { PaymentBuilder } from "@zion/sdk";
95
+
96
+ // ... Perform Solana Transaction (Split: 99% to recipient, 1% to treasury) ...
97
+
98
+ // Create the Zion Payment Header
99
+ const header = PaymentBuilder.createHeader({
100
+ sender: "USER_WALLET_ADDRESS",
101
+ recipient: "DEVELOPER_WALLET_ADDRESS",
102
+ amount: "100000",
103
+ asset: "USDC_MINT_ADDRESS",
104
+ timestamp: Math.floor(Date.now() / 1000),
105
+ nonce: "RANDOM_STRING_GT_8_CHARS",
106
+ signature: "TRANSACTION_SIGNATURE", // From Solana Web3.js
107
+ network: "solana-devnet"
108
+ });
109
+
110
+ // Retry the request with the header
111
+ const response = await fetch("http://api.example.com/protected", {
112
+ headers: {
113
+ "Authorization": `Bearer ${header}`
114
+ }
115
+ });
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Webhooks
121
+
122
+ Webhooks allow your application to receive **asynchronous notifications** when payments are successfully settled.
123
+
124
+ ### Do I need Webhooks?
125
+ * **No**, if you only need to gate a synchronous API response (like the example above). The SDK handles verification immediately before serving the response.
126
+ * **Yes**, if you need to:
127
+ * Trigger a background job (e.g., generate a video, fine-tune a model).
128
+ * Update a user's credit balance in your database.
129
+ * Send a confirmation email.
130
+ * Handle payments that might confirm slowly.
131
+
132
+ ### Setting up Webhooks
133
+ 1. Register your Webhook URL in the **Facilitator Database** (table: `webhooks`).
134
+ 2. Listen for the `payment.success` event.
135
+
136
+ **Example Payload:**
137
+ ```json
138
+ {
139
+ "event": "payment.success",
140
+ "payload": {
141
+ "settlementId": "stl_12345",
142
+ "amount": "100000",
143
+ "netAmount": "99000",
144
+ "payer": "UserAddress...",
145
+ "resource": "api-access"
146
+ }
147
+ }
148
+ ```
@@ -0,0 +1,59 @@
1
+ export interface ZionConfig {
2
+ apiKey: string;
3
+ price: number;
4
+ network?: "solana-mainnet" | "solana-devnet";
5
+ facilitatorUrl?: string;
6
+ resource?: string;
7
+ }
8
+ export interface CheckResult {
9
+ authorized: boolean;
10
+ error?: {
11
+ status: number;
12
+ body: any;
13
+ };
14
+ success?: boolean;
15
+ verification?: VerifyResponse;
16
+ }
17
+ export interface VerifyResponse {
18
+ valid: boolean;
19
+ invalidReason?: {
20
+ code: string;
21
+ message: string;
22
+ };
23
+ payer?: string;
24
+ transaction?: {
25
+ signature: string;
26
+ confirmedAt: string;
27
+ };
28
+ }
29
+ export interface SettleResponse {
30
+ success: boolean;
31
+ error?: {
32
+ code: string;
33
+ message: string;
34
+ };
35
+ settlement?: {
36
+ id: string;
37
+ txSignature: string;
38
+ network: string;
39
+ grossAmount: string;
40
+ fee: string;
41
+ netAmount: string;
42
+ feePercent: string;
43
+ payer: string;
44
+ recipient: string;
45
+ settledAt: string;
46
+ };
47
+ }
48
+ export declare const NETWORKS: {
49
+ "solana-mainnet": {
50
+ treasury: string;
51
+ usdcMint: string;
52
+ facilitator: string;
53
+ };
54
+ "solana-devnet": {
55
+ treasury: string;
56
+ usdcMint: string;
57
+ facilitator: string;
58
+ };
59
+ };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NETWORKS = void 0;
4
+ exports.NETWORKS = {
5
+ "solana-mainnet": {
6
+ treasury: "AgH4Eq3JYBpEt5aPgCnZsygf9tnQ5D2qMKH3eJxB5rm8",
7
+ usdcMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
8
+ facilitator: "https://zion-x402-facilitator-production.up.railway.app/v1"
9
+ },
10
+ "solana-devnet": {
11
+ treasury: "AgH4Eq3JYBpEt5aPgCnZsygf9tnQ5D2qMKH3eJxB5rm8",
12
+ usdcMint: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
13
+ facilitator: "https://zion-x402-facilitator-production.up.railway.app/v1"
14
+ }
15
+ };
@@ -0,0 +1,35 @@
1
+ import { ZionConfig, CheckResult } from "./Universal";
2
+ export declare class ZionGate {
3
+ private static instance;
4
+ private config;
5
+ private api;
6
+ private developerWallet;
7
+ private isInitialized;
8
+ constructor(config: ZionConfig);
9
+ /**
10
+ * Initialize the SDK by fetching the developer's wallet address from the Facilitator.
11
+ * This is required because we don't want the developer to hardcode their receiving address,
12
+ * to ensure funds go to their specific embedded wallet or chosen payout address.
13
+ */
14
+ init(): Promise<void>;
15
+ /**
16
+ * Core check logic generic for any framework.
17
+ */
18
+ check(headers: Record<string, string | undefined>): Promise<CheckResult>;
19
+ private create402Response;
20
+ /**
21
+ * Express Middleware Adapter
22
+ */
23
+ express(): (req: any, res: any, next: any) => Promise<any>;
24
+ /**
25
+ * NextJS API Route / App Router Helper
26
+ * Usage:
27
+ * const zion = new ZionGate(...);
28
+ * export async function GET(req: Request) {
29
+ * const authorized = await zion.nextServer(req);
30
+ * if (authorized !== true) return authorized; // Returns Response object
31
+ * // ... logic
32
+ * }
33
+ */
34
+ nextServer(req: Request): Promise<true | Response>;
35
+ }
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ZionGate = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const Universal_1 = require("./Universal");
9
+ class ZionGate {
10
+ constructor(config) {
11
+ this.developerWallet = null;
12
+ this.isInitialized = false;
13
+ this.config = config;
14
+ const defaults = Universal_1.NETWORKS[config.network || "solana-mainnet"];
15
+ this.api = axios_1.default.create({
16
+ baseURL: config.facilitatorUrl || defaults.facilitator,
17
+ headers: {
18
+ "X-API-KEY": config.apiKey,
19
+ "Content-Type": "application/json"
20
+ }
21
+ });
22
+ }
23
+ /**
24
+ * Initialize the SDK by fetching the developer's wallet address from the Facilitator.
25
+ * This is required because we don't want the developer to hardcode their receiving address,
26
+ * to ensure funds go to their specific embedded wallet or chosen payout address.
27
+ */
28
+ async init() {
29
+ if (this.isInitialized)
30
+ return;
31
+ try {
32
+ const res = await this.api.get("/v1/wallet");
33
+ if (res.data && res.data.address) {
34
+ this.developerWallet = res.data.address;
35
+ this.isInitialized = true;
36
+ }
37
+ else {
38
+ throw new Error("Invalid response from Facilitator wallet endpoint");
39
+ }
40
+ }
41
+ catch (error) {
42
+ console.error("ZionGate Init Failed:", error.message);
43
+ throw new Error("Failed to initialize ZionGate: Could not fetch developer wallet.");
44
+ }
45
+ }
46
+ /**
47
+ * Core check logic generic for any framework.
48
+ */
49
+ async check(headers) {
50
+ if (!this.isInitialized) {
51
+ await this.init();
52
+ }
53
+ const authHeader = headers["authorization"] || headers["Authorization"];
54
+ // 1. If no header or invalid format -> 402 Payment Required
55
+ if (!authHeader || typeof authHeader !== 'string' || !authHeader.startsWith("Bearer ")) {
56
+ return this.create402Response();
57
+ }
58
+ const token = authHeader.replace("Bearer ", "");
59
+ // 2. Verify the token (which is the Payment Header) with Facilitator
60
+ try {
61
+ const atomicAmount = BigInt(Math.floor(this.config.price * 1000000)).toString();
62
+ const defaults = Universal_1.NETWORKS[this.config.network || "solana-mainnet"];
63
+ const verification = await this.api.post("/v1/verify", {
64
+ paymentHeader: token,
65
+ paymentRequirements: {
66
+ recipient: this.developerWallet,
67
+ amount: atomicAmount,
68
+ asset: defaults.usdcMint,
69
+ network: this.config.network || "solana-mainnet",
70
+ resource: this.config.resource || "api-access"
71
+ }
72
+ });
73
+ if (!verification.data || !verification.data.valid) {
74
+ return {
75
+ authorized: false,
76
+ error: {
77
+ status: 403,
78
+ body: {
79
+ error: "Payment Invalid",
80
+ details: verification.data
81
+ }
82
+ }
83
+ };
84
+ }
85
+ // 3. Settle
86
+ const settlement = await this.api.post("/v1/settle", {
87
+ paymentHeader: token,
88
+ paymentRequirements: {
89
+ recipient: this.developerWallet,
90
+ amount: atomicAmount,
91
+ asset: defaults.usdcMint,
92
+ network: this.config.network || "solana-mainnet",
93
+ resource: "api-access"
94
+ }
95
+ });
96
+ if (!settlement.data || !settlement.data.success) {
97
+ return {
98
+ authorized: false,
99
+ error: {
100
+ status: 403,
101
+ body: { error: "Payment Settlement Failed", details: settlement.data }
102
+ }
103
+ };
104
+ }
105
+ return { authorized: true, success: true, verification: verification.data };
106
+ }
107
+ catch (err) {
108
+ if (err.response) {
109
+ return {
110
+ authorized: false,
111
+ error: {
112
+ status: err.response.status === 400 ? 402 : err.response.status, // Map 400 Bad Request (Validation) to 402 if convenient, or keep 400
113
+ body: err.response.data || { error: "Payment Validation Failed" }
114
+ }
115
+ };
116
+ }
117
+ return {
118
+ authorized: false,
119
+ error: {
120
+ status: 500,
121
+ body: { error: "Facilitator Error", message: err.message }
122
+ }
123
+ };
124
+ }
125
+ }
126
+ create402Response() {
127
+ const defaults = Universal_1.NETWORKS[this.config.network || "solana-mainnet"];
128
+ const atomicAmount = BigInt(Math.floor(this.config.price * 1000000)).toString();
129
+ return {
130
+ authorized: false,
131
+ error: {
132
+ status: 402,
133
+ body: {
134
+ recipient: this.developerWallet,
135
+ amount: atomicAmount,
136
+ asset: defaults.usdcMint,
137
+ treasury: defaults.treasury,
138
+ feeBps: 100, // 1%
139
+ message: "Payment Required. Split transaction: 99% to recipient, 1% to treasury."
140
+ }
141
+ }
142
+ };
143
+ }
144
+ // Adapters
145
+ /**
146
+ * Express Middleware Adapter
147
+ */
148
+ express() {
149
+ return async (req, res, next) => {
150
+ const result = await this.check(req.headers);
151
+ if (result.authorized) {
152
+ return next();
153
+ }
154
+ return res.status(result.error.status).json(result.error.body);
155
+ };
156
+ }
157
+ /**
158
+ * NextJS API Route / App Router Helper
159
+ * Usage:
160
+ * const zion = new ZionGate(...);
161
+ * export async function GET(req: Request) {
162
+ * const authorized = await zion.nextServer(req);
163
+ * if (authorized !== true) return authorized; // Returns Response object
164
+ * // ... logic
165
+ * }
166
+ */
167
+ async nextServer(req) {
168
+ // Convert Request headers to Record<string, string>
169
+ const headers = {};
170
+ req.headers.forEach((v, k) => (headers[k] = v));
171
+ const result = await this.check(headers);
172
+ if (result.authorized) {
173
+ return true;
174
+ }
175
+ return Response.json(result.error.body, { status: result.error.status });
176
+ }
177
+ }
178
+ exports.ZionGate = ZionGate;
@@ -0,0 +1,36 @@
1
+ export * from "./Universal";
2
+ export * from "./ZionGate";
3
+ import { Connection } from "@solana/web3.js";
4
+ export interface PaymentRequirements {
5
+ recipient: string;
6
+ amount: string;
7
+ asset: string;
8
+ resource: string;
9
+ name?: string;
10
+ }
11
+ /**
12
+ * Helper for AI Agents (Client-side) to construct headers
13
+ */
14
+ export declare class PaymentBuilder {
15
+ static createHeader(payload: {
16
+ signature: string;
17
+ sender: string;
18
+ recipient: string;
19
+ amount: string;
20
+ asset: string;
21
+ timestamp: number;
22
+ nonce: string;
23
+ network?: string;
24
+ }): string;
25
+ /**
26
+ * Create Instructions for Atomic Split Payment (99% to Dev, 1% to Zion)
27
+ */
28
+ static createAtomicSplitInstructions(config: {
29
+ sender: string;
30
+ recipient: string;
31
+ treasury: string;
32
+ amount: string;
33
+ asset: string;
34
+ connection: Connection;
35
+ }): Promise<import("@solana/web3.js").TransactionInstruction[]>;
36
+ }
package/dist/index.js ADDED
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.PaymentBuilder = void 0;
40
+ __exportStar(require("./Universal"), exports);
41
+ __exportStar(require("./ZionGate"), exports);
42
+ const web3_js_1 = require("@solana/web3.js");
43
+ /**
44
+ * Helper for AI Agents (Client-side) to construct headers
45
+ */
46
+ class PaymentBuilder {
47
+ static createHeader(payload) {
48
+ const json = {
49
+ x402Version: 1,
50
+ scheme: "exact",
51
+ network: payload.network || "solana-mainnet",
52
+ payload: payload,
53
+ };
54
+ const jsonStr = JSON.stringify(json);
55
+ if (typeof window !== "undefined" && typeof window.btoa === "function") {
56
+ return window.btoa(jsonStr);
57
+ }
58
+ else {
59
+ return Buffer.from(jsonStr).toString("base64");
60
+ }
61
+ }
62
+ /**
63
+ * Create Instructions for Atomic Split Payment (99% to Dev, 1% to Zion)
64
+ */
65
+ static async createAtomicSplitInstructions(config) {
66
+ const { getAssociatedTokenAddress, createTransferInstruction } = await Promise.resolve().then(() => __importStar(require("@solana/spl-token")));
67
+ const total = BigInt(config.amount);
68
+ const fee = (total * BigInt(100)) / BigInt(10000); // 1%
69
+ const net = total - fee;
70
+ const senderPubkey = new web3_js_1.PublicKey(config.sender);
71
+ const recipientPubkey = new web3_js_1.PublicKey(config.recipient);
72
+ const treasuryPubkey = new web3_js_1.PublicKey(config.treasury);
73
+ const mintPubkey = new web3_js_1.PublicKey(config.asset);
74
+ // Get ATAs
75
+ const senderAta = await getAssociatedTokenAddress(mintPubkey, senderPubkey);
76
+ const recipientAta = await getAssociatedTokenAddress(mintPubkey, recipientPubkey);
77
+ const treasuryAta = await getAssociatedTokenAddress(mintPubkey, treasuryPubkey);
78
+ // Instruction 1: 99% to Developer
79
+ const ix1 = createTransferInstruction(senderAta, recipientAta, senderPubkey, net);
80
+ // Instruction 2: 1% to Treasury
81
+ const ix2 = createTransferInstruction(senderAta, treasuryAta, senderPubkey, fee);
82
+ return [ix1, ix2];
83
+ }
84
+ }
85
+ exports.PaymentBuilder = PaymentBuilder;
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@ziongateway/sdk",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for Zion x402 Facilitator",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "solana",
16
+ "x402",
17
+ "payments",
18
+ "ai",
19
+ "agents"
20
+ ],
21
+ "author": "Zion",
22
+ "license": "ISC",
23
+ "dependencies": {
24
+ "@solana/spl-token": "^0.4.14",
25
+ "@solana/web3.js": "^1.98.4",
26
+ "axios": "^1.6.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/bs58": "^4.0.4",
30
+ "@types/express": "^5.0.6",
31
+ "@types/node": "^20.0.0",
32
+ "dotenv": "^17.2.3",
33
+ "express": "^5.2.1",
34
+ "typescript": "^5.0.0"
35
+ },
36
+ "files": ["dist"],
37
+ "homepage": "https://docs.ziongateway.xyz"
38
+ }