@alleyboss/micropay-solana-x402-paywall 2.3.0 → 3.0.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.
Files changed (70) hide show
  1. package/README.md +72 -116
  2. package/dist/agent/index.cjs +358 -0
  3. package/dist/agent/index.cjs.map +1 -0
  4. package/dist/agent/index.d.cts +221 -0
  5. package/dist/agent/index.d.ts +221 -0
  6. package/dist/agent/index.js +347 -0
  7. package/dist/agent/index.js.map +1 -0
  8. package/dist/client/index.cjs +1 -1
  9. package/dist/client/index.cjs.map +1 -1
  10. package/dist/client/index.d.cts +10 -1
  11. package/dist/client/index.d.ts +10 -1
  12. package/dist/client/index.js +1 -1
  13. package/dist/client/index.js.map +1 -1
  14. package/dist/express/index.cjs +79 -0
  15. package/dist/express/index.cjs.map +1 -0
  16. package/dist/express/index.d.cts +40 -0
  17. package/dist/express/index.d.ts +40 -0
  18. package/dist/express/index.js +76 -0
  19. package/dist/express/index.js.map +1 -0
  20. package/dist/index.cjs +315 -1116
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +6 -10
  23. package/dist/index.d.ts +6 -10
  24. package/dist/index.js +283 -1074
  25. package/dist/index.js.map +1 -1
  26. package/dist/session/index.cjs.map +1 -1
  27. package/dist/session/index.d.cts +1 -1
  28. package/dist/session/index.d.ts +1 -1
  29. package/dist/session/index.js.map +1 -1
  30. package/dist/{session-D2IoWAWV.d.cts → types-BWYQMw03.d.cts} +1 -16
  31. package/dist/{session-D2IoWAWV.d.ts → types-BWYQMw03.d.ts} +1 -16
  32. package/package.json +29 -59
  33. package/dist/client-D-dteoJw.d.cts +0 -63
  34. package/dist/client-DfCIRrNG.d.ts +0 -63
  35. package/dist/memory-Daxkczti.d.cts +0 -29
  36. package/dist/memory-Daxkczti.d.ts +0 -29
  37. package/dist/middleware/index.cjs +0 -273
  38. package/dist/middleware/index.cjs.map +0 -1
  39. package/dist/middleware/index.d.cts +0 -91
  40. package/dist/middleware/index.d.ts +0 -91
  41. package/dist/middleware/index.js +0 -267
  42. package/dist/middleware/index.js.map +0 -1
  43. package/dist/nextjs-BDyOqGAq.d.cts +0 -81
  44. package/dist/nextjs-CbX8_9yK.d.ts +0 -81
  45. package/dist/payment-BGp7eMQl.d.cts +0 -103
  46. package/dist/payment-BGp7eMQl.d.ts +0 -103
  47. package/dist/solana/index.cjs +0 -589
  48. package/dist/solana/index.cjs.map +0 -1
  49. package/dist/solana/index.d.cts +0 -240
  50. package/dist/solana/index.d.ts +0 -240
  51. package/dist/solana/index.js +0 -567
  52. package/dist/solana/index.js.map +0 -1
  53. package/dist/store/index.cjs +0 -99
  54. package/dist/store/index.cjs.map +0 -1
  55. package/dist/store/index.d.cts +0 -38
  56. package/dist/store/index.d.ts +0 -38
  57. package/dist/store/index.js +0 -96
  58. package/dist/store/index.js.map +0 -1
  59. package/dist/utils/index.cjs +0 -68
  60. package/dist/utils/index.cjs.map +0 -1
  61. package/dist/utils/index.d.cts +0 -30
  62. package/dist/utils/index.d.ts +0 -30
  63. package/dist/utils/index.js +0 -65
  64. package/dist/utils/index.js.map +0 -1
  65. package/dist/x402/index.cjs +0 -387
  66. package/dist/x402/index.cjs.map +0 -1
  67. package/dist/x402/index.d.cts +0 -96
  68. package/dist/x402/index.d.ts +0 -96
  69. package/dist/x402/index.js +0 -375
  70. package/dist/x402/index.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @alleyboss/micropay-solana-x402-paywall
2
2
 
