moltspay 0.8.1 → 0.8.3
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/.env.example +10 -0
- package/dist/cli/index.js +141 -52
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +160 -65
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.js +237 -148
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +256 -167
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +14 -5
- package/dist/server/index.d.ts +14 -5
- package/dist/server/index.js +151 -112
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +149 -113
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -1
package/.env.example
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# MoltsPay Server Configuration
|
|
2
|
+
# Copy to ~/.moltspay/.env or ./.env and fill in values
|
|
3
|
+
|
|
4
|
+
# Network: true = Base mainnet, false = Base Sepolia testnet
|
|
5
|
+
USE_MAINNET=true
|
|
6
|
+
|
|
7
|
+
# CDP API Credentials (required for mainnet)
|
|
8
|
+
# Get from: https://portal.cdp.coinbase.com/
|
|
9
|
+
CDP_API_KEY_ID=
|
|
10
|
+
CDP_API_KEY_SECRET=
|
package/dist/cli/index.js
CHANGED
|
@@ -367,28 +367,105 @@ var MoltsPayClient = class {
|
|
|
367
367
|
// src/server/index.ts
|
|
368
368
|
var import_fs2 = require("fs");
|
|
369
369
|
var import_http = require("http");
|
|
370
|
+
var path = __toESM(require("path"));
|
|
370
371
|
var X402_VERSION2 = 2;
|
|
371
372
|
var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
|
|
372
373
|
var PAYMENT_HEADER2 = "x-payment";
|
|
373
374
|
var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
374
|
-
var
|
|
375
|
+
var FACILITATOR_TESTNET = "https://www.x402.org/facilitator";
|
|
376
|
+
var FACILITATOR_MAINNET = "https://api.cdp.coinbase.com/platform/v2/x402";
|
|
377
|
+
var USDC_ADDRESSES = {
|
|
378
|
+
"eip155:8453": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
379
|
+
// Base mainnet
|
|
380
|
+
"eip155:84532": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
381
|
+
// Base Sepolia
|
|
382
|
+
};
|
|
383
|
+
var USDC_DOMAIN = {
|
|
384
|
+
name: "USD Coin",
|
|
385
|
+
version: "2"
|
|
386
|
+
};
|
|
387
|
+
function loadEnvFiles() {
|
|
388
|
+
try {
|
|
389
|
+
const dotenv = require("dotenv");
|
|
390
|
+
const envPaths = [
|
|
391
|
+
path.join(process.cwd(), ".env"),
|
|
392
|
+
path.join(process.env.HOME || "", ".moltspay", ".env")
|
|
393
|
+
];
|
|
394
|
+
for (const envPath of envPaths) {
|
|
395
|
+
if ((0, import_fs2.existsSync)(envPath)) {
|
|
396
|
+
dotenv.config({ path: envPath });
|
|
397
|
+
console.log(`[MoltsPay] Loaded config from ${envPath}`);
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
} catch {
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function getCDPConfig() {
|
|
405
|
+
loadEnvFiles();
|
|
406
|
+
return {
|
|
407
|
+
useMainnet: process.env.USE_MAINNET?.toLowerCase() === "true",
|
|
408
|
+
apiKeyId: process.env.CDP_API_KEY_ID,
|
|
409
|
+
apiKeySecret: process.env.CDP_API_KEY_SECRET
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
async function getCDPAuthHeaders(method, urlPath, body) {
|
|
413
|
+
const config = getCDPConfig();
|
|
414
|
+
if (!config.apiKeyId || !config.apiKeySecret) {
|
|
415
|
+
throw new Error("CDP_API_KEY_ID and CDP_API_KEY_SECRET required for mainnet");
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
|
|
419
|
+
const headers = await getAuthHeaders({
|
|
420
|
+
apiKeyId: config.apiKeyId,
|
|
421
|
+
apiKeySecret: config.apiKeySecret,
|
|
422
|
+
requestMethod: method,
|
|
423
|
+
requestHost: "api.cdp.coinbase.com",
|
|
424
|
+
requestPath: urlPath,
|
|
425
|
+
requestBody: body
|
|
426
|
+
});
|
|
427
|
+
return headers;
|
|
428
|
+
} catch (err) {
|
|
429
|
+
console.error("[MoltsPay] Failed to generate CDP auth headers:", err.message);
|
|
430
|
+
throw err;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
375
433
|
var MoltsPayServer = class {
|
|
376
434
|
manifest;
|
|
377
435
|
skills = /* @__PURE__ */ new Map();
|
|
378
436
|
options;
|
|
437
|
+
cdpConfig;
|
|
379
438
|
facilitatorUrl;
|
|
439
|
+
networkId;
|
|
380
440
|
constructor(servicesPath, options = {}) {
|
|
441
|
+
this.cdpConfig = getCDPConfig();
|
|
381
442
|
const content = (0, import_fs2.readFileSync)(servicesPath, "utf-8");
|
|
382
443
|
this.manifest = JSON.parse(content);
|
|
383
444
|
this.options = {
|
|
384
445
|
port: options.port || 3e3,
|
|
385
446
|
host: options.host || "0.0.0.0"
|
|
386
447
|
};
|
|
387
|
-
this.
|
|
448
|
+
if (this.cdpConfig.useMainnet) {
|
|
449
|
+
if (!this.cdpConfig.apiKeyId || !this.cdpConfig.apiKeySecret) {
|
|
450
|
+
console.warn("[MoltsPay] WARNING: USE_MAINNET=true but CDP keys not set!");
|
|
451
|
+
console.warn("[MoltsPay] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET in ~/.moltspay/.env");
|
|
452
|
+
}
|
|
453
|
+
this.facilitatorUrl = FACILITATOR_MAINNET;
|
|
454
|
+
this.networkId = "eip155:8453";
|
|
455
|
+
} else {
|
|
456
|
+
this.facilitatorUrl = options.facilitatorUrl || FACILITATOR_TESTNET;
|
|
457
|
+
this.networkId = "eip155:84532";
|
|
458
|
+
}
|
|
459
|
+
const networkName = this.cdpConfig.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
|
|
460
|
+
const facilitatorName = this.cdpConfig.useMainnet ? "CDP" : "x402.org";
|
|
388
461
|
console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
|
|
389
462
|
console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
|
|
390
463
|
console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
|
|
391
|
-
console.log(`[MoltsPay]
|
|
464
|
+
console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);
|
|
465
|
+
console.log(`[MoltsPay] Facilitator: ${facilitatorName} (${this.facilitatorUrl})`);
|
|
466
|
+
if (this.cdpConfig.useMainnet && this.cdpConfig.apiKeyId) {
|
|
467
|
+
console.log(`[MoltsPay] CDP API Key: ${this.cdpConfig.apiKeyId.slice(0, 8)}...`);
|
|
468
|
+
}
|
|
392
469
|
console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
|
|
393
470
|
}
|
|
394
471
|
/**
|
|
@@ -449,7 +526,6 @@ var MoltsPayServer = class {
|
|
|
449
526
|
* GET /services - List available services
|
|
450
527
|
*/
|
|
451
528
|
handleGetServices(res) {
|
|
452
|
-
const chain = getChain(this.manifest.provider.chain);
|
|
453
529
|
const services = this.manifest.services.map((s) => ({
|
|
454
530
|
id: s.id,
|
|
455
531
|
name: s.name,
|
|
@@ -465,16 +541,15 @@ var MoltsPayServer = class {
|
|
|
465
541
|
services,
|
|
466
542
|
x402: {
|
|
467
543
|
version: X402_VERSION2,
|
|
468
|
-
network:
|
|
544
|
+
network: this.networkId,
|
|
469
545
|
schemes: ["exact"],
|
|
470
|
-
facilitator: this.
|
|
546
|
+
facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
|
|
547
|
+
mainnet: this.cdpConfig.useMainnet
|
|
471
548
|
}
|
|
472
549
|
});
|
|
473
550
|
}
|
|
474
551
|
/**
|
|
475
552
|
* POST /execute - Execute service with x402 payment
|
|
476
|
-
* Body: { service: string, params: object }
|
|
477
|
-
* Header: X-Payment (optional - if missing, returns 402)
|
|
478
553
|
*/
|
|
479
554
|
async handleExecute(body, paymentHeader, res) {
|
|
480
555
|
const { service, params } = body;
|
|
@@ -549,17 +624,8 @@ var MoltsPayServer = class {
|
|
|
549
624
|
* Return 402 with x402 payment requirements
|
|
550
625
|
*/
|
|
551
626
|
sendPaymentRequired(config, res) {
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
const requirements = [{
|
|
555
|
-
scheme: "exact",
|
|
556
|
-
network: `eip155:${chain.chainId}`,
|
|
557
|
-
maxAmountRequired: amountInUnits,
|
|
558
|
-
resource: this.manifest.provider.wallet,
|
|
559
|
-
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
560
|
-
// Include facilitator info for client
|
|
561
|
-
extra: JSON.stringify({ facilitator: this.facilitatorUrl })
|
|
562
|
-
}];
|
|
627
|
+
const requirements = [this.buildPaymentRequirements(config)];
|
|
628
|
+
requirements[0].description = `${config.name} - $${config.price} ${config.currency}`;
|
|
563
629
|
const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
|
|
564
630
|
res.writeHead(402, {
|
|
565
631
|
"Content-Type": "application/json",
|
|
@@ -572,7 +638,7 @@ var MoltsPayServer = class {
|
|
|
572
638
|
}, null, 2));
|
|
573
639
|
}
|
|
574
640
|
/**
|
|
575
|
-
* Basic payment validation
|
|
641
|
+
* Basic payment validation
|
|
576
642
|
*/
|
|
577
643
|
validatePayment(payment, config) {
|
|
578
644
|
if (payment.x402Version !== X402_VERSION2) {
|
|
@@ -581,37 +647,57 @@ var MoltsPayServer = class {
|
|
|
581
647
|
if (payment.scheme !== "exact") {
|
|
582
648
|
return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };
|
|
583
649
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (payment.network !== expectedNetwork) {
|
|
587
|
-
return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };
|
|
650
|
+
if (payment.network !== this.networkId) {
|
|
651
|
+
return { valid: false, error: `Network mismatch: expected ${this.networkId}, got ${payment.network}` };
|
|
588
652
|
}
|
|
589
653
|
return { valid: true };
|
|
590
654
|
}
|
|
591
655
|
/**
|
|
592
|
-
*
|
|
656
|
+
* Build complete payment requirements for facilitator
|
|
657
|
+
*/
|
|
658
|
+
buildPaymentRequirements(config) {
|
|
659
|
+
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
660
|
+
const usdcAddress = USDC_ADDRESSES[this.networkId];
|
|
661
|
+
return {
|
|
662
|
+
scheme: "exact",
|
|
663
|
+
network: this.networkId,
|
|
664
|
+
maxAmountRequired: amountInUnits,
|
|
665
|
+
amount: amountInUnits,
|
|
666
|
+
asset: usdcAddress,
|
|
667
|
+
payTo: this.manifest.provider.wallet,
|
|
668
|
+
maxTimeoutSeconds: 300,
|
|
669
|
+
extra: USDC_DOMAIN
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Verify payment with facilitator (testnet or CDP)
|
|
593
674
|
*/
|
|
594
675
|
async verifyWithFacilitator(payment, config) {
|
|
595
676
|
try {
|
|
596
|
-
const
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
network: `eip155:${chain.chainId}`,
|
|
601
|
-
maxAmountRequired: amountInUnits,
|
|
602
|
-
resource: this.manifest.provider.wallet
|
|
677
|
+
const requirements = this.buildPaymentRequirements(config);
|
|
678
|
+
const requestBody = {
|
|
679
|
+
paymentPayload: payment,
|
|
680
|
+
paymentRequirements: requirements
|
|
603
681
|
};
|
|
682
|
+
console.log("[MoltsPay] Verify request:", JSON.stringify(requestBody, null, 2));
|
|
683
|
+
let headers = { "Content-Type": "application/json" };
|
|
684
|
+
if (this.cdpConfig.useMainnet) {
|
|
685
|
+
const authHeaders = await getCDPAuthHeaders(
|
|
686
|
+
"POST",
|
|
687
|
+
"/platform/v2/x402/verify",
|
|
688
|
+
requestBody
|
|
689
|
+
);
|
|
690
|
+
headers = { ...headers, ...authHeaders };
|
|
691
|
+
}
|
|
604
692
|
const response = await fetch(`${this.facilitatorUrl}/verify`, {
|
|
605
693
|
method: "POST",
|
|
606
|
-
headers
|
|
607
|
-
body: JSON.stringify(
|
|
608
|
-
paymentPayload: payment,
|
|
609
|
-
paymentRequirements: requirements
|
|
610
|
-
})
|
|
694
|
+
headers,
|
|
695
|
+
body: JSON.stringify(requestBody)
|
|
611
696
|
});
|
|
612
697
|
const result = await response.json();
|
|
698
|
+
console.log("[MoltsPay] Verify response:", JSON.stringify(result, null, 2));
|
|
613
699
|
if (!response.ok || !result.isValid) {
|
|
614
|
-
return { valid: false, error: result.invalidReason || "Verification failed" };
|
|
700
|
+
return { valid: false, error: result.invalidReason || result.error || "Verification failed" };
|
|
615
701
|
}
|
|
616
702
|
return { valid: true };
|
|
617
703
|
} catch (err) {
|
|
@@ -622,25 +708,28 @@ var MoltsPayServer = class {
|
|
|
622
708
|
* Settle payment with facilitator (execute on-chain transfer)
|
|
623
709
|
*/
|
|
624
710
|
async settleWithFacilitator(payment, config) {
|
|
625
|
-
const
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
network: `eip155:${chain.chainId}`,
|
|
630
|
-
maxAmountRequired: amountInUnits,
|
|
631
|
-
resource: this.manifest.provider.wallet
|
|
711
|
+
const requirements = this.buildPaymentRequirements(config);
|
|
712
|
+
const requestBody = {
|
|
713
|
+
paymentPayload: payment,
|
|
714
|
+
paymentRequirements: requirements
|
|
632
715
|
};
|
|
716
|
+
let headers = { "Content-Type": "application/json" };
|
|
717
|
+
if (this.cdpConfig.useMainnet) {
|
|
718
|
+
const authHeaders = await getCDPAuthHeaders(
|
|
719
|
+
"POST",
|
|
720
|
+
"/platform/v2/x402/settle",
|
|
721
|
+
requestBody
|
|
722
|
+
);
|
|
723
|
+
headers = { ...headers, ...authHeaders };
|
|
724
|
+
}
|
|
633
725
|
const response = await fetch(`${this.facilitatorUrl}/settle`, {
|
|
634
726
|
method: "POST",
|
|
635
|
-
headers
|
|
636
|
-
body: JSON.stringify(
|
|
637
|
-
paymentPayload: payment,
|
|
638
|
-
paymentRequirements: requirements
|
|
639
|
-
})
|
|
727
|
+
headers,
|
|
728
|
+
body: JSON.stringify(requestBody)
|
|
640
729
|
});
|
|
641
730
|
const result = await response.json();
|
|
642
|
-
if (!response.ok) {
|
|
643
|
-
throw new Error(result.error || "Settlement failed");
|
|
731
|
+
if (!response.ok || !result.success) {
|
|
732
|
+
throw new Error(result.error || result.errorReason || "Settlement failed");
|
|
644
733
|
}
|
|
645
734
|
return {
|
|
646
735
|
transaction: result.transaction,
|