@alleyboss/micropay-solana-x402-paywall 3.5.1 → 3.5.2

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 CHANGED
@@ -117,6 +117,12 @@ const x402Fetch = createX402Fetch({
117
117
  windowMs: 60_000, // Per minute
118
118
  },
119
119
  });
120
+
121
+ // 💡 Header Compatibility:
122
+ // Automatically detects requirements from:
123
+ // - WWW-Authenticate: X402 ... (Standard)
124
+ // - X-Payment-Requirements: json (Simple)
125
+ // - payment-required: base64 (Upstream Middleware)
120
126
  ```
121
127
 
122
128
  **Security Error Codes:**
@@ -229,6 +235,19 @@ const withMicropay = createX402Middleware({
229
235
  | **Setup** | Zero-config | Requires RPC URL |
230
236
  | **Best For** | Quick startups, MVPs | Production, High-Volume, Agents |
231
237
 
238
+ ## 🔧 What's New in v3.5.2
239
+
240
+ **Middleware Fix**: The `createX402Middleware` now generates 402 responses directly, fixing a bug where the upstream library multiplied payment amounts by 1M. Your configured `price` in lamports is now used exactly as specified.
241
+
242
+ ```typescript
243
+ // price: '1000000' → 0.001 SOL (correct!)
244
+ const withPayment = createX402Middleware({
245
+ walletAddress: 'YOUR_WALLET',
246
+ price: '1000000', // This is now used correctly
247
+ network: 'devnet'
248
+ });
249
+ ```
250
+
232
251
  ## 🌐 PayAI Format Support (New in v3.3.13)
233
252
 
234
253
  Native support for the **PayAI payment format** - a universal payment protocol that works across Solana, Ethereum, Base, and other chains.
@@ -542,6 +542,25 @@ function parse402Response(response) {
542
542
  throw invalid402ResponseError("Invalid WWW-Authenticate header");
543
543
  }
544
544
  }
545
+ const paymentRequired = response.headers.get("payment-required");
546
+ if (paymentRequired) {
547
+ try {
548
+ const jsonStr = atob(paymentRequired.trim());
549
+ const parsed = JSON.parse(jsonStr);
550
+ const option = Array.isArray(parsed.accepts) && parsed.accepts.length > 0 ? parsed.accepts[0] : parsed;
551
+ return {
552
+ payTo: option.payTo ?? option.recipient,
553
+ amount: String(option.amount),
554
+ asset: option.asset ?? "SOL",
555
+ network: option.network ?? "solana-mainnet",
556
+ description: parsed.description ?? option.description,
557
+ resource: parsed.resource ?? option.resource,
558
+ maxAge: parsed.maxAge ?? option.maxAge
559
+ };
560
+ } catch {
561
+ throw invalid402ResponseError("Invalid payment-required header");
562
+ }
563
+ }
545
564
  throw invalid402ResponseError("No payment requirements found in 402 response");
546
565
  }
547
566
  function buildPaymentHeader(signature) {
@@ -536,6 +536,25 @@ function parse402Response(response) {
536
536
  throw invalid402ResponseError("Invalid WWW-Authenticate header");
537
537
  }
538
538
  }
539
+ const paymentRequired = response.headers.get("payment-required");
540
+ if (paymentRequired) {
541
+ try {
542
+ const jsonStr = atob(paymentRequired.trim());
543
+ const parsed = JSON.parse(jsonStr);
544
+ const option = Array.isArray(parsed.accepts) && parsed.accepts.length > 0 ? parsed.accepts[0] : parsed;
545
+ return {
546
+ payTo: option.payTo ?? option.recipient,
547
+ amount: String(option.amount),
548
+ asset: option.asset ?? "SOL",
549
+ network: option.network ?? "solana-mainnet",
550
+ description: parsed.description ?? option.description,
551
+ resource: parsed.resource ?? option.resource,
552
+ maxAge: parsed.maxAge ?? option.maxAge
553
+ };
554
+ } catch {
555
+ throw invalid402ResponseError("Invalid payment-required header");
556
+ }
557
+ }
539
558
  throw invalid402ResponseError("No payment requirements found in 402 response");
540
559
  }
541
560
  function buildPaymentHeader(signature) {
@@ -210,6 +210,25 @@ function parse402Response(response) {
210
210
  throw invalid402ResponseError("Invalid WWW-Authenticate header");
211
211
  }
212
212
  }
213
+ const paymentRequired = response.headers.get("payment-required");
214
+ if (paymentRequired) {
215
+ try {
216
+ const jsonStr = atob(paymentRequired.trim());
217
+ const parsed = JSON.parse(jsonStr);
218
+ const option = Array.isArray(parsed.accepts) && parsed.accepts.length > 0 ? parsed.accepts[0] : parsed;
219
+ return {
220
+ payTo: option.payTo ?? option.recipient,
221
+ amount: String(option.amount),
222
+ asset: option.asset ?? "SOL",
223
+ network: option.network ?? "solana-mainnet",
224
+ description: parsed.description ?? option.description,
225
+ resource: parsed.resource ?? option.resource,
226
+ maxAge: parsed.maxAge ?? option.maxAge
227
+ };
228
+ } catch {
229
+ throw invalid402ResponseError("Invalid payment-required header");
230
+ }
231
+ }
213
232
  throw invalid402ResponseError("No payment requirements found in 402 response");
214
233
  }
215
234
  function buildPaymentHeader(signature) {
@@ -208,6 +208,25 @@ function parse402Response(response) {
208
208
  throw invalid402ResponseError("Invalid WWW-Authenticate header");
209
209
  }
210
210
  }
211
+ const paymentRequired = response.headers.get("payment-required");
212
+ if (paymentRequired) {
213
+ try {
214
+ const jsonStr = atob(paymentRequired.trim());
215
+ const parsed = JSON.parse(jsonStr);
216
+ const option = Array.isArray(parsed.accepts) && parsed.accepts.length > 0 ? parsed.accepts[0] : parsed;
217
+ return {
218
+ payTo: option.payTo ?? option.recipient,
219
+ amount: String(option.amount),
220
+ asset: option.asset ?? "SOL",
221
+ network: option.network ?? "solana-mainnet",
222
+ description: parsed.description ?? option.description,
223
+ resource: parsed.resource ?? option.resource,
224
+ maxAge: parsed.maxAge ?? option.maxAge
225
+ };
226
+ } catch {
227
+ throw invalid402ResponseError("Invalid payment-required header");
228
+ }
229
+ }
211
230
  throw invalid402ResponseError("No payment requirements found in 402 response");
212
231
  }
213
232
  function buildPaymentHeader(signature) {
package/dist/index.cjs CHANGED
@@ -822,6 +822,25 @@ function parse402Response(response) {
822
822
  throw invalid402ResponseError("Invalid WWW-Authenticate header");
823
823
  }
824
824
  }
825
+ const paymentRequired = response.headers.get("payment-required");
826
+ if (paymentRequired) {
827
+ try {
828
+ const jsonStr = atob(paymentRequired.trim());
829
+ const parsed = JSON.parse(jsonStr);
830
+ const option = Array.isArray(parsed.accepts) && parsed.accepts.length > 0 ? parsed.accepts[0] : parsed;
831
+ return {
832
+ payTo: option.payTo ?? option.recipient,
833
+ amount: String(option.amount),
834
+ asset: option.asset ?? "SOL",
835
+ network: option.network ?? "solana-mainnet",
836
+ description: parsed.description ?? option.description,
837
+ resource: parsed.resource ?? option.resource,
838
+ maxAge: parsed.maxAge ?? option.maxAge
839
+ };
840
+ } catch {
841
+ throw invalid402ResponseError("Invalid payment-required header");
842
+ }
843
+ }
825
844
  throw invalid402ResponseError("No payment requirements found in 402 response");
826
845
  }
827
846
  function buildPaymentHeader(signature) {
package/dist/index.js CHANGED
@@ -817,6 +817,25 @@ function parse402Response(response) {
817
817
  throw invalid402ResponseError("Invalid WWW-Authenticate header");
818
818
  }
819
819
  }
820
+ const paymentRequired = response.headers.get("payment-required");
821
+ if (paymentRequired) {
822
+ try {
823
+ const jsonStr = atob(paymentRequired.trim());
824
+ const parsed = JSON.parse(jsonStr);
825
+ const option = Array.isArray(parsed.accepts) && parsed.accepts.length > 0 ? parsed.accepts[0] : parsed;
826
+ return {
827
+ payTo: option.payTo ?? option.recipient,
828
+ amount: String(option.amount),
829
+ asset: option.asset ?? "SOL",
830
+ network: option.network ?? "solana-mainnet",
831
+ description: parsed.description ?? option.description,
832
+ resource: parsed.resource ?? option.resource,
833
+ maxAge: parsed.maxAge ?? option.maxAge
834
+ };
835
+ } catch {
836
+ throw invalid402ResponseError("Invalid payment-required header");
837
+ }
838
+ }
820
839
  throw invalid402ResponseError("No payment requirements found in 402 response");
821
840
  }
822
841
  function buildPaymentHeader(signature) {
@@ -233,31 +233,14 @@ function createX402Middleware(config) {
233
233
  const server$2 = new server.x402ResourceServer(facilitatorClient);
234
234
  server$1.registerExactSvmScheme(server$2);
235
235
  const networkId = config.network === "mainnet-beta" ? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" : "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
236
- return function withMicropay(handler, routeConfig) {
236
+ return function withMicropay(handler, _routeConfig) {
237
237
  const priceValue = config.price?.toString() || "0";
238
- const amountValue = (BigInt(priceValue) * BigInt(1e6)).toString();
239
- const paymentSpec = {
240
- scheme: "exact",
241
- payTo: config.walletAddress,
242
- amount: amountValue,
243
- network: networkId,
244
- asset: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"
245
- // Native SOL asset
246
- };
247
- const finalConfig = routeConfig || {
248
- accepts: {
249
- ...paymentSpec,
250
- price: priceValue
251
- // Include for x402 SDK 402 generation
252
- }
253
- };
254
- const wrappedHandler = next.withX402(handler, finalConfig, server$2);
255
238
  return async (req, ctx) => {
256
239
  const authHeader = req.headers?.get?.("authorization") || req.headers?.authorization;
257
240
  if (authHeader && authHeader.toLowerCase().startsWith("x402 ")) {
258
241
  try {
259
- const base64Payload = authHeader.slice(5);
260
- const payloadJson = Buffer.from(base64Payload, "base64").toString("utf8");
242
+ const base64Payload2 = authHeader.slice(5);
243
+ const payloadJson = Buffer.from(base64Payload2, "base64").toString("utf8");
261
244
  let payload = JSON.parse(payloadJson);
262
245
  if (payload.scheme === "exact-svm" || payload.scheme === "exact-evm") {
263
246
  payload = transformPayAIToX402(payload, networkId);
@@ -267,7 +250,7 @@ function createX402Middleware(config) {
267
250
  const verifyPaymentSpec = {
268
251
  scheme: payload.scheme || "exact",
269
252
  network: payload.network || networkId,
270
- amount: amountValue,
253
+ amount: priceValue,
271
254
  payTo: config.walletAddress
272
255
  };
273
256
  let verifyResult;
@@ -290,32 +273,33 @@ function createX402Middleware(config) {
290
273
  } catch (parseError) {
291
274
  }
292
275
  }
293
- const compatibleReq = new Proxy(req, {
294
- get(target, prop) {
295
- if (prop === "url") {
296
- return target.nextUrl?.pathname || target.url;
297
- }
298
- if (prop === "headers") {
299
- const headers = target.headers;
300
- return new Proxy(headers, {
301
- get(hTarget, hProp) {
302
- if (hProp === "authorization" || hProp === "Authorization") {
303
- const val3 = hTarget.get("authorization");
304
- return val3;
305
- }
306
- if (typeof hProp === "string" && !["get", "set", "has", "delete", "entries", "keys", "values", "forEach", "append"].includes(hProp) && typeof hTarget[hProp] === "undefined") {
307
- return hTarget.get(hProp) || void 0;
308
- }
309
- const val2 = hTarget[hProp];
310
- return typeof val2 === "function" ? val2.bind(hTarget) : val2;
311
- }
312
- });
313
- }
314
- const val = target[prop];
315
- return typeof val === "function" ? val.bind(target) : val;
276
+ const paymentPayload = {
277
+ x402Version: 2,
278
+ error: "Payment required",
279
+ resource: {
280
+ url: req.nextUrl?.pathname || req.url,
281
+ description: "",
282
+ mimeType: ""
283
+ },
284
+ accepts: [{
285
+ scheme: "exact",
286
+ network: networkId,
287
+ amount: priceValue,
288
+ // Use raw lamports, no multiplier
289
+ asset: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
290
+ payTo: config.walletAddress,
291
+ maxTimeoutSeconds: 300,
292
+ extra: { feePayer: "11111111111111111111111111111111" }
293
+ }]
294
+ };
295
+ const base64Payload = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
296
+ return new Response(JSON.stringify({}), {
297
+ status: 402,
298
+ headers: {
299
+ "Content-Type": "application/json",
300
+ "payment-required": base64Payload
316
301
  }
317
302
  });
318
- return await wrappedHandler(compatibleReq, ctx);
319
303
  };
320
304
  };
321
305
  }
@@ -19,7 +19,7 @@ interface X402Config {
19
19
  /**
20
20
  * Create a specialized Next.js middleware with Solana support pre-configured
21
21
  */
22
- declare function createX402Middleware(config: X402Config): (handler: any, routeConfig?: any) => (req: any, ctx: any) => Promise<any>;
22
+ declare function createX402Middleware(config: X402Config): (handler: any, _routeConfig?: any) => (req: any, ctx: any) => Promise<any>;
23
23
  declare const withX402: typeof withX402$1;
24
24
 
25
25
  export { type X402Config, createX402Middleware, withX402 };
@@ -19,7 +19,7 @@ interface X402Config {
19
19
  /**
20
20
  * Create a specialized Next.js middleware with Solana support pre-configured
21
21
  */
22
- declare function createX402Middleware(config: X402Config): (handler: any, routeConfig?: any) => (req: any, ctx: any) => Promise<any>;
22
+ declare function createX402Middleware(config: X402Config): (handler: any, _routeConfig?: any) => (req: any, ctx: any) => Promise<any>;
23
23
  declare const withX402: typeof withX402$1;
24
24
 
25
25
  export { type X402Config, createX402Middleware, withX402 };
@@ -232,31 +232,14 @@ function createX402Middleware(config) {
232
232
  const server = new x402ResourceServer(facilitatorClient);
233
233
  registerExactSvmScheme(server);
234
234
  const networkId = config.network === "mainnet-beta" ? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" : "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
235
- return function withMicropay(handler, routeConfig) {
235
+ return function withMicropay(handler, _routeConfig) {
236
236
  const priceValue = config.price?.toString() || "0";
237
- const amountValue = (BigInt(priceValue) * BigInt(1e6)).toString();
238
- const paymentSpec = {
239
- scheme: "exact",
240
- payTo: config.walletAddress,
241
- amount: amountValue,
242
- network: networkId,
243
- asset: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"
244
- // Native SOL asset
245
- };
246
- const finalConfig = routeConfig || {
247
- accepts: {
248
- ...paymentSpec,
249
- price: priceValue
250
- // Include for x402 SDK 402 generation
251
- }
252
- };
253
- const wrappedHandler = withX402$1(handler, finalConfig, server);
254
237
  return async (req, ctx) => {
255
238
  const authHeader = req.headers?.get?.("authorization") || req.headers?.authorization;
256
239
  if (authHeader && authHeader.toLowerCase().startsWith("x402 ")) {
257
240
  try {
258
- const base64Payload = authHeader.slice(5);
259
- const payloadJson = Buffer.from(base64Payload, "base64").toString("utf8");
241
+ const base64Payload2 = authHeader.slice(5);
242
+ const payloadJson = Buffer.from(base64Payload2, "base64").toString("utf8");
260
243
  let payload = JSON.parse(payloadJson);
261
244
  if (payload.scheme === "exact-svm" || payload.scheme === "exact-evm") {
262
245
  payload = transformPayAIToX402(payload, networkId);
@@ -266,7 +249,7 @@ function createX402Middleware(config) {
266
249
  const verifyPaymentSpec = {
267
250
  scheme: payload.scheme || "exact",
268
251
  network: payload.network || networkId,
269
- amount: amountValue,
252
+ amount: priceValue,
270
253
  payTo: config.walletAddress
271
254
  };
272
255
  let verifyResult;
@@ -289,32 +272,33 @@ function createX402Middleware(config) {
289
272
  } catch (parseError) {
290
273
  }
291
274
  }
292
- const compatibleReq = new Proxy(req, {
293
- get(target, prop) {
294
- if (prop === "url") {
295
- return target.nextUrl?.pathname || target.url;
296
- }
297
- if (prop === "headers") {
298
- const headers = target.headers;
299
- return new Proxy(headers, {
300
- get(hTarget, hProp) {
301
- if (hProp === "authorization" || hProp === "Authorization") {
302
- const val3 = hTarget.get("authorization");
303
- return val3;
304
- }
305
- if (typeof hProp === "string" && !["get", "set", "has", "delete", "entries", "keys", "values", "forEach", "append"].includes(hProp) && typeof hTarget[hProp] === "undefined") {
306
- return hTarget.get(hProp) || void 0;
307
- }
308
- const val2 = hTarget[hProp];
309
- return typeof val2 === "function" ? val2.bind(hTarget) : val2;
310
- }
311
- });
312
- }
313
- const val = target[prop];
314
- return typeof val === "function" ? val.bind(target) : val;
275
+ const paymentPayload = {
276
+ x402Version: 2,
277
+ error: "Payment required",
278
+ resource: {
279
+ url: req.nextUrl?.pathname || req.url,
280
+ description: "",
281
+ mimeType: ""
282
+ },
283
+ accepts: [{
284
+ scheme: "exact",
285
+ network: networkId,
286
+ amount: priceValue,
287
+ // Use raw lamports, no multiplier
288
+ asset: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
289
+ payTo: config.walletAddress,
290
+ maxTimeoutSeconds: 300,
291
+ extra: { feePayer: "11111111111111111111111111111111" }
292
+ }]
293
+ };
294
+ const base64Payload = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
295
+ return new Response(JSON.stringify({}), {
296
+ status: 402,
297
+ headers: {
298
+ "Content-Type": "application/json",
299
+ "payment-required": base64Payload
315
300
  }
316
301
  });
317
- return await wrappedHandler(compatibleReq, ctx);
318
302
  };
319
303
  };
320
304
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alleyboss/micropay-solana-x402-paywall",
3
- "version": "3.5.1",
3
+ "version": "3.5.2",
4
4
  "description": "Production-ready Solana micropayments library wrapper for official x402 SDK",
5
5
  "author": "AlleyBoss",
6
6
  "license": "MIT",