3
- > Production-ready Solana micropayments library implementing the x402 protocol.
3
+ > Production-ready Solana micropayments library wrapper for the official x402 SDK.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@alleyboss/micropay-solana-x402-paywall)](https://www.npmjs.com/package/@alleyboss/micropay-solana-x402-paywall)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
@@ -8,158 +8,114 @@
8
8
 
9
9
  ## 🚀 What It Does
10
10
 
11
- Turn any content into paid content with **one-time micropayments** on Solana. No subscriptions, no recurring charges—just pay to unlock.
11
+ Turn any content into paid content with **one-time micropayments** on Solana. fully compatible with the official [x402.org](https://x402.org) protocol.
12
+
13
+ This library enhances the official SDK with features like **AI Agent Payments**, **Hybrid Sessions**, and **Express.js Middleware**.
12
14
 
13
15
  ```bash
14
- npm install @alleyboss/micropay-solana-x402-paywall @solana/web3.js
16
+ npm install @alleyboss/micropay-solana-x402-paywall @x402/core @x402/svm @solana/web3.js
15
17
  ```
16
18
 
17
19
  ## ✨ Features
18
20
 
19
- | Feature | Description |
20
- |---------|-------------|
21
- | 💰 **SOL & USDC Payments** | Native SOL and SPL tokens (USDC, USDT) |
22
- | 🔐 **x402 Protocol** | Full HTTP 402 compliance with `X-Payment-Required` headers |
23
- | 🔑 **JWT Sessions** | Secure unlock tracking with anti-replay |
24
- | 🛡️ **Signature Store** | Prevent double-spend at app layer |
25
- | 🔌 **Express & Next.js** | Zero-boilerplate middleware |
26
- | 💵 **Price Conversion** | USD↔SOL with multi-provider fallback |
27
- | 🌳 **Tree-Shakeable** | Import only what you need |
28
- | 🔄 **RPC Fallback** | Automatic failover on RPC errors |
29
- | **Priority Fees** | Land transactions faster |
30
- | 📦 **Versioned Tx** | Full v0 transaction support |
31
-
32
- ## 📦 Quick Example
21
+ | Feature | Description | Status |
22
+ |---------|-------------|--------|
23
+ | 💰 **SOL & USDC** | Native SOL and SPL tokens (USDC, USDT) | ✅ Verified by `@x402/svm` |
24
+ | 🔐 **x402 Protocol** | Full HTTP 402 compliance | ✅ Powered by `@x402/core` |
25
+ | 🔑 **JWT Sessions** | "Pay once, unlock for 24h" logic | ✅ Built-in (Hybrid Support) |
26
+ | 🛡️ **Replay Protection** | Prevent double-spend / replay attacks | ✅ Managed by x402 Facilitator |
27
+ | 🔌 **Express Integration** | Middleware for Express/Node.js | ✅ Built-in |
28
+ | 💵 **Price Conversion** | USD↔SOL with multi-provider fallback | ✅ Built-in |
29
+ | 🤖 **AI Agents** | Autonomous payment execution for agents | ✅ Built-in |
30
+ | **Priority Fees** | Compute unit price optimization | ✅ Supported (Agent Module) |
31
+ | 📦 **Versioned Tx** | v0 Transaction support | ✅ Native (x402 SDK) |
32
+ | 🌳 **Tree-Shakeable** | Modular exports | Built-in |
33
+
34
+ ## 📦 Quick Example (Express.js)
33
35
 
34
36
  ```typescript
35
- import { verifyPayment, createSession } from '@alleyboss/micropay-solana-x402-paywall';
36
-
37
- // Verify on-chain payment
38
- const result = await verifyPayment({
39
- signature: 'tx...',
40
- expectedRecipient: 'CreatorWallet',
41
- expectedAmount: 10_000_000n, // 0.01 SOL
42
- clientConfig: { network: 'mainnet-beta' },
37
+ import express from 'express';
38
+ import { x402ResourceServer } from '@x402/core/server';
39
+ import { x402Middleware } from '@alleyboss/micropay-solana-x402-paywall/express';
40
+
41
+ const app = express();
42
+ // The x402ResourceServer handles protocol logic and facilitator communication
43
+ const server = new x402ResourceServer({
44
+ facilitatorUrl: process.env.X402_FACILITATOR_URL,
45
+ serviceId: process.env.X402_SERVICE_ID,
43
46
  });
44
47
 
45
- // Create session for unlocked content
46
- if (result.valid) {
47
- const { token } = await createSession(
48
- result.from!,
49
- 'article-123',
50
- { secret: process.env.SESSION_SECRET!, durationHours: 24 }
51
- );
52
- }
48
+ app.get('/premium', x402Middleware(server, {
49
+ accepts: {
50
+ scheme: 'exact',
51
+ amount: '1000000', // 0.001 SOL
52
+ network: 'solana-mainnet'
53
+ },
54
+ description: 'Premium Article'
55
+ }), (req, res) => {
56
+ // Session token or payment proof is available
57
+ res.send('Thank you for your payment!');
58
+ });
53
59
  ```
54
60
 
55
61
  ## 🔧 Modules
56
62
 
57
- 9 tree-shakeable entry points for minimal bundle size:
63
+ Import only what you need:
58
64
 
59
65
  ```typescript
60
- // Core verification
61
- import { verifyPayment, verifySPLPayment } from '@alleyboss/micropay-solana-x402-paywall/solana';
62
-
63
- // Session management
64
- import { createSession, validateSession } from '@alleyboss/micropay-solana-x402-paywall/session';
65
-
66
- // x402 protocol
67
- import { buildPaymentRequirement } from '@alleyboss/micropay-solana-x402-paywall/x402';
66
+ // Express Middleware
67
+ import { x402Middleware } from '@alleyboss/micropay-solana-x402-paywall/express';
68
68
 
69
- // Express/Next.js middleware
70
- import { createExpressMiddleware, createPaywallMiddleware } from '@alleyboss/micropay-solana-x402-paywall/middleware';
69
+ // AI Agent Payments
70
+ import { executeAgentPayment } from '@alleyboss/micropay-solana-x402-paywall/agent';
71
71
 
72
- // Anti-replay signature store
73
- import { createMemoryStore, createRedisStore } from '@alleyboss/micropay-solana-x402-paywall/store';
72
+ // Pricing Utilities
73
+ import { getSolPrice, lamportsToUsd } from '@alleyboss/micropay-solana-x402-paywall/pricing';
74
74
 
75
- // Client-side helpers
76
- import { createPaymentFlow, buildSolanaPayUrl } from '@alleyboss/micropay-solana-x402-paywall/client';
77
-
78
- // Price conversion (4-provider rotation)
79
- import { getSolPrice, formatPriceDisplay, configurePricing } from '@alleyboss/micropay-solana-x402-paywall/pricing';
75
+ // Session Management (Hybrid)
76
+ import { createSession, validateSession } from '@alleyboss/micropay-solana-x402-paywall/session';
80
77
 
81
- // Retry utilities
82
- import { withRetry } from '@alleyboss/micropay-solana-x402-paywall/utils';
78
+ // Client Helpers
79
+ import { createPaymentFlow } from '@alleyboss/micropay-solana-x402-paywall/client';
83
80
  ```
84
81
 
85
- ## 🔥 New in v2.2.0
86
-
87
- ### x402 Protocol Compliance
82
+ ## 🤖 AI Agent Payments
88
83
 
89
- Full compliance with the [x402.org](https://x402.org) specification:
84
+ Enable autonomous AI agents to pay for premium API access.
90
85
 
91
86
  ```typescript
92
- import { create402Response, parsePaymentHeader, encodePaymentRequirement } from '@alleyboss/micropay-solana-x402-paywall/x402';
93
-
94
- // Create 402 response with X-Payment-Required header
95
- const response = create402Response({
96
- scheme: 'exact',
97
- network: 'solana-mainnet',
98
- maxAmountRequired: '10000000',
99
- payTo: 'CreatorWallet...',
100
- resource: '/api/premium',
101
- description: 'Premium content access',
102
- maxTimeoutSeconds: 300,
103
- asset: 'native',
104
- });
105
- // Response includes: X-Payment-Required: <base64-encoded-requirement>
106
-
107
- // Parse X-Payment header from client
108
- const payload = parsePaymentHeader(request.headers.get('x-payment'));
109
- ```
87
+ import { executeAgentPayment } from '@alleyboss/micropay-solana-x402-paywall/agent';
88
+ import { Keypair, Connection } from '@solana/web3.js';
110
89
 
111
- ### Previous Features (v2.1.x)
90
+ const agentKeypair = Keypair.fromSecretKey(/* ... */);
112
91
 
113
- - **RPC Fallback Support** — Automatic failover on primary RPC failure
114
- - **Priority Fees** — Compute budget instructions for landing transactions faster
115
- - **Versioned Transactions** — Full v0 transaction support with lookup tables
116
- - **TDD Test Suite** — Comprehensive tests with vitest
117
-
118
- ```typescript
119
- // Priority fees
120
- import { createPriorityFeeInstructions } from '@alleyboss/micropay-solana-x402-paywall/solana';
121
-
122
- const instructions = createPriorityFeeInstructions({
123
- enabled: true,
124
- microLamports: 5000,
125
- computeUnits: 200_000,
92
+ const result = await executeAgentPayment({
93
+ connection: new Connection('https://api.mainnet-beta.solana.com'),
94
+ agentKeypair,
95
+ recipientAddress: 'CREATOR_WALLET',
96
+ amountLamports: 2_000_000n,
97
+ priorityFee: { enabled: true, microLamports: 10000 }, // Priority Fees Supported
126
98
  });
127
99
 
128
- // Versioned transactions
129
- import { buildVersionedTransaction } from '@alleyboss/micropay-solana-x402-paywall/solana';
130
-
131
- const { transaction } = await buildVersionedTransaction({
132
- connection,
133
- payer: wallet.publicKey,
134
- instructions: [transferIx],
135
- priorityFee: { enabled: true },
136
- });
100
+ if (result.success) {
101
+ console.log('Payment confirmed:', result.signature);
102
+ }
137
103
  ```
138
104
 
139
- ## 🛠️ RPC Providers
105
+ ## 📚 Documentation
140
106
 
141
- Works with any Solana RPC provider:
107
+ For full documentation:
108
+ - **Library Docs**: [solana-x402-paywall.vercel.app/docs](https://solana-x402-paywall.vercel.app/docs)
109
+ - **Protocol Docs**: [x402.org](https://docs.x402.org)
142
110
 
143
- ```typescript
144
- const config = {
145
- network: 'mainnet-beta',
146
- // Tatum.io
147
- tatumApiKey: 'your-key',
148
- // Or custom (Helius, QuickNode, etc.)
149
- rpcUrl: 'https://your-rpc.com',
150
- // Optional: enable fallback
151
- enableFallback: true,
152
- fallbackRpcUrls: ['https://backup.rpc.com'],
153
- };
154
- ```
111
+ ## ☕ Support
155
112
 
156
- ## 📚 Documentation
113
+ If you find this library useful, you can buy me a coffee by sending some SOL to:
157
114
 
158
- **Full documentation, API reference, and examples:**
115
+ **`7fPjNJaEHtepp1ZRr6GsaW1k22U1FupQtwuHUkTb6Xg9`**
159
116
 
160
- 👉 **[solana-x402-paywall.vercel.app/docs](https://solana-x402-paywall.vercel.app/docs)**
117
+ Your support helps maintain this project!
161
118
 
162
119
  ## 📄 License
163
120
 
164
121
  MIT © AlleyBoss
165
-
@@ -0,0 +1,358 @@
1
+ 'use strict';
2
+
3
+ var web3_js = require('@solana/web3.js');
4
+ var jose = require('jose');
5
+ var uuid = require('uuid');
6
+
7
+ // src/agent/agentPayment.ts
8
+ var DEFAULT_COMPUTE_UNITS = 2e5;
9
+ var DEFAULT_MICRO_LAMPORTS = 1e3;
10
+ function createPriorityFeeInstructions(config = {}) {
11
+ const { enabled = false, microLamports, computeUnits } = config;
12
+ if (!enabled) {
13
+ return [];
14
+ }
15
+ const instructions = [];
16
+ const units = computeUnits ?? DEFAULT_COMPUTE_UNITS;
17
+ instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units }));
18
+ const price = microLamports ?? DEFAULT_MICRO_LAMPORTS;
19
+ instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
20
+ return instructions;
21
+ }
22
+ async function buildVersionedTransaction(config) {
23
+ const {
24
+ connection,
25
+ payer,
26
+ instructions,
27
+ priorityFee,
28
+ recentBlockhash
29
+ } = config;
30
+ const priorityIxs = createPriorityFeeInstructions(priorityFee);
31
+ const allInstructions = [...priorityIxs, ...instructions];
32
+ let blockhash;
33
+ let lastValidBlockHeight;
34
+ if (recentBlockhash) {
35
+ blockhash = recentBlockhash;
36
+ const slot = await connection.getSlot();
37
+ lastValidBlockHeight = slot + 150;
38
+ } else {
39
+ const latestBlockhash = await connection.getLatestBlockhash("confirmed");
40
+ blockhash = latestBlockhash.blockhash;
41
+ lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
42
+ }
43
+ const message = new web3_js.TransactionMessage({
44
+ payerKey: payer,
45
+ recentBlockhash: blockhash,
46
+ instructions: allInstructions
47
+ }).compileToV0Message([]);
48
+ const transaction = new web3_js.VersionedTransaction(message);
49
+ return {
50
+ transaction,
51
+ blockhash,
52
+ lastValidBlockHeight
53
+ };
54
+ }
55
+
56
+ // src/agent/agentPayment.ts
57
+ var WALLET_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
58
+ function isValidWalletAddress(address) {
59
+ if (!address || typeof address !== "string") return false;
60
+ return WALLET_REGEX.test(address);
61
+ }
62
+ async function executeAgentPayment(params) {
63
+ const {
64
+ connection,
65
+ agentKeypair,
66
+ recipientAddress,
67
+ amountLamports,
68
+ priorityFee,
69
+ confirmationTimeout = 6e4
70
+ } = params;
71
+ if (!isValidWalletAddress(recipientAddress)) {
72
+ return {
73
+ success: false,
74
+ error: "Invalid recipient address format"
75
+ };
76
+ }
77
+ if (amountLamports <= 0n) {
78
+ return {
79
+ success: false,
80
+ error: "Amount must be greater than 0"
81
+ };
82
+ }
83
+ try {
84
+ const recipientPubkey = new web3_js.PublicKey(recipientAddress);
85
+ const transferInstruction = web3_js.SystemProgram.transfer({
86
+ fromPubkey: agentKeypair.publicKey,
87
+ toPubkey: recipientPubkey,
88
+ lamports: amountLamports
89
+ });
90
+ const { transaction, lastValidBlockHeight } = await buildVersionedTransaction({
91
+ connection,
92
+ payer: agentKeypair.publicKey,
93
+ instructions: [transferInstruction],
94
+ priorityFee
95
+ });
96
+ transaction.sign([agentKeypair]);
97
+ const signature = await connection.sendTransaction(transaction, {
98
+ maxRetries: 3,
99
+ skipPreflight: false
100
+ });
101
+ const confirmationPromise = connection.confirmTransaction(
102
+ {
103
+ signature,
104
+ lastValidBlockHeight,
105
+ blockhash: transaction.message.recentBlockhash
106
+ },
107
+ "confirmed"
108
+ );
109
+ const timeoutPromise = new Promise((_, reject) => {
110
+ setTimeout(() => reject(new Error("Confirmation timeout")), confirmationTimeout);
111
+ });
112
+ const confirmation = await Promise.race([confirmationPromise, timeoutPromise]);
113
+ if (confirmation.value.err) {
114
+ return {
115
+ success: false,
116
+ signature,
117
+ error: "Transaction failed on-chain"
118
+ };
119
+ }
120
+ const txDetails = await connection.getTransaction(signature, {
121
+ commitment: "confirmed",
122
+ maxSupportedTransactionVersion: 0
123
+ });
124
+ return {
125
+ success: true,
126
+ signature,
127
+ confirmedAt: txDetails?.blockTime ?? Math.floor(Date.now() / 1e3),
128
+ slot: txDetails?.slot ?? confirmation.context.slot,
129
+ amountLamports,
130
+ amountSol: Number(amountLamports) / web3_js.LAMPORTS_PER_SOL
131
+ };
132
+ } catch (error) {
133
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
134
+ return {
135
+ success: false,
136
+ error: errorMessage
137
+ };
138
+ }
139
+ }
140
+ async function getAgentBalance(connection, agentKeypair) {
141
+ const balance = await connection.getBalance(agentKeypair.publicKey);
142
+ return {
143
+ balance: BigInt(balance),
144
+ balanceSol: balance / web3_js.LAMPORTS_PER_SOL
145
+ };
146
+ }
147
+ async function hasAgentSufficientBalance(connection, agentKeypair, requiredLamports) {
148
+ const { balance } = await getAgentBalance(connection, agentKeypair);
149
+ const totalRequired = requiredLamports + 10000n;
150
+ return {
151
+ sufficient: balance >= totalRequired,
152
+ balance,
153
+ required: totalRequired
154
+ };
155
+ }
156
+ function keypairFromBase58(base58Secret) {
157
+ const bytes = Buffer.from(base58Secret, "base64");
158
+ if (bytes.length !== 64) {
159
+ const parts = base58Secret.split(",").map((n) => parseInt(n.trim(), 10));
160
+ if (parts.length === 64) {
161
+ return web3_js.Keypair.fromSecretKey(Uint8Array.from(parts));
162
+ }
163
+ throw new Error("Invalid secret key format. Expected base58 string or comma-separated bytes.");
164
+ }
165
+ return web3_js.Keypair.fromSecretKey(bytes);
166
+ }
167
+ function generateAgentKeypair() {
168
+ const keypair = web3_js.Keypair.generate();
169
+ const secretBytes = Array.from(keypair.secretKey);
170
+ return {
171
+ keypair,
172
+ secretBase58: secretBytes.join(","),
173
+ // Comma-separated for easy storage
174
+ publicKey: keypair.publicKey.toBase58()
175
+ };
176
+ }
177
+ var MAX_CREDITS = 1e3;
178
+ var MIN_SECRET_LENGTH = 32;
179
+ function getSecretKey(secret) {
180
+ if (!secret || typeof secret !== "string") {
181
+ throw new Error("Session secret is required");
182
+ }
183
+ if (secret.length < MIN_SECRET_LENGTH) {
184
+ throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH} characters`);
185
+ }
186
+ return new TextEncoder().encode(secret);
187
+ }
188
+ function validateWalletAddress(address) {
189
+ if (!address || typeof address !== "string") return false;
190
+ const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
191
+ return base58Regex.test(address);
192
+ }
193
+ async function createCreditSession(walletAddress, purchaseId, config) {
194
+ if (!validateWalletAddress(walletAddress)) {
195
+ throw new Error("Invalid wallet address format");
196
+ }
197
+ if (config.initialCredits <= 0 || config.initialCredits > MAX_CREDITS) {
198
+ throw new Error(`Credits must be between 1 and ${MAX_CREDITS}`);
199
+ }
200
+ if (!config.durationHours || config.durationHours <= 0 || config.durationHours > 8760) {
201
+ throw new Error("Session duration must be between 1 and 8760 hours (1 year)");
202
+ }
203
+ const sessionId = uuid.v4();
204
+ const now = Math.floor(Date.now() / 1e3);
205
+ const expiresAt = now + config.durationHours * 3600;
206
+ const bundleExpiry = config.bundleExpiryHours ? now + config.bundleExpiryHours * 3600 : expiresAt;
207
+ const session = {
208
+ id: sessionId,
209
+ walletAddress,
210
+ unlockedArticles: [purchaseId],
211
+ siteWideUnlock: false,
212
+ createdAt: now,
213
+ expiresAt,
214
+ credits: config.initialCredits,
215
+ bundleExpiry,
216
+ bundleType: config.bundleType
217
+ };
218
+ const payload = {
219
+ sub: walletAddress,
220
+ sid: sessionId,
221
+ articles: session.unlockedArticles,
222
+ siteWide: false,
223
+ credits: config.initialCredits,
224
+ bundleExpiry,
225
+ bundleType: config.bundleType,
226
+ iat: now,
227
+ exp: expiresAt
228
+ };
229
+ const token = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config.durationHours}h`).sign(getSecretKey(config.secret));
230
+ return { token, session };
231
+ }
232
+ async function validateCreditSession(token, secret) {
233
+ if (!token || typeof token !== "string") {
234
+ return { valid: false, reason: "Invalid token format" };
235
+ }
236
+ try {
237
+ const { payload } = await jose.jwtVerify(token, getSecretKey(secret));
238
+ const creditPayload = payload;
239
+ if (!creditPayload.sub || !creditPayload.sid || !creditPayload.exp) {
240
+ return { valid: false, reason: "Malformed session payload" };
241
+ }
242
+ const now = Math.floor(Date.now() / 1e3);
243
+ if (creditPayload.exp < now) {
244
+ return { valid: false, reason: "Session expired" };
245
+ }
246
+ if (creditPayload.bundleExpiry && creditPayload.bundleExpiry < now) {
247
+ return { valid: false, reason: "Bundle expired" };
248
+ }
249
+ if (!validateWalletAddress(creditPayload.sub)) {
250
+ return { valid: false, reason: "Invalid session data" };
251
+ }
252
+ const session = {
253
+ id: creditPayload.sid,
254
+ walletAddress: creditPayload.sub,
255
+ unlockedArticles: Array.isArray(creditPayload.articles) ? creditPayload.articles : [],
256
+ siteWideUnlock: Boolean(creditPayload.siteWide),
257
+ createdAt: creditPayload.iat ?? 0,
258
+ expiresAt: creditPayload.exp,
259
+ credits: creditPayload.credits ?? 0,
260
+ bundleExpiry: creditPayload.bundleExpiry,
261
+ bundleType: creditPayload.bundleType
262
+ };
263
+ return { valid: true, session };
264
+ } catch {
265
+ return { valid: false, reason: "Invalid session" };
266
+ }
267
+ }
268
+ async function useCredit(token, secret, creditsToUse = 1) {
269
+ if (creditsToUse <= 0) {
270
+ return { success: false, remainingCredits: 0, error: "Invalid credit amount" };
271
+ }
272
+ const validation = await validateCreditSession(token, secret);
273
+ if (!validation.valid || !validation.session) {
274
+ return {
275
+ success: false,
276
+ remainingCredits: 0,
277
+ error: validation.reason || "Invalid session"
278
+ };
279
+ }
280
+ const session = validation.session;
281
+ if (session.credits < creditsToUse) {
282
+ return {
283
+ success: false,
284
+ remainingCredits: session.credits,
285
+ error: "Insufficient credits"
286
+ };
287
+ }
288
+ const newCredits = session.credits - creditsToUse;
289
+ const payload = {
290
+ sub: session.walletAddress,
291
+ sid: session.id,
292
+ articles: session.unlockedArticles,
293
+ siteWide: session.siteWideUnlock,
294
+ credits: newCredits,
295
+ bundleExpiry: session.bundleExpiry,
296
+ bundleType: session.bundleType,
297
+ iat: session.createdAt,
298
+ exp: session.expiresAt
299
+ };
300
+ const newToken = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
301
+ return {
302
+ success: true,
303
+ remainingCredits: newCredits,
304
+ newToken
305
+ };
306
+ }
307
+ async function addCredits(token, secret, creditsToAdd) {
308
+ if (creditsToAdd <= 0 || creditsToAdd > MAX_CREDITS) {
309
+ return { success: false, error: "Invalid credit amount" };
310
+ }
311
+ const validation = await validateCreditSession(token, secret);
312
+ if (!validation.valid || !validation.session) {
313
+ return { success: false, error: validation.reason || "Invalid session" };
314
+ }
315
+ const session = validation.session;
316
+ const newCredits = Math.min(session.credits + creditsToAdd, MAX_CREDITS);
317
+ const payload = {
318
+ sub: session.walletAddress,
319
+ sid: session.id,
320
+ articles: session.unlockedArticles,
321
+ siteWide: session.siteWideUnlock,
322
+ credits: newCredits,
323
+ bundleExpiry: session.bundleExpiry,
324
+ bundleType: session.bundleType,
325
+ iat: session.createdAt,
326
+ exp: session.expiresAt
327
+ };
328
+ const newToken = await new jose.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
329
+ return {
330
+ success: true,
331
+ newToken,
332
+ totalCredits: newCredits
333
+ };
334
+ }
335
+ async function getRemainingCredits(token, secret) {
336
+ const validation = await validateCreditSession(token, secret);
337
+ if (!validation.valid || !validation.session) {
338
+ return { credits: 0, valid: false };
339
+ }
340
+ return {
341
+ credits: validation.session.credits,
342
+ valid: true,
343
+ bundleExpiry: validation.session.bundleExpiry
344
+ };
345
+ }
346
+
347
+ exports.addCredits = addCredits;
348
+ exports.createCreditSession = createCreditSession;
349
+ exports.executeAgentPayment = executeAgentPayment;
350
+ exports.generateAgentKeypair = generateAgentKeypair;
351
+ exports.getAgentBalance = getAgentBalance;
352
+ exports.getRemainingCredits = getRemainingCredits;
353
+ exports.hasAgentSufficientBalance = hasAgentSufficientBalance;
354
+ exports.keypairFromBase58 = keypairFromBase58;
355
+ exports.useCredit = useCredit;
356
+ exports.validateCreditSession = validateCreditSession;
357
+ //# sourceMappingURL=index.cjs.map
358
+ //# sourceMappingURL=index.cjs.map