@mixrpay/merchant-sdk 0.1.0 → 0.2.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 +186 -88
- package/dist/index.js +498 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +496 -3
- package/dist/index.mjs.map +1 -1
- package/dist/middleware/express.js +259 -1
- package/dist/middleware/express.js.map +1 -1
- package/dist/middleware/express.mjs +258 -1
- package/dist/middleware/express.mjs.map +1 -1
- package/dist/middleware/fastify.js +1 -1
- package/dist/middleware/fastify.js.map +1 -1
- package/dist/middleware/fastify.mjs +1 -1
- package/dist/middleware/fastify.mjs.map +1 -1
- package/dist/middleware/nextjs.js +238 -1
- package/dist/middleware/nextjs.js.map +1 -1
- package/dist/middleware/nextjs.mjs +237 -1
- package/dist/middleware/nextjs.mjs.map +1 -1
- package/package.json +4 -1
- package/dist/index.d.mts +0 -291
- package/dist/index.d.ts +0 -291
- package/dist/middleware/express.d.mts +0 -57
- package/dist/middleware/express.d.ts +0 -57
- package/dist/middleware/fastify.d.mts +0 -56
- package/dist/middleware/fastify.d.ts +0 -56
- package/dist/middleware/nextjs.d.mts +0 -78
- package/dist/middleware/nextjs.d.ts +0 -78
- package/dist/types-BJNy6Hhb.d.mts +0 -166
- package/dist/types-BJNy6Hhb.d.ts +0 -166
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var express_exports = {};
|
|
22
22
|
__export(express_exports, {
|
|
23
23
|
default: () => x402,
|
|
24
|
+
mixrpay: () => mixrpay,
|
|
24
25
|
x402: () => x402
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(express_exports);
|
|
@@ -191,7 +192,7 @@ async function verifyX402Payment(paymentHeader, options) {
|
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
// src/receipt-fetcher.ts
|
|
194
|
-
var DEFAULT_MIXRPAY_API_URL = process.env.MIXRPAY_BASE_URL || "
|
|
195
|
+
var DEFAULT_MIXRPAY_API_URL = process.env.MIXRPAY_BASE_URL || "https://mixrpay.com";
|
|
195
196
|
async function fetchPaymentReceipt(params) {
|
|
196
197
|
const apiUrl = params.apiUrl || DEFAULT_MIXRPAY_API_URL;
|
|
197
198
|
const endpoint = `${apiUrl}/api/v1/receipts`;
|
|
@@ -319,8 +320,265 @@ function x402(options) {
|
|
|
319
320
|
}
|
|
320
321
|
};
|
|
321
322
|
}
|
|
323
|
+
function mixrpay(options) {
|
|
324
|
+
return async (req, res, next) => {
|
|
325
|
+
try {
|
|
326
|
+
const context = {
|
|
327
|
+
path: req.path,
|
|
328
|
+
method: req.method,
|
|
329
|
+
headers: req.headers,
|
|
330
|
+
body: req.body
|
|
331
|
+
};
|
|
332
|
+
if (options.skip) {
|
|
333
|
+
const shouldSkip = await options.skip(context);
|
|
334
|
+
if (shouldSkip) {
|
|
335
|
+
return next();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const priceUsd = options.getPrice ? await options.getPrice(context) : options.priceUsd;
|
|
339
|
+
const sessionHeader = req.headers["x-mixr-session"];
|
|
340
|
+
const widgetHeader = req.headers["x-mixr-payment"];
|
|
341
|
+
const x402Header = req.headers["x-payment"];
|
|
342
|
+
let result;
|
|
343
|
+
if (sessionHeader) {
|
|
344
|
+
result = await verifySessionPayment(sessionHeader, priceUsd, options, req);
|
|
345
|
+
} else if (widgetHeader) {
|
|
346
|
+
result = await verifyWidgetPayment(widgetHeader, priceUsd, options, req);
|
|
347
|
+
} else if (x402Header) {
|
|
348
|
+
result = await verifyX402PaymentUnified(x402Header, priceUsd, options, res);
|
|
349
|
+
} else {
|
|
350
|
+
return returnPaymentRequired(priceUsd, options, res);
|
|
351
|
+
}
|
|
352
|
+
if (!result.valid) {
|
|
353
|
+
return res.status(402).json({
|
|
354
|
+
error: "Invalid payment",
|
|
355
|
+
reason: result.error,
|
|
356
|
+
method: result.method
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
req.mixrPayment = result;
|
|
360
|
+
if (result.method === "x402" && result.x402Result) {
|
|
361
|
+
req.x402Payment = result.x402Result;
|
|
362
|
+
}
|
|
363
|
+
if (result.txHash) {
|
|
364
|
+
res.setHeader("X-Payment-TxHash", result.txHash);
|
|
365
|
+
}
|
|
366
|
+
if (result.chargeId) {
|
|
367
|
+
res.setHeader("X-Mixr-Charge-Id", result.chargeId);
|
|
368
|
+
}
|
|
369
|
+
if (result.amountUsd !== void 0) {
|
|
370
|
+
res.setHeader("X-Mixr-Charged", result.amountUsd.toString());
|
|
371
|
+
}
|
|
372
|
+
if (options.onPayment) {
|
|
373
|
+
await options.onPayment(result);
|
|
374
|
+
}
|
|
375
|
+
next();
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error("[mixrpay] Middleware error:", error);
|
|
378
|
+
return res.status(500).json({
|
|
379
|
+
error: "Payment processing error",
|
|
380
|
+
message: error.message
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
async function verifySessionPayment(sessionId, priceUsd, options, req) {
|
|
386
|
+
const baseUrl = options.mixrpayApiUrl || process.env.MIXRPAY_API_URL || "https://mixrpay.com";
|
|
387
|
+
const secretKey = process.env.MIXRPAY_SECRET_KEY;
|
|
388
|
+
if (!secretKey) {
|
|
389
|
+
return {
|
|
390
|
+
valid: false,
|
|
391
|
+
method: "session",
|
|
392
|
+
error: "MIXRPAY_SECRET_KEY not configured"
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
const response = await fetch(`${baseUrl}/api/v2/charge`, {
|
|
397
|
+
method: "POST",
|
|
398
|
+
headers: {
|
|
399
|
+
"Content-Type": "application/json",
|
|
400
|
+
"Authorization": `Bearer ${secretKey}`
|
|
401
|
+
},
|
|
402
|
+
body: JSON.stringify({
|
|
403
|
+
sessionId,
|
|
404
|
+
amountUsd: priceUsd,
|
|
405
|
+
feature: options.feature || req.headers["x-mixr-feature"],
|
|
406
|
+
idempotencyKey: req.headers["x-idempotency-key"]
|
|
407
|
+
})
|
|
408
|
+
});
|
|
409
|
+
if (!response.ok) {
|
|
410
|
+
const errorData = await response.json().catch(() => ({}));
|
|
411
|
+
return {
|
|
412
|
+
valid: false,
|
|
413
|
+
method: "session",
|
|
414
|
+
error: errorData.message || errorData.error || `Charge failed: ${response.status}`,
|
|
415
|
+
sessionId
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
const data = await response.json();
|
|
419
|
+
return {
|
|
420
|
+
valid: true,
|
|
421
|
+
method: "session",
|
|
422
|
+
payer: data.payer || data.walletAddress,
|
|
423
|
+
amountUsd: data.amountUsd || priceUsd,
|
|
424
|
+
txHash: data.txHash,
|
|
425
|
+
chargeId: data.chargeId,
|
|
426
|
+
sessionId,
|
|
427
|
+
feature: options.feature,
|
|
428
|
+
settledAt: data.settledAt ? new Date(data.settledAt) : /* @__PURE__ */ new Date()
|
|
429
|
+
};
|
|
430
|
+
} catch (error) {
|
|
431
|
+
return {
|
|
432
|
+
valid: false,
|
|
433
|
+
method: "session",
|
|
434
|
+
error: `Session verification failed: ${error.message}`,
|
|
435
|
+
sessionId
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async function verifyWidgetPayment(paymentJwt, priceUsd, options, req) {
|
|
440
|
+
const baseUrl = options.mixrpayApiUrl || process.env.MIXRPAY_API_URL || "https://mixrpay.com";
|
|
441
|
+
const secretKey = process.env.MIXRPAY_SECRET_KEY;
|
|
442
|
+
if (!secretKey) {
|
|
443
|
+
return {
|
|
444
|
+
valid: false,
|
|
445
|
+
method: "widget",
|
|
446
|
+
error: "MIXRPAY_SECRET_KEY not configured"
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
const response = await fetch(`${baseUrl}/api/widget/verify`, {
|
|
451
|
+
method: "POST",
|
|
452
|
+
headers: {
|
|
453
|
+
"Content-Type": "application/json",
|
|
454
|
+
"Authorization": `Bearer ${secretKey}`
|
|
455
|
+
},
|
|
456
|
+
body: JSON.stringify({
|
|
457
|
+
paymentJwt,
|
|
458
|
+
expectedAmountUsd: priceUsd,
|
|
459
|
+
feature: options.feature || req.headers["x-mixr-feature"]
|
|
460
|
+
})
|
|
461
|
+
});
|
|
462
|
+
if (!response.ok) {
|
|
463
|
+
const errorData = await response.json().catch(() => ({}));
|
|
464
|
+
return {
|
|
465
|
+
valid: false,
|
|
466
|
+
method: "widget",
|
|
467
|
+
error: errorData.message || errorData.error || `Widget verification failed: ${response.status}`
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const data = await response.json();
|
|
471
|
+
return {
|
|
472
|
+
valid: true,
|
|
473
|
+
method: "widget",
|
|
474
|
+
payer: data.payer || data.walletAddress,
|
|
475
|
+
amountUsd: data.amountUsd || priceUsd,
|
|
476
|
+
txHash: data.txHash,
|
|
477
|
+
chargeId: data.chargeId,
|
|
478
|
+
feature: options.feature,
|
|
479
|
+
settledAt: data.settledAt ? new Date(data.settledAt) : /* @__PURE__ */ new Date()
|
|
480
|
+
};
|
|
481
|
+
} catch (error) {
|
|
482
|
+
return {
|
|
483
|
+
valid: false,
|
|
484
|
+
method: "widget",
|
|
485
|
+
error: `Widget verification failed: ${error.message}`
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
async function verifyX402PaymentUnified(paymentHeader, priceUsd, options, res) {
|
|
490
|
+
const recipient = options.recipient || process.env.MIXRPAY_MERCHANT_ADDRESS;
|
|
491
|
+
if (!recipient) {
|
|
492
|
+
return {
|
|
493
|
+
valid: false,
|
|
494
|
+
method: "x402",
|
|
495
|
+
error: "MIXRPAY_MERCHANT_ADDRESS not configured for x402 payments"
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
const priceMinor = usdToMinor(priceUsd);
|
|
499
|
+
const chainId = options.chainId || 8453;
|
|
500
|
+
const facilitator = options.facilitator || DEFAULT_FACILITATOR;
|
|
501
|
+
const x402Result = await verifyX402Payment(paymentHeader, {
|
|
502
|
+
expectedAmount: priceMinor,
|
|
503
|
+
expectedRecipient: recipient,
|
|
504
|
+
chainId,
|
|
505
|
+
facilitator,
|
|
506
|
+
skipSettlement: options.testMode
|
|
507
|
+
});
|
|
508
|
+
if (!x402Result.valid) {
|
|
509
|
+
return {
|
|
510
|
+
valid: false,
|
|
511
|
+
method: "x402",
|
|
512
|
+
error: x402Result.error,
|
|
513
|
+
x402Result
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
const receiptMode = options.receiptMode || "webhook";
|
|
517
|
+
if ((receiptMode === "jwt" || receiptMode === "both") && x402Result.txHash && x402Result.payer) {
|
|
518
|
+
try {
|
|
519
|
+
const receipt = await fetchPaymentReceipt({
|
|
520
|
+
txHash: x402Result.txHash,
|
|
521
|
+
payer: x402Result.payer,
|
|
522
|
+
recipient,
|
|
523
|
+
amount: priceMinor,
|
|
524
|
+
chainId,
|
|
525
|
+
apiUrl: options.mixrpayApiUrl
|
|
526
|
+
});
|
|
527
|
+
if (receipt) {
|
|
528
|
+
x402Result.receipt = receipt;
|
|
529
|
+
res.setHeader("X-Payment-Receipt", receipt);
|
|
530
|
+
}
|
|
531
|
+
} catch (receiptError) {
|
|
532
|
+
console.warn("[mixrpay] Failed to fetch JWT receipt:", receiptError);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
valid: true,
|
|
537
|
+
method: "x402",
|
|
538
|
+
payer: x402Result.payer,
|
|
539
|
+
amountUsd: x402Result.amount,
|
|
540
|
+
txHash: x402Result.txHash,
|
|
541
|
+
receipt: x402Result.receipt,
|
|
542
|
+
settledAt: x402Result.settledAt,
|
|
543
|
+
x402Result
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function returnPaymentRequired(priceUsd, options, res) {
|
|
547
|
+
const priceMinor = usdToMinor(priceUsd);
|
|
548
|
+
const recipient = options.recipient || process.env.MIXRPAY_MERCHANT_ADDRESS;
|
|
549
|
+
const chainId = options.chainId || 8453;
|
|
550
|
+
const facilitator = options.facilitator || DEFAULT_FACILITATOR;
|
|
551
|
+
const nonce = generateNonce();
|
|
552
|
+
const expiresAt = Math.floor(Date.now() / 1e3) + 300;
|
|
553
|
+
const x402PaymentRequired = recipient ? {
|
|
554
|
+
recipient,
|
|
555
|
+
amount: priceMinor.toString(),
|
|
556
|
+
currency: "USDC",
|
|
557
|
+
chainId,
|
|
558
|
+
facilitator,
|
|
559
|
+
nonce,
|
|
560
|
+
expiresAt,
|
|
561
|
+
description: options.description
|
|
562
|
+
} : null;
|
|
563
|
+
if (x402PaymentRequired) {
|
|
564
|
+
res.setHeader("X-Payment-Required", JSON.stringify(x402PaymentRequired));
|
|
565
|
+
res.setHeader("WWW-Authenticate", `X-402 ${Buffer.from(JSON.stringify(x402PaymentRequired)).toString("base64")}`);
|
|
566
|
+
}
|
|
567
|
+
return res.status(402).json({
|
|
568
|
+
error: "Payment required",
|
|
569
|
+
priceUsd,
|
|
570
|
+
acceptedMethods: [
|
|
571
|
+
{ method: "session", header: "X-Mixr-Session", description: "Session authorization ID" },
|
|
572
|
+
{ method: "widget", header: "X-Mixr-Payment", description: "Widget payment JWT" },
|
|
573
|
+
...recipient ? [{ method: "x402", header: "X-PAYMENT", description: "x402 protocol payment" }] : []
|
|
574
|
+
],
|
|
575
|
+
x402: x402PaymentRequired,
|
|
576
|
+
description: options.description
|
|
577
|
+
});
|
|
578
|
+
}
|
|
322
579
|
// Annotate the CommonJS export names for ESM import in node:
|
|
323
580
|
0 && (module.exports = {
|
|
581
|
+
mixrpay,
|
|
324
582
|
x402
|
|
325
583
|
});
|
|
326
584
|
//# sourceMappingURL=express.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/middleware/express.ts","../../src/verify.ts","../../src/utils.ts","../../src/receipt-fetcher.ts"],"sourcesContent":["/**\n * MixrPay Merchant SDK - Express Middleware\n * \n * Add x402 payment requirement to Express routes with a single line of code.\n * \n * @example\n * ```typescript\n * import express from 'express';\n * import { x402 } from '@mixrpay/merchant-sdk/express';\n * \n * const app = express();\n * \n * // Fixed price\n * app.post('/api/query', x402({ price: 0.05 }), (req, res) => {\n * // This handler only runs after payment is verified\n * res.json({ result: 'success', payer: req.x402Payment?.payer });\n * });\n * \n * // Dynamic pricing\n * app.post('/api/generate', x402({ \n * getPrice: (ctx) => ctx.body?.premium ? 0.10 : 0.05 \n * }), handler);\n * \n * // With JWT receipts\n * app.post('/api/premium', x402({ price: 0.10, receiptMode: 'jwt' }), (req, res) => {\n * // Receipt is set in X-Payment-Receipt header\n * res.json({ receipt: req.x402Payment?.receipt });\n * });\n * ```\n */\n\nimport type { Request, Response, NextFunction } from 'express';\nimport { verifyX402Payment } from '../verify';\nimport { usdToMinor, generateNonce, DEFAULT_FACILITATOR } from '../utils';\nimport { fetchPaymentReceipt } from '../receipt-fetcher';\nimport type { X402Options, X402PaymentResult, X402PaymentRequired, PriceContext } from '../types';\n\n// Extend Express Request type\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n /** Payment result after x402 verification */\n x402Payment?: X402PaymentResult;\n }\n }\n}\n\n/**\n * Express middleware that adds x402 payment requirement to a route.\n * \n * When a request comes in without a valid X-PAYMENT header, it returns\n * a 402 Payment Required response with the payment details.\n * \n * When a valid X-PAYMENT header is provided, it verifies the payment\n * and calls the next handler with `req.x402Payment` containing the result.\n * \n * @param options - x402 configuration options\n * @returns Express middleware function\n */\nexport function x402(options: X402Options) {\n return async (req: Request, res: Response, next: NextFunction) => {\n try {\n // Build context for callbacks\n const context: PriceContext = {\n path: req.path,\n method: req.method,\n headers: req.headers as Record<string, string | string[] | undefined>,\n body: req.body,\n };\n\n // Check if we should skip payment\n if (options.skip) {\n const shouldSkip = await options.skip(context);\n if (shouldSkip) {\n return next();\n }\n }\n\n // Get recipient address\n const recipient = options.recipient || process.env.MIXRPAY_MERCHANT_ADDRESS;\n if (!recipient) {\n console.error('[x402] No recipient address configured. Set MIXRPAY_MERCHANT_ADDRESS env var or pass recipient option.');\n return res.status(500).json({ \n error: 'Payment configuration error',\n message: 'Merchant wallet address not configured',\n });\n }\n\n // Determine price\n const price = options.getPrice \n ? await options.getPrice(context) \n : options.price;\n \n const priceMinor = usdToMinor(price);\n const chainId = options.chainId || 8453;\n const facilitator = options.facilitator || DEFAULT_FACILITATOR;\n\n // Check for X-PAYMENT header\n const paymentHeader = req.headers['x-payment'] as string | undefined;\n\n if (!paymentHeader) {\n // Return 402 with payment requirements\n const nonce = generateNonce();\n const expiresAt = Math.floor(Date.now() / 1000) + 300; // 5 minutes\n\n const paymentRequired: X402PaymentRequired = {\n recipient,\n amount: priceMinor.toString(),\n currency: 'USDC',\n chainId,\n facilitator,\n nonce,\n expiresAt,\n description: options.description,\n };\n\n // Set headers per x402 spec\n res.setHeader('X-Payment-Required', JSON.stringify(paymentRequired));\n res.setHeader('WWW-Authenticate', `X-402 ${Buffer.from(JSON.stringify(paymentRequired)).toString('base64')}`);\n\n return res.status(402).json({\n error: 'Payment required',\n payment: paymentRequired,\n });\n }\n\n // Verify the payment\n const result = await verifyX402Payment(paymentHeader, {\n expectedAmount: priceMinor,\n expectedRecipient: recipient,\n chainId,\n facilitator,\n skipSettlement: options.testMode,\n });\n\n if (!result.valid) {\n return res.status(402).json({\n error: 'Invalid payment',\n reason: result.error,\n });\n }\n\n // Payment verified! Attach to request\n req.x402Payment = result;\n\n // Set response header with tx hash\n if (result.txHash) {\n res.setHeader('X-Payment-TxHash', result.txHash);\n }\n\n // Handle receipt mode\n const receiptMode = options.receiptMode || 'webhook';\n \n if ((receiptMode === 'jwt' || receiptMode === 'both') && result.txHash && result.payer) {\n try {\n const receipt = await fetchPaymentReceipt({\n txHash: result.txHash,\n payer: result.payer,\n recipient,\n amount: priceMinor,\n chainId,\n apiUrl: options.mixrpayApiUrl,\n });\n \n if (receipt) {\n result.receipt = receipt;\n res.setHeader('X-Payment-Receipt', receipt);\n }\n } catch (receiptError) {\n console.warn('[x402] Failed to fetch JWT receipt:', receiptError);\n // Continue without receipt - payment was still successful\n }\n }\n\n // Call onPayment callback\n if (options.onPayment) {\n await options.onPayment(result);\n }\n\n next();\n } catch (error) {\n console.error('[x402] Middleware error:', error);\n return res.status(500).json({\n error: 'Payment processing error',\n message: (error as Error).message,\n });\n }\n };\n}\n\n// Re-export for convenience\nexport { x402 as default };\nexport type { X402Options, X402PaymentResult };\n\n","/**\n * MixrPay Merchant SDK - Payment Verification\n */\n\nimport { recoverTypedDataAddress } from 'viem';\nimport type { \n X402PaymentResult, \n X402PaymentPayload, \n VerifyOptions,\n TransferWithAuthorizationMessage,\n} from './types';\nimport { \n getUSDCDomain, \n TRANSFER_WITH_AUTHORIZATION_TYPES, \n base64Decode,\n minorToUsd,\n DEFAULT_FACILITATOR,\n} from './utils';\n\n// =============================================================================\n// Payment Verification\n// =============================================================================\n\n/**\n * Verify an X-PAYMENT header and optionally settle the payment.\n * \n * @param paymentHeader - The X-PAYMENT header value (base64 encoded JSON)\n * @param options - Verification options\n * @returns Payment verification result\n * \n * @example\n * ```typescript\n * const result = await verifyX402Payment(req.headers['x-payment'], {\n * expectedAmount: 50000n, // 0.05 USDC\n * expectedRecipient: '0x...',\n * });\n * \n * if (result.valid) {\n * console.log(`Payment from ${result.payer}: $${result.amount}`);\n * }\n * ```\n */\nexport async function verifyX402Payment(\n paymentHeader: string,\n options: VerifyOptions\n): Promise<X402PaymentResult> {\n try {\n // 1. Decode the X-PAYMENT header\n let decoded: X402PaymentPayload;\n try {\n const jsonStr = base64Decode(paymentHeader);\n decoded = JSON.parse(jsonStr);\n } catch (e) {\n return { valid: false, error: 'Invalid payment header encoding' };\n }\n\n // 2. Validate payload structure\n if (!decoded.payload?.authorization || !decoded.payload?.signature) {\n return { valid: false, error: 'Missing authorization or signature in payment' };\n }\n\n const { authorization, signature } = decoded.payload;\n const chainId = options.chainId || 8453;\n\n // 3. Build EIP-712 message for verification\n const message: TransferWithAuthorizationMessage = {\n from: authorization.from as `0x${string}`,\n to: authorization.to as `0x${string}`,\n value: BigInt(authorization.value),\n validAfter: BigInt(authorization.validAfter),\n validBefore: BigInt(authorization.validBefore),\n nonce: authorization.nonce as `0x${string}`,\n };\n\n // 4. Recover signer address\n const domain = getUSDCDomain(chainId);\n let signerAddress: string;\n \n try {\n signerAddress = await recoverTypedDataAddress({\n domain,\n types: TRANSFER_WITH_AUTHORIZATION_TYPES,\n primaryType: 'TransferWithAuthorization',\n message,\n signature: signature as `0x${string}`,\n });\n } catch (e) {\n return { valid: false, error: 'Failed to recover signer from signature' };\n }\n\n // 5. Verify signer matches the 'from' address\n if (signerAddress.toLowerCase() !== authorization.from.toLowerCase()) {\n return { \n valid: false, \n error: `Signature mismatch: expected ${authorization.from}, got ${signerAddress}` \n };\n }\n\n // 6. Verify payment amount\n const paymentAmount = BigInt(authorization.value);\n if (paymentAmount < options.expectedAmount) {\n return { \n valid: false, \n error: `Insufficient payment: expected ${options.expectedAmount}, got ${paymentAmount}` \n };\n }\n\n // 7. Verify recipient\n if (authorization.to.toLowerCase() !== options.expectedRecipient.toLowerCase()) {\n return { \n valid: false, \n error: `Wrong recipient: expected ${options.expectedRecipient}, got ${authorization.to}` \n };\n }\n\n // 8. Check expiration\n const now = Math.floor(Date.now() / 1000);\n if (Number(authorization.validBefore) < now) {\n return { valid: false, error: 'Payment authorization has expired' };\n }\n\n // 9. Check validAfter\n if (Number(authorization.validAfter) > now) {\n return { valid: false, error: 'Payment authorization is not yet valid' };\n }\n\n // 10. Submit to facilitator for settlement (unless skipped)\n let txHash: string | undefined;\n let settledAt: Date | undefined;\n\n if (!options.skipSettlement) {\n const facilitatorUrl = options.facilitator || DEFAULT_FACILITATOR;\n \n try {\n const settlementResponse = await fetch(`${facilitatorUrl}/settle`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ \n authorization, \n signature,\n chainId,\n }),\n });\n\n if (!settlementResponse.ok) {\n const errorBody = await settlementResponse.text();\n let errorMessage = 'Settlement failed';\n try {\n const errorJson = JSON.parse(errorBody);\n errorMessage = errorJson.message || errorJson.error || errorMessage;\n } catch {\n errorMessage = errorBody || errorMessage;\n }\n return { valid: false, error: `Settlement failed: ${errorMessage}` };\n }\n\n const settlement = await settlementResponse.json() as { txHash?: string; tx_hash?: string };\n txHash = settlement.txHash || settlement.tx_hash;\n settledAt = new Date();\n } catch (e) {\n return { \n valid: false, \n error: `Settlement request failed: ${(e as Error).message}` \n };\n }\n }\n\n // Success!\n return {\n valid: true,\n payer: authorization.from,\n amount: minorToUsd(paymentAmount),\n amountMinor: paymentAmount,\n txHash,\n settledAt: settledAt || new Date(),\n nonce: authorization.nonce,\n };\n\n } catch (error) {\n return { \n valid: false, \n error: `Verification error: ${(error as Error).message}` \n };\n }\n}\n\n/**\n * Parse and validate an X-PAYMENT header without settlement.\n * Useful for checking if a payment is structurally valid before processing.\n */\nexport async function parseX402Payment(\n paymentHeader: string,\n chainId: number = 8453\n): Promise<{\n valid: boolean;\n error?: string;\n payer?: string;\n recipient?: string;\n amount?: number;\n amountMinor?: bigint;\n expiresAt?: Date;\n}> {\n try {\n const jsonStr = base64Decode(paymentHeader);\n const decoded: X402PaymentPayload = JSON.parse(jsonStr);\n\n if (!decoded.payload?.authorization) {\n return { valid: false, error: 'Missing authorization in payment' };\n }\n\n const { authorization, signature } = decoded.payload;\n\n // Verify signature\n const domain = getUSDCDomain(chainId);\n const message: TransferWithAuthorizationMessage = {\n from: authorization.from as `0x${string}`,\n to: authorization.to as `0x${string}`,\n value: BigInt(authorization.value),\n validAfter: BigInt(authorization.validAfter),\n validBefore: BigInt(authorization.validBefore),\n nonce: authorization.nonce as `0x${string}`,\n };\n\n const signerAddress = await recoverTypedDataAddress({\n domain,\n types: TRANSFER_WITH_AUTHORIZATION_TYPES,\n primaryType: 'TransferWithAuthorization',\n message,\n signature: signature as `0x${string}`,\n });\n\n if (signerAddress.toLowerCase() !== authorization.from.toLowerCase()) {\n return { valid: false, error: 'Signature mismatch' };\n }\n\n const amountMinor = BigInt(authorization.value);\n \n return {\n valid: true,\n payer: authorization.from,\n recipient: authorization.to,\n amount: minorToUsd(amountMinor),\n amountMinor,\n expiresAt: new Date(Number(authorization.validBefore) * 1000),\n };\n } catch (error) {\n return { valid: false, error: `Parse error: ${(error as Error).message}` };\n }\n}\n\n","/**\n * MixrPay Merchant SDK - Utilities\n */\n\nimport type { EIP712Domain } from './types';\n\n// =============================================================================\n// USDC Constants by Chain\n// =============================================================================\n\nexport const USDC_CONTRACTS: Record<number, `0x${string}`> = {\n 8453: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // Base Mainnet\n 84532: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // Base Sepolia\n};\n\nexport const DEFAULT_FACILITATOR = 'https://x402.org/facilitator';\n\n// =============================================================================\n// EIP-712 Domain\n// =============================================================================\n\nexport function getUSDCDomain(chainId: number): EIP712Domain {\n const verifyingContract = USDC_CONTRACTS[chainId];\n if (!verifyingContract) {\n throw new Error(`Unsupported chain ID: ${chainId}. Supported: ${Object.keys(USDC_CONTRACTS).join(', ')}`);\n }\n\n return {\n name: 'USD Coin',\n version: '2',\n chainId,\n verifyingContract,\n };\n}\n\n// EIP-712 types for TransferWithAuthorization\nexport const TRANSFER_WITH_AUTHORIZATION_TYPES = {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n} as const;\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Convert USD dollars to USDC minor units (6 decimals)\n */\nexport function usdToMinor(usd: number): bigint {\n return BigInt(Math.round(usd * 1_000_000));\n}\n\n/**\n * Convert USDC minor units to USD dollars\n */\nexport function minorToUsd(minor: bigint): number {\n return Number(minor) / 1_000_000;\n}\n\n/**\n * Generate a random nonce for x402 payments\n */\nexport function generateNonce(): `0x${string}` {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return `0x${Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')}` as `0x${string}`;\n}\n\n/**\n * Check if an address is valid\n */\nexport function isValidAddress(address: string): address is `0x${string}` {\n return /^0x[a-fA-F0-9]{40}$/.test(address);\n}\n\n/**\n * Normalize an address to lowercase checksum format\n */\nexport function normalizeAddress(address: string): `0x${string}` {\n if (!isValidAddress(address)) {\n throw new Error(`Invalid address: ${address}`);\n }\n return address.toLowerCase() as `0x${string}`;\n}\n\n/**\n * Safe base64 decode that works in both Node.js and browsers\n */\nexport function base64Decode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n return atob(str);\n}\n\n/**\n * Safe base64 encode that works in both Node.js and browsers\n */\nexport function base64Encode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n return btoa(str);\n}\n\n","/**\n * MixrPay Merchant SDK - Receipt Fetcher\n * \n * Internal utility to fetch JWT receipts from MixrPay API after settlement.\n * Used by x402 middleware to get receipts for the X-Payment-Receipt header.\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst DEFAULT_MIXRPAY_API_URL = process.env.MIXRPAY_BASE_URL || 'http://localhost:3000';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface FetchReceiptParams {\n /** Settlement transaction hash */\n txHash: string;\n /** Payer address */\n payer: string;\n /** Recipient address */\n recipient: string;\n /** Amount in USDC minor units */\n amount: bigint;\n /** Chain ID */\n chainId: number;\n /** Custom MixrPay API URL (optional) */\n apiUrl?: string;\n}\n\ninterface ReceiptResponse {\n receipt: string;\n expiresAt: string;\n}\n\n// =============================================================================\n// Receipt Fetching\n// =============================================================================\n\n/**\n * Fetch a JWT payment receipt from MixrPay API.\n * \n * This is called by the x402 middleware after successful payment verification\n * when receiptMode is 'jwt' or 'both'.\n * \n * @param params - Receipt request parameters\n * @returns JWT receipt string, or null if not available\n */\nexport async function fetchPaymentReceipt(params: FetchReceiptParams): Promise<string | null> {\n const apiUrl = params.apiUrl || DEFAULT_MIXRPAY_API_URL;\n const endpoint = `${apiUrl}/api/v1/receipts`;\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n tx_hash: params.txHash,\n payer: params.payer,\n recipient: params.recipient,\n amount: params.amount.toString(),\n chain_id: params.chainId,\n }),\n });\n\n if (!response.ok) {\n // Log but don't throw - receipt is optional\n console.warn(`[x402] Receipt fetch failed: ${response.status} ${response.statusText}`);\n return null;\n }\n\n const data = await response.json() as ReceiptResponse;\n return data.receipt || null;\n } catch (error) {\n // Log but don't throw - payment was successful, receipt is optional\n console.warn('[x402] Receipt fetch error:', (error as Error).message);\n return null;\n }\n}\n\n/**\n * Generate a local receipt for testing purposes.\n * \n * WARNING: This generates an unsigned receipt that will NOT verify.\n * Use only for local development and testing.\n * \n * @param params - Receipt parameters\n * @returns Mock JWT-like string (not cryptographically signed)\n */\nexport function generateMockReceipt(params: FetchReceiptParams): string {\n const header = {\n alg: 'none',\n typ: 'JWT',\n };\n\n const payload = {\n paymentId: `mock_${Date.now()}`,\n amount: params.amount.toString(),\n amountUsd: Number(params.amount) / 1_000_000,\n payer: params.payer,\n recipient: params.recipient,\n chainId: params.chainId,\n txHash: params.txHash,\n settledAt: new Date().toISOString(),\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + 3600,\n _mock: true,\n };\n\n const base64Header = Buffer.from(JSON.stringify(header)).toString('base64url');\n const base64Payload = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // Mock signature (not cryptographically valid)\n return `${base64Header}.${base64Payload}.mock_signature_for_testing_only`;\n}\n\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/middleware/express.ts","../../src/verify.ts","../../src/utils.ts","../../src/receipt-fetcher.ts"],"sourcesContent":["/**\n * MixrPay Merchant SDK - Express Middleware\n * \n * Add x402 payment requirement to Express routes with a single line of code.\n * \n * @example\n * ```typescript\n * import express from 'express';\n * import { x402 } from '@mixrpay/merchant-sdk/express';\n * \n * const app = express();\n * \n * // Fixed price\n * app.post('/api/query', x402({ price: 0.05 }), (req, res) => {\n * // This handler only runs after payment is verified\n * res.json({ result: 'success', payer: req.x402Payment?.payer });\n * });\n * \n * // Dynamic pricing\n * app.post('/api/generate', x402({ \n * getPrice: (ctx) => ctx.body?.premium ? 0.10 : 0.05 \n * }), handler);\n * \n * // With JWT receipts\n * app.post('/api/premium', x402({ price: 0.10, receiptMode: 'jwt' }), (req, res) => {\n * // Receipt is set in X-Payment-Receipt header\n * res.json({ receipt: req.x402Payment?.receipt });\n * });\n * ```\n */\n\nimport type { Request, Response, NextFunction } from 'express';\nimport { verifyX402Payment } from '../verify';\nimport { usdToMinor, generateNonce, DEFAULT_FACILITATOR } from '../utils';\nimport { fetchPaymentReceipt } from '../receipt-fetcher';\nimport type { X402Options, X402PaymentResult, X402PaymentRequired, PriceContext, MixrPayOptions, MixrPayPaymentResult, PaymentMethod } from '../types';\n\n// Extend Express Request type\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n /** Payment result after x402 verification */\n x402Payment?: X402PaymentResult;\n /** Unified payment result (session, widget, or x402) */\n mixrPayment?: MixrPayPaymentResult;\n }\n }\n}\n\n/**\n * Express middleware that adds x402 payment requirement to a route.\n * \n * When a request comes in without a valid X-PAYMENT header, it returns\n * a 402 Payment Required response with the payment details.\n * \n * When a valid X-PAYMENT header is provided, it verifies the payment\n * and calls the next handler with `req.x402Payment` containing the result.\n * \n * @param options - x402 configuration options\n * @returns Express middleware function\n */\nexport function x402(options: X402Options) {\n return async (req: Request, res: Response, next: NextFunction) => {\n try {\n // Build context for callbacks\n const context: PriceContext = {\n path: req.path,\n method: req.method,\n headers: req.headers as Record<string, string | string[] | undefined>,\n body: req.body,\n };\n\n // Check if we should skip payment\n if (options.skip) {\n const shouldSkip = await options.skip(context);\n if (shouldSkip) {\n return next();\n }\n }\n\n // Get recipient address\n const recipient = options.recipient || process.env.MIXRPAY_MERCHANT_ADDRESS;\n if (!recipient) {\n console.error('[x402] No recipient address configured. Set MIXRPAY_MERCHANT_ADDRESS env var or pass recipient option.');\n return res.status(500).json({ \n error: 'Payment configuration error',\n message: 'Merchant wallet address not configured',\n });\n }\n\n // Determine price\n const price = options.getPrice \n ? await options.getPrice(context) \n : options.price;\n \n const priceMinor = usdToMinor(price);\n const chainId = options.chainId || 8453;\n const facilitator = options.facilitator || DEFAULT_FACILITATOR;\n\n // Check for X-PAYMENT header\n const paymentHeader = req.headers['x-payment'] as string | undefined;\n\n if (!paymentHeader) {\n // Return 402 with payment requirements\n const nonce = generateNonce();\n const expiresAt = Math.floor(Date.now() / 1000) + 300; // 5 minutes\n\n const paymentRequired: X402PaymentRequired = {\n recipient,\n amount: priceMinor.toString(),\n currency: 'USDC',\n chainId,\n facilitator,\n nonce,\n expiresAt,\n description: options.description,\n };\n\n // Set headers per x402 spec\n res.setHeader('X-Payment-Required', JSON.stringify(paymentRequired));\n res.setHeader('WWW-Authenticate', `X-402 ${Buffer.from(JSON.stringify(paymentRequired)).toString('base64')}`);\n\n return res.status(402).json({\n error: 'Payment required',\n payment: paymentRequired,\n });\n }\n\n // Verify the payment\n const result = await verifyX402Payment(paymentHeader, {\n expectedAmount: priceMinor,\n expectedRecipient: recipient,\n chainId,\n facilitator,\n skipSettlement: options.testMode,\n });\n\n if (!result.valid) {\n return res.status(402).json({\n error: 'Invalid payment',\n reason: result.error,\n });\n }\n\n // Payment verified! Attach to request\n req.x402Payment = result;\n\n // Set response header with tx hash\n if (result.txHash) {\n res.setHeader('X-Payment-TxHash', result.txHash);\n }\n\n // Handle receipt mode\n const receiptMode = options.receiptMode || 'webhook';\n \n if ((receiptMode === 'jwt' || receiptMode === 'both') && result.txHash && result.payer) {\n try {\n const receipt = await fetchPaymentReceipt({\n txHash: result.txHash,\n payer: result.payer,\n recipient,\n amount: priceMinor,\n chainId,\n apiUrl: options.mixrpayApiUrl,\n });\n \n if (receipt) {\n result.receipt = receipt;\n res.setHeader('X-Payment-Receipt', receipt);\n }\n } catch (receiptError) {\n console.warn('[x402] Failed to fetch JWT receipt:', receiptError);\n // Continue without receipt - payment was still successful\n }\n }\n\n // Call onPayment callback\n if (options.onPayment) {\n await options.onPayment(result);\n }\n\n next();\n } catch (error) {\n console.error('[x402] Middleware error:', error);\n return res.status(500).json({\n error: 'Payment processing error',\n message: (error as Error).message,\n });\n }\n };\n}\n\n// =============================================================================\n// Unified MixrPay Middleware\n// =============================================================================\n\n/**\n * Unified Express middleware that accepts payments from multiple sources:\n * - Session Authorizations (X-Mixr-Session header)\n * - Widget payments (X-Mixr-Payment header)\n * - x402 Protocol (X-PAYMENT header)\n * \n * This is the recommended middleware for new integrations.\n * \n * @example\n * ```typescript\n * import express from 'express';\n * import { mixrpay } from '@mixrpay/merchant-sdk/express';\n * \n * const app = express();\n * \n * // Accept payments from any MixrPay client\n * app.post('/api/query', mixrpay({ priceUsd: 0.05 }), (req, res) => {\n * const { payer, amountUsd, method } = req.mixrPayment!;\n * console.log(`Paid $${amountUsd} via ${method} from ${payer}`);\n * res.json({ result: 'success' });\n * });\n * ```\n */\nexport function mixrpay(options: MixrPayOptions) {\n return async (req: Request, res: Response, next: NextFunction) => {\n try {\n // Build context for callbacks\n const context: PriceContext = {\n path: req.path,\n method: req.method,\n headers: req.headers as Record<string, string | string[] | undefined>,\n body: req.body,\n };\n\n // Check if we should skip payment\n if (options.skip) {\n const shouldSkip = await options.skip(context);\n if (shouldSkip) {\n return next();\n }\n }\n\n // Determine price\n const priceUsd = options.getPrice\n ? await options.getPrice(context)\n : options.priceUsd;\n\n // Check headers in priority order\n const sessionHeader = req.headers['x-mixr-session'] as string | undefined;\n const widgetHeader = req.headers['x-mixr-payment'] as string | undefined;\n const x402Header = req.headers['x-payment'] as string | undefined;\n\n let result: MixrPayPaymentResult;\n\n // 1. Session Authorization (highest priority - agent SDK)\n if (sessionHeader) {\n result = await verifySessionPayment(sessionHeader, priceUsd, options, req);\n }\n // 2. Widget Payment (human users)\n else if (widgetHeader) {\n result = await verifyWidgetPayment(widgetHeader, priceUsd, options, req);\n }\n // 3. x402 Protocol (external agents)\n else if (x402Header) {\n result = await verifyX402PaymentUnified(x402Header, priceUsd, options, res);\n }\n // No payment header - return 402\n else {\n return returnPaymentRequired(priceUsd, options, res);\n }\n\n if (!result.valid) {\n return res.status(402).json({\n error: 'Invalid payment',\n reason: result.error,\n method: result.method,\n });\n }\n\n // Payment verified! Attach to request\n req.mixrPayment = result;\n\n // Also set x402Payment for backwards compatibility if using x402\n if (result.method === 'x402' && result.x402Result) {\n req.x402Payment = result.x402Result;\n }\n\n // Set response headers\n if (result.txHash) {\n res.setHeader('X-Payment-TxHash', result.txHash);\n }\n if (result.chargeId) {\n res.setHeader('X-Mixr-Charge-Id', result.chargeId);\n }\n if (result.amountUsd !== undefined) {\n res.setHeader('X-Mixr-Charged', result.amountUsd.toString());\n }\n\n // Call onPayment callback\n if (options.onPayment) {\n await options.onPayment(result);\n }\n\n next();\n } catch (error) {\n console.error('[mixrpay] Middleware error:', error);\n return res.status(500).json({\n error: 'Payment processing error',\n message: (error as Error).message,\n });\n }\n };\n}\n\n/**\n * Verify a session authorization payment.\n */\nasync function verifySessionPayment(\n sessionId: string,\n priceUsd: number,\n options: MixrPayOptions,\n req: Request\n): Promise<MixrPayPaymentResult> {\n const baseUrl = options.mixrpayApiUrl || process.env.MIXRPAY_API_URL || 'https://mixrpay.com';\n const secretKey = process.env.MIXRPAY_SECRET_KEY;\n\n if (!secretKey) {\n return {\n valid: false,\n method: 'session',\n error: 'MIXRPAY_SECRET_KEY not configured',\n };\n }\n\n try {\n // Charge against the session\n const response = await fetch(`${baseUrl}/api/v2/charge`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${secretKey}`,\n },\n body: JSON.stringify({\n sessionId,\n amountUsd: priceUsd,\n feature: options.feature || req.headers['x-mixr-feature'],\n idempotencyKey: req.headers['x-idempotency-key'],\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n return {\n valid: false,\n method: 'session',\n error: errorData.message || errorData.error || `Charge failed: ${response.status}`,\n sessionId,\n };\n }\n\n const data = await response.json();\n\n return {\n valid: true,\n method: 'session',\n payer: data.payer || data.walletAddress,\n amountUsd: data.amountUsd || priceUsd,\n txHash: data.txHash,\n chargeId: data.chargeId,\n sessionId,\n feature: options.feature,\n settledAt: data.settledAt ? new Date(data.settledAt) : new Date(),\n };\n } catch (error) {\n return {\n valid: false,\n method: 'session',\n error: `Session verification failed: ${(error as Error).message}`,\n sessionId,\n };\n }\n}\n\n/**\n * Verify a widget payment JWT.\n */\nasync function verifyWidgetPayment(\n paymentJwt: string,\n priceUsd: number,\n options: MixrPayOptions,\n req: Request\n): Promise<MixrPayPaymentResult> {\n const baseUrl = options.mixrpayApiUrl || process.env.MIXRPAY_API_URL || 'https://mixrpay.com';\n const secretKey = process.env.MIXRPAY_SECRET_KEY;\n\n if (!secretKey) {\n return {\n valid: false,\n method: 'widget',\n error: 'MIXRPAY_SECRET_KEY not configured',\n };\n }\n\n try {\n // Verify the widget payment JWT\n const response = await fetch(`${baseUrl}/api/widget/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${secretKey}`,\n },\n body: JSON.stringify({\n paymentJwt,\n expectedAmountUsd: priceUsd,\n feature: options.feature || req.headers['x-mixr-feature'],\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n return {\n valid: false,\n method: 'widget',\n error: errorData.message || errorData.error || `Widget verification failed: ${response.status}`,\n };\n }\n\n const data = await response.json();\n\n return {\n valid: true,\n method: 'widget',\n payer: data.payer || data.walletAddress,\n amountUsd: data.amountUsd || priceUsd,\n txHash: data.txHash,\n chargeId: data.chargeId,\n feature: options.feature,\n settledAt: data.settledAt ? new Date(data.settledAt) : new Date(),\n };\n } catch (error) {\n return {\n valid: false,\n method: 'widget',\n error: `Widget verification failed: ${(error as Error).message}`,\n };\n }\n}\n\n/**\n * Verify an x402 payment header.\n */\nasync function verifyX402PaymentUnified(\n paymentHeader: string,\n priceUsd: number,\n options: MixrPayOptions,\n res: Response\n): Promise<MixrPayPaymentResult> {\n const recipient = options.recipient || process.env.MIXRPAY_MERCHANT_ADDRESS;\n\n if (!recipient) {\n return {\n valid: false,\n method: 'x402',\n error: 'MIXRPAY_MERCHANT_ADDRESS not configured for x402 payments',\n };\n }\n\n const priceMinor = usdToMinor(priceUsd);\n const chainId = options.chainId || 8453;\n const facilitator = options.facilitator || DEFAULT_FACILITATOR;\n\n const x402Result = await verifyX402Payment(paymentHeader, {\n expectedAmount: priceMinor,\n expectedRecipient: recipient,\n chainId,\n facilitator,\n skipSettlement: options.testMode,\n });\n\n if (!x402Result.valid) {\n return {\n valid: false,\n method: 'x402',\n error: x402Result.error,\n x402Result,\n };\n }\n\n // Fetch JWT receipt if needed\n const receiptMode = options.receiptMode || 'webhook';\n if ((receiptMode === 'jwt' || receiptMode === 'both') && x402Result.txHash && x402Result.payer) {\n try {\n const receipt = await fetchPaymentReceipt({\n txHash: x402Result.txHash,\n payer: x402Result.payer,\n recipient,\n amount: priceMinor,\n chainId,\n apiUrl: options.mixrpayApiUrl,\n });\n\n if (receipt) {\n x402Result.receipt = receipt;\n res.setHeader('X-Payment-Receipt', receipt);\n }\n } catch (receiptError) {\n console.warn('[mixrpay] Failed to fetch JWT receipt:', receiptError);\n }\n }\n\n return {\n valid: true,\n method: 'x402',\n payer: x402Result.payer,\n amountUsd: x402Result.amount,\n txHash: x402Result.txHash,\n receipt: x402Result.receipt,\n settledAt: x402Result.settledAt,\n x402Result,\n };\n}\n\n/**\n * Return 402 Payment Required response with multiple payment options.\n */\nfunction returnPaymentRequired(\n priceUsd: number,\n options: MixrPayOptions,\n res: Response\n): Response {\n const priceMinor = usdToMinor(priceUsd);\n const recipient = options.recipient || process.env.MIXRPAY_MERCHANT_ADDRESS;\n const chainId = options.chainId || 8453;\n const facilitator = options.facilitator || DEFAULT_FACILITATOR;\n const nonce = generateNonce();\n const expiresAt = Math.floor(Date.now() / 1000) + 300; // 5 minutes\n\n // x402 payment requirements (for x402-enabled agents)\n const x402PaymentRequired: X402PaymentRequired | null = recipient ? {\n recipient,\n amount: priceMinor.toString(),\n currency: 'USDC',\n chainId,\n facilitator,\n nonce,\n expiresAt,\n description: options.description,\n } : null;\n\n // Set headers\n if (x402PaymentRequired) {\n res.setHeader('X-Payment-Required', JSON.stringify(x402PaymentRequired));\n res.setHeader('WWW-Authenticate', `X-402 ${Buffer.from(JSON.stringify(x402PaymentRequired)).toString('base64')}`);\n }\n\n return res.status(402).json({\n error: 'Payment required',\n priceUsd,\n acceptedMethods: [\n { method: 'session', header: 'X-Mixr-Session', description: 'Session authorization ID' },\n { method: 'widget', header: 'X-Mixr-Payment', description: 'Widget payment JWT' },\n ...(recipient ? [{ method: 'x402', header: 'X-PAYMENT', description: 'x402 protocol payment' }] : []),\n ],\n x402: x402PaymentRequired,\n description: options.description,\n });\n}\n\n// Re-export for convenience\nexport { x402 as default };\nexport type { X402Options, X402PaymentResult, MixrPayOptions, MixrPayPaymentResult };\n\n","/**\n * MixrPay Merchant SDK - Payment Verification\n */\n\nimport { recoverTypedDataAddress } from 'viem';\nimport type { \n X402PaymentResult, \n X402PaymentPayload, \n VerifyOptions,\n TransferWithAuthorizationMessage,\n} from './types';\nimport { \n getUSDCDomain, \n TRANSFER_WITH_AUTHORIZATION_TYPES, \n base64Decode,\n minorToUsd,\n DEFAULT_FACILITATOR,\n} from './utils';\n\n// =============================================================================\n// Payment Verification\n// =============================================================================\n\n/**\n * Verify an X-PAYMENT header and optionally settle the payment.\n * \n * @param paymentHeader - The X-PAYMENT header value (base64 encoded JSON)\n * @param options - Verification options\n * @returns Payment verification result\n * \n * @example\n * ```typescript\n * const result = await verifyX402Payment(req.headers['x-payment'], {\n * expectedAmount: 50000n, // 0.05 USDC\n * expectedRecipient: '0x...',\n * });\n * \n * if (result.valid) {\n * console.log(`Payment from ${result.payer}: $${result.amount}`);\n * }\n * ```\n */\nexport async function verifyX402Payment(\n paymentHeader: string,\n options: VerifyOptions\n): Promise<X402PaymentResult> {\n try {\n // 1. Decode the X-PAYMENT header\n let decoded: X402PaymentPayload;\n try {\n const jsonStr = base64Decode(paymentHeader);\n decoded = JSON.parse(jsonStr);\n } catch (e) {\n return { valid: false, error: 'Invalid payment header encoding' };\n }\n\n // 2. Validate payload structure\n if (!decoded.payload?.authorization || !decoded.payload?.signature) {\n return { valid: false, error: 'Missing authorization or signature in payment' };\n }\n\n const { authorization, signature } = decoded.payload;\n const chainId = options.chainId || 8453;\n\n // 3. Build EIP-712 message for verification\n const message: TransferWithAuthorizationMessage = {\n from: authorization.from as `0x${string}`,\n to: authorization.to as `0x${string}`,\n value: BigInt(authorization.value),\n validAfter: BigInt(authorization.validAfter),\n validBefore: BigInt(authorization.validBefore),\n nonce: authorization.nonce as `0x${string}`,\n };\n\n // 4. Recover signer address\n const domain = getUSDCDomain(chainId);\n let signerAddress: string;\n \n try {\n signerAddress = await recoverTypedDataAddress({\n domain,\n types: TRANSFER_WITH_AUTHORIZATION_TYPES,\n primaryType: 'TransferWithAuthorization',\n message,\n signature: signature as `0x${string}`,\n });\n } catch (e) {\n return { valid: false, error: 'Failed to recover signer from signature' };\n }\n\n // 5. Verify signer matches the 'from' address\n if (signerAddress.toLowerCase() !== authorization.from.toLowerCase()) {\n return { \n valid: false, \n error: `Signature mismatch: expected ${authorization.from}, got ${signerAddress}` \n };\n }\n\n // 6. Verify payment amount\n const paymentAmount = BigInt(authorization.value);\n if (paymentAmount < options.expectedAmount) {\n return { \n valid: false, \n error: `Insufficient payment: expected ${options.expectedAmount}, got ${paymentAmount}` \n };\n }\n\n // 7. Verify recipient\n if (authorization.to.toLowerCase() !== options.expectedRecipient.toLowerCase()) {\n return { \n valid: false, \n error: `Wrong recipient: expected ${options.expectedRecipient}, got ${authorization.to}` \n };\n }\n\n // 8. Check expiration\n const now = Math.floor(Date.now() / 1000);\n if (Number(authorization.validBefore) < now) {\n return { valid: false, error: 'Payment authorization has expired' };\n }\n\n // 9. Check validAfter\n if (Number(authorization.validAfter) > now) {\n return { valid: false, error: 'Payment authorization is not yet valid' };\n }\n\n // 10. Submit to facilitator for settlement (unless skipped)\n let txHash: string | undefined;\n let settledAt: Date | undefined;\n\n if (!options.skipSettlement) {\n const facilitatorUrl = options.facilitator || DEFAULT_FACILITATOR;\n \n try {\n const settlementResponse = await fetch(`${facilitatorUrl}/settle`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ \n authorization, \n signature,\n chainId,\n }),\n });\n\n if (!settlementResponse.ok) {\n const errorBody = await settlementResponse.text();\n let errorMessage = 'Settlement failed';\n try {\n const errorJson = JSON.parse(errorBody);\n errorMessage = errorJson.message || errorJson.error || errorMessage;\n } catch {\n errorMessage = errorBody || errorMessage;\n }\n return { valid: false, error: `Settlement failed: ${errorMessage}` };\n }\n\n const settlement = await settlementResponse.json() as { txHash?: string; tx_hash?: string };\n txHash = settlement.txHash || settlement.tx_hash;\n settledAt = new Date();\n } catch (e) {\n return { \n valid: false, \n error: `Settlement request failed: ${(e as Error).message}` \n };\n }\n }\n\n // Success!\n return {\n valid: true,\n payer: authorization.from,\n amount: minorToUsd(paymentAmount),\n amountMinor: paymentAmount,\n txHash,\n settledAt: settledAt || new Date(),\n nonce: authorization.nonce,\n };\n\n } catch (error) {\n return { \n valid: false, \n error: `Verification error: ${(error as Error).message}` \n };\n }\n}\n\n/**\n * Parse and validate an X-PAYMENT header without settlement.\n * Useful for checking if a payment is structurally valid before processing.\n */\nexport async function parseX402Payment(\n paymentHeader: string,\n chainId: number = 8453\n): Promise<{\n valid: boolean;\n error?: string;\n payer?: string;\n recipient?: string;\n amount?: number;\n amountMinor?: bigint;\n expiresAt?: Date;\n}> {\n try {\n const jsonStr = base64Decode(paymentHeader);\n const decoded: X402PaymentPayload = JSON.parse(jsonStr);\n\n if (!decoded.payload?.authorization) {\n return { valid: false, error: 'Missing authorization in payment' };\n }\n\n const { authorization, signature } = decoded.payload;\n\n // Verify signature\n const domain = getUSDCDomain(chainId);\n const message: TransferWithAuthorizationMessage = {\n from: authorization.from as `0x${string}`,\n to: authorization.to as `0x${string}`,\n value: BigInt(authorization.value),\n validAfter: BigInt(authorization.validAfter),\n validBefore: BigInt(authorization.validBefore),\n nonce: authorization.nonce as `0x${string}`,\n };\n\n const signerAddress = await recoverTypedDataAddress({\n domain,\n types: TRANSFER_WITH_AUTHORIZATION_TYPES,\n primaryType: 'TransferWithAuthorization',\n message,\n signature: signature as `0x${string}`,\n });\n\n if (signerAddress.toLowerCase() !== authorization.from.toLowerCase()) {\n return { valid: false, error: 'Signature mismatch' };\n }\n\n const amountMinor = BigInt(authorization.value);\n \n return {\n valid: true,\n payer: authorization.from,\n recipient: authorization.to,\n amount: minorToUsd(amountMinor),\n amountMinor,\n expiresAt: new Date(Number(authorization.validBefore) * 1000),\n };\n } catch (error) {\n return { valid: false, error: `Parse error: ${(error as Error).message}` };\n }\n}\n\n","/**\n * MixrPay Merchant SDK - Utilities\n */\n\nimport type { EIP712Domain } from './types';\n\n// =============================================================================\n// USDC Constants by Chain\n// =============================================================================\n\nexport const USDC_CONTRACTS: Record<number, `0x${string}`> = {\n 8453: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // Base Mainnet\n 84532: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // Base Sepolia\n};\n\nexport const DEFAULT_FACILITATOR = 'https://x402.org/facilitator';\n\n// =============================================================================\n// EIP-712 Domain\n// =============================================================================\n\nexport function getUSDCDomain(chainId: number): EIP712Domain {\n const verifyingContract = USDC_CONTRACTS[chainId];\n if (!verifyingContract) {\n throw new Error(`Unsupported chain ID: ${chainId}. Supported: ${Object.keys(USDC_CONTRACTS).join(', ')}`);\n }\n\n return {\n name: 'USD Coin',\n version: '2',\n chainId,\n verifyingContract,\n };\n}\n\n// EIP-712 types for TransferWithAuthorization\nexport const TRANSFER_WITH_AUTHORIZATION_TYPES = {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n} as const;\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Convert USD dollars to USDC minor units (6 decimals)\n */\nexport function usdToMinor(usd: number): bigint {\n return BigInt(Math.round(usd * 1_000_000));\n}\n\n/**\n * Convert USDC minor units to USD dollars\n */\nexport function minorToUsd(minor: bigint): number {\n return Number(minor) / 1_000_000;\n}\n\n/**\n * Generate a random nonce for x402 payments\n */\nexport function generateNonce(): `0x${string}` {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return `0x${Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')}` as `0x${string}`;\n}\n\n/**\n * Check if an address is valid\n */\nexport function isValidAddress(address: string): address is `0x${string}` {\n return /^0x[a-fA-F0-9]{40}$/.test(address);\n}\n\n/**\n * Normalize an address to lowercase checksum format\n */\nexport function normalizeAddress(address: string): `0x${string}` {\n if (!isValidAddress(address)) {\n throw new Error(`Invalid address: ${address}`);\n }\n return address.toLowerCase() as `0x${string}`;\n}\n\n/**\n * Safe base64 decode that works in both Node.js and browsers\n */\nexport function base64Decode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n return atob(str);\n}\n\n/**\n * Safe base64 encode that works in both Node.js and browsers\n */\nexport function base64Encode(str: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n return btoa(str);\n}\n\n","/**\n * MixrPay Merchant SDK - Receipt Fetcher\n * \n * Internal utility to fetch JWT receipts from MixrPay API after settlement.\n * Used by x402 middleware to get receipts for the X-Payment-Receipt header.\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst DEFAULT_MIXRPAY_API_URL = process.env.MIXRPAY_BASE_URL || 'https://mixrpay.com';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface FetchReceiptParams {\n /** Settlement transaction hash */\n txHash: string;\n /** Payer address */\n payer: string;\n /** Recipient address */\n recipient: string;\n /** Amount in USDC minor units */\n amount: bigint;\n /** Chain ID */\n chainId: number;\n /** Custom MixrPay API URL (optional) */\n apiUrl?: string;\n}\n\ninterface ReceiptResponse {\n receipt: string;\n expiresAt: string;\n}\n\n// =============================================================================\n// Receipt Fetching\n// =============================================================================\n\n/**\n * Fetch a JWT payment receipt from MixrPay API.\n * \n * This is called by the x402 middleware after successful payment verification\n * when receiptMode is 'jwt' or 'both'.\n * \n * @param params - Receipt request parameters\n * @returns JWT receipt string, or null if not available\n */\nexport async function fetchPaymentReceipt(params: FetchReceiptParams): Promise<string | null> {\n const apiUrl = params.apiUrl || DEFAULT_MIXRPAY_API_URL;\n const endpoint = `${apiUrl}/api/v1/receipts`;\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n tx_hash: params.txHash,\n payer: params.payer,\n recipient: params.recipient,\n amount: params.amount.toString(),\n chain_id: params.chainId,\n }),\n });\n\n if (!response.ok) {\n // Log but don't throw - receipt is optional\n console.warn(`[x402] Receipt fetch failed: ${response.status} ${response.statusText}`);\n return null;\n }\n\n const data = await response.json() as ReceiptResponse;\n return data.receipt || null;\n } catch (error) {\n // Log but don't throw - payment was successful, receipt is optional\n console.warn('[x402] Receipt fetch error:', (error as Error).message);\n return null;\n }\n}\n\n/**\n * Generate a local receipt for testing purposes.\n * \n * WARNING: This generates an unsigned receipt that will NOT verify.\n * Use only for local development and testing.\n * \n * @param params - Receipt parameters\n * @returns Mock JWT-like string (not cryptographically signed)\n */\nexport function generateMockReceipt(params: FetchReceiptParams): string {\n const header = {\n alg: 'none',\n typ: 'JWT',\n };\n\n const payload = {\n paymentId: `mock_${Date.now()}`,\n amount: params.amount.toString(),\n amountUsd: Number(params.amount) / 1_000_000,\n payer: params.payer,\n recipient: params.recipient,\n chainId: params.chainId,\n txHash: params.txHash,\n settledAt: new Date().toISOString(),\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + 3600,\n _mock: true,\n };\n\n const base64Header = Buffer.from(JSON.stringify(header)).toString('base64url');\n const base64Payload = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // Mock signature (not cryptographically valid)\n return `${base64Header}.${base64Payload}.mock_signature_for_testing_only`;\n}\n\n"],"mappings":"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","names":[]}
|