moltspay 0.8.0 → 0.8.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/.env.example +10 -0
- package/dist/cli/index.js +207 -98
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +226 -111
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.js +321 -211
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +338 -228
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +23 -11
- package/dist/server/index.d.ts +23 -11
- package/dist/server/index.js +213 -153
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +211 -154
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
2
8
|
|
|
3
9
|
// src/cli/index.ts
|
|
4
10
|
import { Command } from "commander";
|
|
5
11
|
import { homedir as homedir2 } from "os";
|
|
6
|
-
import { join as
|
|
7
|
-
import { existsSync as
|
|
12
|
+
import { join as join3, dirname, resolve } from "path";
|
|
13
|
+
import { existsSync as existsSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync3, unlinkSync, mkdirSync as mkdirSync2 } from "fs";
|
|
8
14
|
import { spawn } from "child_process";
|
|
9
15
|
|
|
10
16
|
// src/client/index.ts
|
|
@@ -342,40 +348,98 @@ var MoltsPayClient = class {
|
|
|
342
348
|
};
|
|
343
349
|
|
|
344
350
|
// src/server/index.ts
|
|
345
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
351
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
346
352
|
import { createServer } from "http";
|
|
347
|
-
import
|
|
353
|
+
import * as path from "path";
|
|
348
354
|
var X402_VERSION2 = 2;
|
|
349
355
|
var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
|
|
350
356
|
var PAYMENT_HEADER2 = "x-payment";
|
|
357
|
+
var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
358
|
+
var FACILITATOR_TESTNET = "https://www.x402.org/facilitator";
|
|
359
|
+
var FACILITATOR_MAINNET = "https://api.cdp.coinbase.com/platform/v2/x402";
|
|
360
|
+
function loadEnvFiles() {
|
|
361
|
+
try {
|
|
362
|
+
const dotenv = __require("dotenv");
|
|
363
|
+
const envPaths = [
|
|
364
|
+
path.join(process.cwd(), ".env"),
|
|
365
|
+
path.join(process.env.HOME || "", ".moltspay", ".env")
|
|
366
|
+
];
|
|
367
|
+
for (const envPath of envPaths) {
|
|
368
|
+
if (existsSync2(envPath)) {
|
|
369
|
+
dotenv.config({ path: envPath });
|
|
370
|
+
console.log(`[MoltsPay] Loaded config from ${envPath}`);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
} catch {
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function getCDPConfig() {
|
|
378
|
+
loadEnvFiles();
|
|
379
|
+
return {
|
|
380
|
+
useMainnet: process.env.USE_MAINNET?.toLowerCase() === "true",
|
|
381
|
+
apiKeyId: process.env.CDP_API_KEY_ID,
|
|
382
|
+
apiKeySecret: process.env.CDP_API_KEY_SECRET
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
async function getCDPAuthHeaders(method, urlPath, body) {
|
|
386
|
+
const config = getCDPConfig();
|
|
387
|
+
if (!config.apiKeyId || !config.apiKeySecret) {
|
|
388
|
+
throw new Error("CDP_API_KEY_ID and CDP_API_KEY_SECRET required for mainnet");
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
|
|
392
|
+
const headers = await getAuthHeaders({
|
|
393
|
+
apiKeyId: config.apiKeyId,
|
|
394
|
+
apiKeySecret: config.apiKeySecret,
|
|
395
|
+
requestMethod: method,
|
|
396
|
+
requestHost: "api.cdp.coinbase.com",
|
|
397
|
+
requestPath: urlPath,
|
|
398
|
+
requestBody: body
|
|
399
|
+
});
|
|
400
|
+
return headers;
|
|
401
|
+
} catch (err) {
|
|
402
|
+
console.error("[MoltsPay] Failed to generate CDP auth headers:", err.message);
|
|
403
|
+
throw err;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
351
406
|
var MoltsPayServer = class {
|
|
352
407
|
manifest;
|
|
353
408
|
skills = /* @__PURE__ */ new Map();
|
|
354
409
|
options;
|
|
355
|
-
|
|
356
|
-
|
|
410
|
+
cdpConfig;
|
|
411
|
+
facilitatorUrl;
|
|
412
|
+
networkId;
|
|
357
413
|
constructor(servicesPath, options = {}) {
|
|
414
|
+
this.cdpConfig = getCDPConfig();
|
|
358
415
|
const content = readFileSync2(servicesPath, "utf-8");
|
|
359
416
|
this.manifest = JSON.parse(content);
|
|
360
417
|
this.options = {
|
|
361
418
|
port: options.port || 3e3,
|
|
362
|
-
host: options.host || "0.0.0.0"
|
|
363
|
-
privateKey: options.privateKey || process.env.MOLTSPAY_PRIVATE_KEY
|
|
419
|
+
host: options.host || "0.0.0.0"
|
|
364
420
|
};
|
|
365
|
-
if (this.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
this.wallet = new ethers2.Wallet(this.options.privateKey, this.provider);
|
|
370
|
-
console.log(`[MoltsPay] Payment wallet: ${this.wallet.address}`);
|
|
371
|
-
} catch (err) {
|
|
372
|
-
console.warn("[MoltsPay] Warning: Could not initialize wallet for payment claims");
|
|
421
|
+
if (this.cdpConfig.useMainnet) {
|
|
422
|
+
if (!this.cdpConfig.apiKeyId || !this.cdpConfig.apiKeySecret) {
|
|
423
|
+
console.warn("[MoltsPay] WARNING: USE_MAINNET=true but CDP keys not set!");
|
|
424
|
+
console.warn("[MoltsPay] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET in ~/.moltspay/.env");
|
|
373
425
|
}
|
|
426
|
+
this.facilitatorUrl = FACILITATOR_MAINNET;
|
|
427
|
+
this.networkId = "eip155:8453";
|
|
428
|
+
} else {
|
|
429
|
+
this.facilitatorUrl = options.facilitatorUrl || FACILITATOR_TESTNET;
|
|
430
|
+
this.networkId = "eip155:84532";
|
|
374
431
|
}
|
|
432
|
+
const networkName = this.cdpConfig.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
|
|
433
|
+
const facilitatorName = this.cdpConfig.useMainnet ? "CDP" : "x402.org";
|
|
375
434
|
console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
|
|
376
435
|
console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
|
|
377
436
|
console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
|
|
378
|
-
console.log(`[MoltsPay]
|
|
437
|
+
console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);
|
|
438
|
+
console.log(`[MoltsPay] Facilitator: ${facilitatorName} (${this.facilitatorUrl})`);
|
|
439
|
+
if (this.cdpConfig.useMainnet && this.cdpConfig.apiKeyId) {
|
|
440
|
+
console.log(`[MoltsPay] CDP API Key: ${this.cdpConfig.apiKeyId.slice(0, 8)}...`);
|
|
441
|
+
}
|
|
442
|
+
console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
|
|
379
443
|
}
|
|
380
444
|
/**
|
|
381
445
|
* Register a skill handler for a service
|
|
@@ -385,48 +449,45 @@ var MoltsPayServer = class {
|
|
|
385
449
|
if (!config) {
|
|
386
450
|
throw new Error(`Service '${serviceId}' not found in manifest`);
|
|
387
451
|
}
|
|
388
|
-
this.skills.set(serviceId, {
|
|
389
|
-
id: serviceId,
|
|
390
|
-
config,
|
|
391
|
-
handler
|
|
392
|
-
});
|
|
393
|
-
console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);
|
|
452
|
+
this.skills.set(serviceId, { id: serviceId, config, handler });
|
|
394
453
|
return this;
|
|
395
454
|
}
|
|
396
455
|
/**
|
|
397
|
-
* Start
|
|
456
|
+
* Start HTTP server
|
|
398
457
|
*/
|
|
399
458
|
listen(port) {
|
|
400
|
-
const p = port || this.options.port;
|
|
459
|
+
const p = port || this.options.port || 3e3;
|
|
460
|
+
const host = this.options.host || "0.0.0.0";
|
|
401
461
|
const server = createServer((req, res) => this.handleRequest(req, res));
|
|
402
|
-
server.listen(p,
|
|
403
|
-
console.log(`[MoltsPay] Server listening on http://${
|
|
462
|
+
server.listen(p, host, () => {
|
|
463
|
+
console.log(`[MoltsPay] Server listening on http://${host}:${p}`);
|
|
404
464
|
console.log(`[MoltsPay] Endpoints:`);
|
|
405
465
|
console.log(` GET /services - List available services`);
|
|
406
466
|
console.log(` POST /execute - Execute service (x402 payment)`);
|
|
407
467
|
});
|
|
408
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* Handle incoming request
|
|
471
|
+
*/
|
|
409
472
|
async handleRequest(req, res) {
|
|
410
|
-
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
411
|
-
const path = url.pathname;
|
|
412
|
-
const method = req.method || "GET";
|
|
413
473
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
414
474
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
415
475
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment");
|
|
416
476
|
res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response");
|
|
417
|
-
if (method === "OPTIONS") {
|
|
477
|
+
if (req.method === "OPTIONS") {
|
|
418
478
|
res.writeHead(204);
|
|
419
479
|
res.end();
|
|
420
480
|
return;
|
|
421
481
|
}
|
|
422
482
|
try {
|
|
423
|
-
|
|
483
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
484
|
+
if (url.pathname === "/services" && req.method === "GET") {
|
|
424
485
|
return this.handleGetServices(res);
|
|
425
486
|
}
|
|
426
|
-
if (
|
|
487
|
+
if (url.pathname === "/execute" && req.method === "POST") {
|
|
427
488
|
const body = await this.readBody(req);
|
|
428
489
|
const paymentHeader = req.headers[PAYMENT_HEADER2];
|
|
429
|
-
return this.handleExecute(body, paymentHeader, res);
|
|
490
|
+
return await this.handleExecute(body, paymentHeader, res);
|
|
430
491
|
}
|
|
431
492
|
this.sendJson(res, 404, { error: "Not found" });
|
|
432
493
|
} catch (err) {
|
|
@@ -438,7 +499,6 @@ var MoltsPayServer = class {
|
|
|
438
499
|
* GET /services - List available services
|
|
439
500
|
*/
|
|
440
501
|
handleGetServices(res) {
|
|
441
|
-
const chain = getChain(this.manifest.provider.chain);
|
|
442
502
|
const services = this.manifest.services.map((s) => ({
|
|
443
503
|
id: s.id,
|
|
444
504
|
name: s.name,
|
|
@@ -454,15 +514,15 @@ var MoltsPayServer = class {
|
|
|
454
514
|
services,
|
|
455
515
|
x402: {
|
|
456
516
|
version: X402_VERSION2,
|
|
457
|
-
network:
|
|
458
|
-
schemes: ["exact"]
|
|
517
|
+
network: this.networkId,
|
|
518
|
+
schemes: ["exact"],
|
|
519
|
+
facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
|
|
520
|
+
mainnet: this.cdpConfig.useMainnet
|
|
459
521
|
}
|
|
460
522
|
});
|
|
461
523
|
}
|
|
462
524
|
/**
|
|
463
525
|
* POST /execute - Execute service with x402 payment
|
|
464
|
-
* Body: { service: string, params: object }
|
|
465
|
-
* Header: X-Payment (optional - if missing, returns 402)
|
|
466
526
|
*/
|
|
467
527
|
async handleExecute(body, paymentHeader, res) {
|
|
468
528
|
const { service, params } = body;
|
|
@@ -492,6 +552,11 @@ var MoltsPayServer = class {
|
|
|
492
552
|
if (!validation.valid) {
|
|
493
553
|
return this.sendJson(res, 402, { error: validation.error });
|
|
494
554
|
}
|
|
555
|
+
console.log(`[MoltsPay] Verifying payment with facilitator...`);
|
|
556
|
+
const verifyResult = await this.verifyWithFacilitator(payment, skill.config);
|
|
557
|
+
if (!verifyResult.valid) {
|
|
558
|
+
return this.sendJson(res, 402, { error: `Payment verification failed: ${verifyResult.error}` });
|
|
559
|
+
}
|
|
495
560
|
console.log(`[MoltsPay] Executing skill: ${service}`);
|
|
496
561
|
let result;
|
|
497
562
|
try {
|
|
@@ -503,32 +568,46 @@ var MoltsPayServer = class {
|
|
|
503
568
|
message: err.message
|
|
504
569
|
});
|
|
505
570
|
}
|
|
506
|
-
console.log(`[MoltsPay] Skill succeeded,
|
|
507
|
-
let
|
|
571
|
+
console.log(`[MoltsPay] Skill succeeded, settling payment...`);
|
|
572
|
+
let settlement = null;
|
|
508
573
|
try {
|
|
509
|
-
|
|
510
|
-
console.log(`[MoltsPay] Payment
|
|
574
|
+
settlement = await this.settleWithFacilitator(payment, skill.config);
|
|
575
|
+
console.log(`[MoltsPay] Payment settled: ${settlement.transaction || "pending"}`);
|
|
511
576
|
} catch (err) {
|
|
512
|
-
console.error("[MoltsPay]
|
|
577
|
+
console.error("[MoltsPay] Settlement failed:", err.message);
|
|
578
|
+
}
|
|
579
|
+
const responseHeaders = {};
|
|
580
|
+
if (settlement) {
|
|
581
|
+
const responsePayload = {
|
|
582
|
+
success: true,
|
|
583
|
+
transaction: settlement.transaction,
|
|
584
|
+
network: payment.network
|
|
585
|
+
};
|
|
586
|
+
responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(
|
|
587
|
+
JSON.stringify(responsePayload)
|
|
588
|
+
).toString("base64");
|
|
513
589
|
}
|
|
514
590
|
this.sendJson(res, 200, {
|
|
515
591
|
success: true,
|
|
516
592
|
result,
|
|
517
|
-
payment:
|
|
518
|
-
});
|
|
593
|
+
payment: settlement ? { transaction: settlement.transaction, status: "settled" } : { status: "pending" }
|
|
594
|
+
}, responseHeaders);
|
|
519
595
|
}
|
|
520
596
|
/**
|
|
521
597
|
* Return 402 with x402 payment requirements
|
|
522
598
|
*/
|
|
523
599
|
sendPaymentRequired(config, res) {
|
|
524
|
-
const chain = getChain(this.manifest.provider.chain);
|
|
525
600
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
526
601
|
const requirements = [{
|
|
527
602
|
scheme: "exact",
|
|
528
|
-
network:
|
|
603
|
+
network: this.networkId,
|
|
529
604
|
maxAmountRequired: amountInUnits,
|
|
530
605
|
resource: this.manifest.provider.wallet,
|
|
531
|
-
description: `${config.name} - $${config.price} ${config.currency}
|
|
606
|
+
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
607
|
+
extra: JSON.stringify({
|
|
608
|
+
facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
|
|
609
|
+
mainnet: this.cdpConfig.useMainnet
|
|
610
|
+
})
|
|
532
611
|
}];
|
|
533
612
|
const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
|
|
534
613
|
res.writeHead(402, {
|
|
@@ -542,7 +621,7 @@ var MoltsPayServer = class {
|
|
|
542
621
|
}, null, 2));
|
|
543
622
|
}
|
|
544
623
|
/**
|
|
545
|
-
*
|
|
624
|
+
* Basic payment validation
|
|
546
625
|
*/
|
|
547
626
|
validatePayment(payment, config) {
|
|
548
627
|
if (payment.x402Version !== X402_VERSION2) {
|
|
@@ -551,56 +630,89 @@ var MoltsPayServer = class {
|
|
|
551
630
|
if (payment.scheme !== "exact") {
|
|
552
631
|
return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };
|
|
553
632
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
if (payment.network !== expectedNetwork) {
|
|
557
|
-
return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };
|
|
558
|
-
}
|
|
559
|
-
const auth = payment.payload.authorization;
|
|
560
|
-
if (auth.to.toLowerCase() !== this.manifest.provider.wallet.toLowerCase()) {
|
|
561
|
-
return { valid: false, error: "Payment recipient mismatch" };
|
|
562
|
-
}
|
|
563
|
-
const amount = Number(auth.value) / 1e6;
|
|
564
|
-
if (amount < config.price) {
|
|
565
|
-
return { valid: false, error: `Insufficient amount: $${amount} < $${config.price}` };
|
|
566
|
-
}
|
|
567
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
568
|
-
if (Number(auth.validBefore) < now) {
|
|
569
|
-
return { valid: false, error: "Payment authorization expired" };
|
|
570
|
-
}
|
|
571
|
-
if (Number(auth.validAfter) > now) {
|
|
572
|
-
return { valid: false, error: "Payment authorization not yet valid" };
|
|
633
|
+
if (payment.network !== this.networkId) {
|
|
634
|
+
return { valid: false, error: `Network mismatch: expected ${this.networkId}, got ${payment.network}` };
|
|
573
635
|
}
|
|
574
636
|
return { valid: true };
|
|
575
637
|
}
|
|
576
638
|
/**
|
|
577
|
-
*
|
|
639
|
+
* Verify payment with facilitator (testnet or CDP)
|
|
578
640
|
*/
|
|
579
|
-
async
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
641
|
+
async verifyWithFacilitator(payment, config) {
|
|
642
|
+
try {
|
|
643
|
+
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
644
|
+
const requirements = {
|
|
645
|
+
scheme: "exact",
|
|
646
|
+
network: this.networkId,
|
|
647
|
+
maxAmountRequired: amountInUnits,
|
|
648
|
+
resource: this.manifest.provider.wallet,
|
|
649
|
+
payTo: this.manifest.provider.wallet
|
|
650
|
+
};
|
|
651
|
+
const requestBody = {
|
|
652
|
+
paymentPayload: payment,
|
|
653
|
+
paymentRequirements: requirements
|
|
654
|
+
};
|
|
655
|
+
let headers = { "Content-Type": "application/json" };
|
|
656
|
+
if (this.cdpConfig.useMainnet) {
|
|
657
|
+
const authHeaders = await getCDPAuthHeaders(
|
|
658
|
+
"POST",
|
|
659
|
+
"/platform/v2/x402/verify",
|
|
660
|
+
requestBody
|
|
661
|
+
);
|
|
662
|
+
headers = { ...headers, ...authHeaders };
|
|
663
|
+
}
|
|
664
|
+
const response = await fetch(`${this.facilitatorUrl}/verify`, {
|
|
665
|
+
method: "POST",
|
|
666
|
+
headers,
|
|
667
|
+
body: JSON.stringify(requestBody)
|
|
668
|
+
});
|
|
669
|
+
const result = await response.json();
|
|
670
|
+
if (!response.ok || !result.isValid) {
|
|
671
|
+
return { valid: false, error: result.invalidReason || result.error || "Verification failed" };
|
|
672
|
+
}
|
|
673
|
+
return { valid: true };
|
|
674
|
+
} catch (err) {
|
|
675
|
+
return { valid: false, error: `Facilitator error: ${err.message}` };
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Settle payment with facilitator (execute on-chain transfer)
|
|
680
|
+
*/
|
|
681
|
+
async settleWithFacilitator(payment, config) {
|
|
682
|
+
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
683
|
+
const requirements = {
|
|
684
|
+
scheme: "exact",
|
|
685
|
+
network: this.networkId,
|
|
686
|
+
maxAmountRequired: amountInUnits,
|
|
687
|
+
resource: this.manifest.provider.wallet,
|
|
688
|
+
payTo: this.manifest.provider.wallet
|
|
689
|
+
};
|
|
690
|
+
const requestBody = {
|
|
691
|
+
paymentPayload: payment,
|
|
692
|
+
paymentRequirements: requirements
|
|
693
|
+
};
|
|
694
|
+
let headers = { "Content-Type": "application/json" };
|
|
695
|
+
if (this.cdpConfig.useMainnet) {
|
|
696
|
+
const authHeaders = await getCDPAuthHeaders(
|
|
697
|
+
"POST",
|
|
698
|
+
"/platform/v2/x402/settle",
|
|
699
|
+
requestBody
|
|
700
|
+
);
|
|
701
|
+
headers = { ...headers, ...authHeaders };
|
|
702
|
+
}
|
|
703
|
+
const response = await fetch(`${this.facilitatorUrl}/settle`, {
|
|
704
|
+
method: "POST",
|
|
705
|
+
headers,
|
|
706
|
+
body: JSON.stringify(requestBody)
|
|
707
|
+
});
|
|
708
|
+
const result = await response.json();
|
|
709
|
+
if (!response.ok || !result.success) {
|
|
710
|
+
throw new Error(result.error || result.errorReason || "Settlement failed");
|
|
711
|
+
}
|
|
712
|
+
return {
|
|
713
|
+
transaction: result.transaction,
|
|
714
|
+
status: result.status || "settled"
|
|
715
|
+
};
|
|
604
716
|
}
|
|
605
717
|
async readBody(req) {
|
|
606
718
|
return new Promise((resolve2, reject) => {
|
|
@@ -616,8 +728,12 @@ var MoltsPayServer = class {
|
|
|
616
728
|
req.on("error", reject);
|
|
617
729
|
});
|
|
618
730
|
}
|
|
619
|
-
sendJson(res, status, data) {
|
|
620
|
-
|
|
731
|
+
sendJson(res, status, data, extraHeaders) {
|
|
732
|
+
const headers = { "Content-Type": "application/json" };
|
|
733
|
+
if (extraHeaders) {
|
|
734
|
+
Object.assign(headers, extraHeaders);
|
|
735
|
+
}
|
|
736
|
+
res.writeHead(status, headers);
|
|
621
737
|
res.end(JSON.stringify(data, null, 2));
|
|
622
738
|
}
|
|
623
739
|
};
|
|
@@ -625,9 +741,9 @@ var MoltsPayServer = class {
|
|
|
625
741
|
// src/cli/index.ts
|
|
626
742
|
import * as readline from "readline";
|
|
627
743
|
var program = new Command();
|
|
628
|
-
var DEFAULT_CONFIG_DIR =
|
|
629
|
-
var PID_FILE =
|
|
630
|
-
if (!
|
|
744
|
+
var DEFAULT_CONFIG_DIR = join3(homedir2(), ".moltspay");
|
|
745
|
+
var PID_FILE = join3(DEFAULT_CONFIG_DIR, "server.pid");
|
|
746
|
+
if (!existsSync3(DEFAULT_CONFIG_DIR)) {
|
|
631
747
|
mkdirSync2(DEFAULT_CONFIG_DIR, { recursive: true });
|
|
632
748
|
}
|
|
633
749
|
function prompt(question) {
|
|
@@ -645,7 +761,7 @@ function prompt(question) {
|
|
|
645
761
|
program.name("moltspay").description("MoltsPay - Payment infrastructure for AI Agents").version("1.0.0");
|
|
646
762
|
program.command("init").description("Initialize MoltsPay client (create wallet, set limits)").option("--chain <chain>", "Blockchain to use", "base").option("--max-per-tx <amount>", "Max amount per transaction").option("--max-per-day <amount>", "Max amount per day").option("--config-dir <dir>", "Config directory", DEFAULT_CONFIG_DIR).action(async (options) => {
|
|
647
763
|
console.log("\n\u{1F510} MoltsPay Client Setup\n");
|
|
648
|
-
if (
|
|
764
|
+
if (existsSync3(join3(options.configDir, "wallet.json"))) {
|
|
649
765
|
console.log('\u26A0\uFE0F Already initialized. Use "moltspay config" to update settings.');
|
|
650
766
|
console.log(` Config dir: ${options.configDir}`);
|
|
651
767
|
return;
|
|
@@ -672,7 +788,7 @@ program.command("init").description("Initialize MoltsPay client (create wallet,
|
|
|
672
788
|
console.log(`
|
|
673
789
|
\u{1F4C1} Config saved to: ${result.configDir}`);
|
|
674
790
|
console.log(`
|
|
675
|
-
\u26A0\uFE0F IMPORTANT: Back up ${
|
|
791
|
+
\u26A0\uFE0F IMPORTANT: Back up ${join3(result.configDir, "wallet.json")}`);
|
|
676
792
|
console.log(` This file contains your private key!
|
|
677
793
|
`);
|
|
678
794
|
console.log(`\u{1F4B0} Fund your wallet with USDC on ${chain} to start using services.
|
|
@@ -778,24 +894,23 @@ program.command("services <url>").description("List services from a provider").o
|
|
|
778
894
|
console.error("\u274C Error:", err.message);
|
|
779
895
|
}
|
|
780
896
|
});
|
|
781
|
-
program.command("start <manifest>").description("Start MoltsPay server from services manifest").option("-p, --port <port>", "Port to listen on", "3000").option("--host <host>", "Host to bind", "0.0.0.0").option("--
|
|
897
|
+
program.command("start <manifest>").description("Start MoltsPay server from services manifest").option("-p, --port <port>", "Port to listen on", "3000").option("--host <host>", "Host to bind", "0.0.0.0").option("--facilitator <url>", "x402 facilitator URL (default: https://x402.org/facilitator)").action(async (manifest, options) => {
|
|
782
898
|
const manifestPath = resolve(manifest);
|
|
783
|
-
if (!
|
|
899
|
+
if (!existsSync3(manifestPath)) {
|
|
784
900
|
console.error(`\u274C Manifest not found: ${manifestPath}`);
|
|
785
901
|
process.exit(1);
|
|
786
902
|
}
|
|
787
903
|
const port = parseInt(options.port, 10);
|
|
788
904
|
const host = options.host;
|
|
789
|
-
const
|
|
905
|
+
const facilitatorUrl = options.facilitator;
|
|
790
906
|
console.log(`
|
|
791
907
|
\u{1F680} Starting MoltsPay Server (x402 protocol)
|
|
792
908
|
`);
|
|
793
909
|
console.log(` Manifest: ${manifestPath}`);
|
|
794
910
|
console.log(` Port: ${port}`);
|
|
795
|
-
console.log(` Payment claims: ${privateKey ? "enabled" : "disabled (no private key)"}`);
|
|
796
911
|
console.log("");
|
|
797
912
|
try {
|
|
798
|
-
const server = new MoltsPayServer(manifestPath, { port, host,
|
|
913
|
+
const server = new MoltsPayServer(manifestPath, { port, host, facilitatorUrl });
|
|
799
914
|
const manifestContent = await import("fs").then(
|
|
800
915
|
(fs) => JSON.parse(fs.readFileSync(manifestPath, "utf-8"))
|
|
801
916
|
);
|
|
@@ -845,7 +960,7 @@ program.command("start <manifest>").description("Start MoltsPay server from serv
|
|
|
845
960
|
server.listen(port);
|
|
846
961
|
const cleanup = () => {
|
|
847
962
|
try {
|
|
848
|
-
if (
|
|
963
|
+
if (existsSync3(PID_FILE)) {
|
|
849
964
|
unlinkSync(PID_FILE);
|
|
850
965
|
}
|
|
851
966
|
} catch {
|
|
@@ -868,7 +983,7 @@ program.command("start <manifest>").description("Start MoltsPay server from serv
|
|
|
868
983
|
}
|
|
869
984
|
});
|
|
870
985
|
program.command("stop").description("Stop the running MoltsPay server").action(async () => {
|
|
871
|
-
if (!
|
|
986
|
+
if (!existsSync3(PID_FILE)) {
|
|
872
987
|
console.log("\u274C No running server found (no PID file)");
|
|
873
988
|
process.exit(1);
|
|
874
989
|
}
|
|
@@ -898,7 +1013,7 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
|
|
|
898
1013
|
process.kill(pid, "SIGKILL");
|
|
899
1014
|
} catch {
|
|
900
1015
|
}
|
|
901
|
-
if (
|
|
1016
|
+
if (existsSync3(PID_FILE)) {
|
|
902
1017
|
unlinkSync(PID_FILE);
|
|
903
1018
|
}
|
|
904
1019
|
console.log("\u2705 Server stopped\n");
|
|
@@ -929,7 +1044,7 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
929
1044
|
params.image_url = imagePath;
|
|
930
1045
|
} else {
|
|
931
1046
|
const filePath = resolve(imagePath);
|
|
932
|
-
if (!
|
|
1047
|
+
if (!existsSync3(filePath)) {
|
|
933
1048
|
console.error(`\u274C Image file not found: ${filePath}`);
|
|
934
1049
|
process.exit(1);
|
|
935
1050
|
}
|