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.
@@ -69,14 +69,20 @@ interface MoltsPayServerOptions {
69
69
  port?: number;
70
70
  host?: string;
71
71
  chargeExpirySecs?: number;
72
- /** Private key for claiming payments (can also use MOLTSPAY_PRIVATE_KEY env) */
73
- privateKey?: string;
72
+ /** x402 Facilitator URL (default: https://x402.org/facilitator) */
73
+ facilitatorUrl?: string;
74
74
  }
75
75
 
76
76
  /**
77
77
  * MoltsPay Server - Payment infrastructure for AI Agents
78
78
  *
79
- * Uses x402 protocol for gasless, pay-for-success payments.
79
+ * Supports both testnet (x402.org) and mainnet (CDP) facilitators.
80
+ * Server does NOT need private key - facilitator handles on-chain settlement.
81
+ *
82
+ * Environment variables (from ~/.moltspay/.env or process.env):
83
+ * USE_MAINNET=true - Use Base mainnet (requires CDP keys)
84
+ * CDP_API_KEY_ID=xxx - Coinbase Developer Platform API key ID
85
+ * CDP_API_KEY_SECRET=xxx - CDP API key secret
80
86
  *
81
87
  * Usage:
82
88
  * const server = new MoltsPayServer('./moltspay.services.json');
@@ -88,17 +94,21 @@ declare class MoltsPayServer {
88
94
  private manifest;
89
95
  private skills;
90
96
  private options;
91
- private provider;
92
- private wallet;
97
+ private cdpConfig;
98
+ private facilitatorUrl;
99
+ private networkId;
93
100
  constructor(servicesPath: string, options?: MoltsPayServerOptions);
94
101
  /**
95
102
  * Register a skill handler for a service
96
103
  */
97
104
  skill(serviceId: string, handler: SkillFunction): this;
98
105
  /**
99
- * Start the server
106
+ * Start HTTP server
100
107
  */
101
108
  listen(port?: number): void;
109
+ /**
110
+ * Handle incoming request
111
+ */
102
112
  private handleRequest;
103
113
  /**
104
114
  * GET /services - List available services
@@ -106,8 +116,6 @@ declare class MoltsPayServer {
106
116
  private handleGetServices;
107
117
  /**
108
118
  * POST /execute - Execute service with x402 payment
109
- * Body: { service: string, params: object }
110
- * Header: X-Payment (optional - if missing, returns 402)
111
119
  */
112
120
  private handleExecute;
113
121
  /**
@@ -115,13 +123,17 @@ declare class MoltsPayServer {
115
123
  */
116
124
  private sendPaymentRequired;
117
125
  /**
118
- * Validate x402 payment payload
126
+ * Basic payment validation
119
127
  */
120
128
  private validatePayment;
121
129
  /**
122
- * Claim payment using transferWithAuthorization
130
+ * Verify payment with facilitator (testnet or CDP)
131
+ */
132
+ private verifyWithFacilitator;
133
+ /**
134
+ * Settle payment with facilitator (execute on-chain transfer)
123
135
  */
124
- private claimPayment;
136
+ private settleWithFacilitator;
125
137
  private readBody;
126
138
  private sendJson;
127
139
  }
@@ -69,14 +69,20 @@ interface MoltsPayServerOptions {
69
69
  port?: number;
70
70
  host?: string;
71
71
  chargeExpirySecs?: number;
72
- /** Private key for claiming payments (can also use MOLTSPAY_PRIVATE_KEY env) */
73
- privateKey?: string;
72
+ /** x402 Facilitator URL (default: https://x402.org/facilitator) */
73
+ facilitatorUrl?: string;
74
74
  }
75
75
 
76
76
  /**
77
77
  * MoltsPay Server - Payment infrastructure for AI Agents
78
78
  *
79
- * Uses x402 protocol for gasless, pay-for-success payments.
79
+ * Supports both testnet (x402.org) and mainnet (CDP) facilitators.
80
+ * Server does NOT need private key - facilitator handles on-chain settlement.
81
+ *
82
+ * Environment variables (from ~/.moltspay/.env or process.env):
83
+ * USE_MAINNET=true - Use Base mainnet (requires CDP keys)
84
+ * CDP_API_KEY_ID=xxx - Coinbase Developer Platform API key ID
85
+ * CDP_API_KEY_SECRET=xxx - CDP API key secret
80
86
  *
81
87
  * Usage:
82
88
  * const server = new MoltsPayServer('./moltspay.services.json');
@@ -88,17 +94,21 @@ declare class MoltsPayServer {
88
94
  private manifest;
89
95
  private skills;
90
96
  private options;
91
- private provider;
92
- private wallet;
97
+ private cdpConfig;
98
+ private facilitatorUrl;
99
+ private networkId;
93
100
  constructor(servicesPath: string, options?: MoltsPayServerOptions);
94
101
  /**
95
102
  * Register a skill handler for a service
96
103
  */
97
104
  skill(serviceId: string, handler: SkillFunction): this;
98
105
  /**
99
- * Start the server
106
+ * Start HTTP server
100
107
  */
101
108
  listen(port?: number): void;
109
+ /**
110
+ * Handle incoming request
111
+ */
102
112
  private handleRequest;
103
113
  /**
104
114
  * GET /services - List available services
@@ -106,8 +116,6 @@ declare class MoltsPayServer {
106
116
  private handleGetServices;
107
117
  /**
108
118
  * POST /execute - Execute service with x402 payment
109
- * Body: { service: string, params: object }
110
- * Header: X-Payment (optional - if missing, returns 402)
111
119
  */
112
120
  private handleExecute;
113
121
  /**
@@ -115,13 +123,17 @@ declare class MoltsPayServer {
115
123
  */
116
124
  private sendPaymentRequired;
117
125
  /**
118
- * Validate x402 payment payload
126
+ * Basic payment validation
119
127
  */
120
128
  private validatePayment;
121
129
  /**
122
- * Claim payment using transferWithAuthorization
130
+ * Verify payment with facilitator (testnet or CDP)
131
+ */
132
+ private verifyWithFacilitator;
133
+ /**
134
+ * Settle payment with facilitator (execute on-chain transfer)
123
135
  */
124
- private claimPayment;
136
+ private settleWithFacilitator;
125
137
  private readBody;
126
138
  private sendJson;
127
139
  }
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/server/index.ts
@@ -25,98 +35,96 @@ __export(server_exports, {
25
35
  module.exports = __toCommonJS(server_exports);
26
36
  var import_fs = require("fs");
27
37
  var import_http = require("http");
28
- var import_ethers = require("ethers");
29
-
30
- // src/chains/index.ts
31
- var CHAINS = {
32
- // ============ Mainnet ============
33
- base: {
34
- name: "Base",
35
- chainId: 8453,
36
- rpc: "https://mainnet.base.org",
37
- usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
38
- explorer: "https://basescan.org/address/",
39
- explorerTx: "https://basescan.org/tx/",
40
- avgBlockTime: 2
41
- },
42
- polygon: {
43
- name: "Polygon",
44
- chainId: 137,
45
- rpc: "https://polygon-rpc.com",
46
- usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
47
- explorer: "https://polygonscan.com/address/",
48
- explorerTx: "https://polygonscan.com/tx/",
49
- avgBlockTime: 2
50
- },
51
- ethereum: {
52
- name: "Ethereum",
53
- chainId: 1,
54
- rpc: "https://eth.llamarpc.com",
55
- usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
56
- explorer: "https://etherscan.io/address/",
57
- explorerTx: "https://etherscan.io/tx/",
58
- avgBlockTime: 12
59
- },
60
- // ============ Testnet ============
61
- base_sepolia: {
62
- name: "Base Sepolia",
63
- chainId: 84532,
64
- rpc: "https://sepolia.base.org",
65
- usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
66
- explorer: "https://sepolia.basescan.org/address/",
67
- explorerTx: "https://sepolia.basescan.org/tx/",
68
- avgBlockTime: 2
69
- },
70
- sepolia: {
71
- name: "Sepolia",
72
- chainId: 11155111,
73
- rpc: "https://rpc.sepolia.org",
74
- usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
75
- explorer: "https://sepolia.etherscan.io/address/",
76
- explorerTx: "https://sepolia.etherscan.io/tx/",
77
- avgBlockTime: 12
78
- }
79
- };
80
- function getChain(name) {
81
- const config = CHAINS[name];
82
- if (!config) {
83
- throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(", ")}`);
84
- }
85
- return config;
86
- }
87
-
88
- // src/server/index.ts
38
+ var path = __toESM(require("path"));
89
39
  var X402_VERSION = 2;
90
40
  var PAYMENT_REQUIRED_HEADER = "x-payment-required";
91
41
  var PAYMENT_HEADER = "x-payment";
42
+ var PAYMENT_RESPONSE_HEADER = "x-payment-response";
43
+ var FACILITATOR_TESTNET = "https://www.x402.org/facilitator";
44
+ var FACILITATOR_MAINNET = "https://api.cdp.coinbase.com/platform/v2/x402";
45
+ function loadEnvFiles() {
46
+ try {
47
+ const dotenv = require("dotenv");
48
+ const envPaths = [
49
+ path.join(process.cwd(), ".env"),
50
+ path.join(process.env.HOME || "", ".moltspay", ".env")
51
+ ];
52
+ for (const envPath of envPaths) {
53
+ if ((0, import_fs.existsSync)(envPath)) {
54
+ dotenv.config({ path: envPath });
55
+ console.log(`[MoltsPay] Loaded config from ${envPath}`);
56
+ break;
57
+ }
58
+ }
59
+ } catch {
60
+ }
61
+ }
62
+ function getCDPConfig() {
63
+ loadEnvFiles();
64
+ return {
65
+ useMainnet: process.env.USE_MAINNET?.toLowerCase() === "true",
66
+ apiKeyId: process.env.CDP_API_KEY_ID,
67
+ apiKeySecret: process.env.CDP_API_KEY_SECRET
68
+ };
69
+ }
70
+ async function getCDPAuthHeaders(method, urlPath, body) {
71
+ const config = getCDPConfig();
72
+ if (!config.apiKeyId || !config.apiKeySecret) {
73
+ throw new Error("CDP_API_KEY_ID and CDP_API_KEY_SECRET required for mainnet");
74
+ }
75
+ try {
76
+ const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
77
+ const headers = await getAuthHeaders({
78
+ apiKeyId: config.apiKeyId,
79
+ apiKeySecret: config.apiKeySecret,
80
+ requestMethod: method,
81
+ requestHost: "api.cdp.coinbase.com",
82
+ requestPath: urlPath,
83
+ requestBody: body
84
+ });
85
+ return headers;
86
+ } catch (err) {
87
+ console.error("[MoltsPay] Failed to generate CDP auth headers:", err.message);
88
+ throw err;
89
+ }
90
+ }
92
91
  var MoltsPayServer = class {
93
92
  manifest;
94
93
  skills = /* @__PURE__ */ new Map();
95
94
  options;
96
- provider = null;
97
- wallet = null;
95
+ cdpConfig;
96
+ facilitatorUrl;
97
+ networkId;
98
98
  constructor(servicesPath, options = {}) {
99
+ this.cdpConfig = getCDPConfig();
99
100
  const content = (0, import_fs.readFileSync)(servicesPath, "utf-8");
100
101
  this.manifest = JSON.parse(content);
101
102
  this.options = {
102
103
  port: options.port || 3e3,
103
- host: options.host || "0.0.0.0",
104
- privateKey: options.privateKey || process.env.MOLTSPAY_PRIVATE_KEY
104
+ host: options.host || "0.0.0.0"
105
105
  };
106
- if (this.options.privateKey) {
107
- try {
108
- const chain = getChain(this.manifest.provider.chain);
109
- this.provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
110
- this.wallet = new import_ethers.ethers.Wallet(this.options.privateKey, this.provider);
111
- console.log(`[MoltsPay] Payment wallet: ${this.wallet.address}`);
112
- } catch (err) {
113
- console.warn("[MoltsPay] Warning: Could not initialize wallet for payment claims");
106
+ if (this.cdpConfig.useMainnet) {
107
+ if (!this.cdpConfig.apiKeyId || !this.cdpConfig.apiKeySecret) {
108
+ console.warn("[MoltsPay] WARNING: USE_MAINNET=true but CDP keys not set!");
109
+ console.warn("[MoltsPay] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET in ~/.moltspay/.env");
114
110
  }
111
+ this.facilitatorUrl = FACILITATOR_MAINNET;
112
+ this.networkId = "eip155:8453";
113
+ } else {
114
+ this.facilitatorUrl = options.facilitatorUrl || FACILITATOR_TESTNET;
115
+ this.networkId = "eip155:84532";
115
116
  }
117
+ const networkName = this.cdpConfig.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
118
+ const facilitatorName = this.cdpConfig.useMainnet ? "CDP" : "x402.org";
116
119
  console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
117
120
  console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
118
121
  console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
119
- console.log(`[MoltsPay] Protocol: x402 (gasless, pay-for-success)`);
122
+ console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);
123
+ console.log(`[MoltsPay] Facilitator: ${facilitatorName} (${this.facilitatorUrl})`);
124
+ if (this.cdpConfig.useMainnet && this.cdpConfig.apiKeyId) {
125
+ console.log(`[MoltsPay] CDP API Key: ${this.cdpConfig.apiKeyId.slice(0, 8)}...`);
126
+ }
127
+ console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
120
128
  }
121
129
  /**
122
130
  * Register a skill handler for a service
@@ -126,48 +134,45 @@ var MoltsPayServer = class {
126
134
  if (!config) {
127
135
  throw new Error(`Service '${serviceId}' not found in manifest`);
128
136
  }
129
- this.skills.set(serviceId, {
130
- id: serviceId,
131
- config,
132
- handler
133
- });
134
- console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);
137
+ this.skills.set(serviceId, { id: serviceId, config, handler });
135
138
  return this;
136
139
  }
137
140
  /**
138
- * Start the server
141
+ * Start HTTP server
139
142
  */
140
143
  listen(port) {
141
- const p = port || this.options.port;
144
+ const p = port || this.options.port || 3e3;
145
+ const host = this.options.host || "0.0.0.0";
142
146
  const server = (0, import_http.createServer)((req, res) => this.handleRequest(req, res));
143
- server.listen(p, this.options.host, () => {
144
- console.log(`[MoltsPay] Server listening on http://${this.options.host}:${p}`);
147
+ server.listen(p, host, () => {
148
+ console.log(`[MoltsPay] Server listening on http://${host}:${p}`);
145
149
  console.log(`[MoltsPay] Endpoints:`);
146
150
  console.log(` GET /services - List available services`);
147
151
  console.log(` POST /execute - Execute service (x402 payment)`);
148
152
  });
149
153
  }
154
+ /**
155
+ * Handle incoming request
156
+ */
150
157
  async handleRequest(req, res) {
151
- const url = new URL(req.url || "/", `http://${req.headers.host}`);
152
- const path = url.pathname;
153
- const method = req.method || "GET";
154
158
  res.setHeader("Access-Control-Allow-Origin", "*");
155
159
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
156
160
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment");
157
161
  res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response");
158
- if (method === "OPTIONS") {
162
+ if (req.method === "OPTIONS") {
159
163
  res.writeHead(204);
160
164
  res.end();
161
165
  return;
162
166
  }
163
167
  try {
164
- if (method === "GET" && path === "/services") {
168
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
169
+ if (url.pathname === "/services" && req.method === "GET") {
165
170
  return this.handleGetServices(res);
166
171
  }
167
- if (method === "POST" && path === "/execute") {
172
+ if (url.pathname === "/execute" && req.method === "POST") {
168
173
  const body = await this.readBody(req);
169
174
  const paymentHeader = req.headers[PAYMENT_HEADER];
170
- return this.handleExecute(body, paymentHeader, res);
175
+ return await this.handleExecute(body, paymentHeader, res);
171
176
  }
172
177
  this.sendJson(res, 404, { error: "Not found" });
173
178
  } catch (err) {
@@ -179,7 +184,6 @@ var MoltsPayServer = class {
179
184
  * GET /services - List available services
180
185
  */
181
186
  handleGetServices(res) {
182
- const chain = getChain(this.manifest.provider.chain);
183
187
  const services = this.manifest.services.map((s) => ({
184
188
  id: s.id,
185
189
  name: s.name,
@@ -195,15 +199,15 @@ var MoltsPayServer = class {
195
199
  services,
196
200
  x402: {
197
201
  version: X402_VERSION,
198
- network: `eip155:${chain.chainId}`,
199
- schemes: ["exact"]
202
+ network: this.networkId,
203
+ schemes: ["exact"],
204
+ facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
205
+ mainnet: this.cdpConfig.useMainnet
200
206
  }
201
207
  });
202
208
  }
203
209
  /**
204
210
  * POST /execute - Execute service with x402 payment
205
- * Body: { service: string, params: object }
206
- * Header: X-Payment (optional - if missing, returns 402)
207
211
  */
208
212
  async handleExecute(body, paymentHeader, res) {
209
213
  const { service, params } = body;
@@ -233,6 +237,11 @@ var MoltsPayServer = class {
233
237
  if (!validation.valid) {
234
238
  return this.sendJson(res, 402, { error: validation.error });
235
239
  }
240
+ console.log(`[MoltsPay] Verifying payment with facilitator...`);
241
+ const verifyResult = await this.verifyWithFacilitator(payment, skill.config);
242
+ if (!verifyResult.valid) {
243
+ return this.sendJson(res, 402, { error: `Payment verification failed: ${verifyResult.error}` });
244
+ }
236
245
  console.log(`[MoltsPay] Executing skill: ${service}`);
237
246
  let result;
238
247
  try {
@@ -244,32 +253,46 @@ var MoltsPayServer = class {
244
253
  message: err.message
245
254
  });
246
255
  }
247
- console.log(`[MoltsPay] Skill succeeded, claiming payment...`);
248
- let txHash = null;
256
+ console.log(`[MoltsPay] Skill succeeded, settling payment...`);
257
+ let settlement = null;
249
258
  try {
250
- txHash = await this.claimPayment(payment);
251
- console.log(`[MoltsPay] Payment claimed: ${txHash}`);
259
+ settlement = await this.settleWithFacilitator(payment, skill.config);
260
+ console.log(`[MoltsPay] Payment settled: ${settlement.transaction || "pending"}`);
252
261
  } catch (err) {
253
- console.error("[MoltsPay] Payment claim failed:", err.message);
262
+ console.error("[MoltsPay] Settlement failed:", err.message);
263
+ }
264
+ const responseHeaders = {};
265
+ if (settlement) {
266
+ const responsePayload = {
267
+ success: true,
268
+ transaction: settlement.transaction,
269
+ network: payment.network
270
+ };
271
+ responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(
272
+ JSON.stringify(responsePayload)
273
+ ).toString("base64");
254
274
  }
255
275
  this.sendJson(res, 200, {
256
276
  success: true,
257
277
  result,
258
- payment: txHash ? { txHash, status: "claimed" } : { status: "pending" }
259
- });
278
+ payment: settlement ? { transaction: settlement.transaction, status: "settled" } : { status: "pending" }
279
+ }, responseHeaders);
260
280
  }
261
281
  /**
262
282
  * Return 402 with x402 payment requirements
263
283
  */
264
284
  sendPaymentRequired(config, res) {
265
- const chain = getChain(this.manifest.provider.chain);
266
285
  const amountInUnits = Math.floor(config.price * 1e6).toString();
267
286
  const requirements = [{
268
287
  scheme: "exact",
269
- network: `eip155:${chain.chainId}`,
288
+ network: this.networkId,
270
289
  maxAmountRequired: amountInUnits,
271
290
  resource: this.manifest.provider.wallet,
272
- description: `${config.name} - $${config.price} ${config.currency}`
291
+ description: `${config.name} - $${config.price} ${config.currency}`,
292
+ extra: JSON.stringify({
293
+ facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
294
+ mainnet: this.cdpConfig.useMainnet
295
+ })
273
296
  }];
274
297
  const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
275
298
  res.writeHead(402, {
@@ -283,7 +306,7 @@ var MoltsPayServer = class {
283
306
  }, null, 2));
284
307
  }
285
308
  /**
286
- * Validate x402 payment payload
309
+ * Basic payment validation
287
310
  */
288
311
  validatePayment(payment, config) {
289
312
  if (payment.x402Version !== X402_VERSION) {
@@ -292,56 +315,89 @@ var MoltsPayServer = class {
292
315
  if (payment.scheme !== "exact") {
293
316
  return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };
294
317
  }
295
- const chain = getChain(this.manifest.provider.chain);
296
- const expectedNetwork = `eip155:${chain.chainId}`;
297
- if (payment.network !== expectedNetwork) {
298
- return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };
299
- }
300
- const auth = payment.payload.authorization;
301
- if (auth.to.toLowerCase() !== this.manifest.provider.wallet.toLowerCase()) {
302
- return { valid: false, error: "Payment recipient mismatch" };
303
- }
304
- const amount = Number(auth.value) / 1e6;
305
- if (amount < config.price) {
306
- return { valid: false, error: `Insufficient amount: $${amount} < $${config.price}` };
307
- }
308
- const now = Math.floor(Date.now() / 1e3);
309
- if (Number(auth.validBefore) < now) {
310
- return { valid: false, error: "Payment authorization expired" };
311
- }
312
- if (Number(auth.validAfter) > now) {
313
- return { valid: false, error: "Payment authorization not yet valid" };
318
+ if (payment.network !== this.networkId) {
319
+ return { valid: false, error: `Network mismatch: expected ${this.networkId}, got ${payment.network}` };
314
320
  }
315
321
  return { valid: true };
316
322
  }
317
323
  /**
318
- * Claim payment using transferWithAuthorization
324
+ * Verify payment with facilitator (testnet or CDP)
319
325
  */
320
- async claimPayment(payment) {
321
- if (!this.wallet || !this.provider) {
322
- throw new Error("Wallet not configured for payment claims");
326
+ async verifyWithFacilitator(payment, config) {
327
+ try {
328
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
329
+ const requirements = {
330
+ scheme: "exact",
331
+ network: this.networkId,
332
+ maxAmountRequired: amountInUnits,
333
+ resource: this.manifest.provider.wallet,
334
+ payTo: this.manifest.provider.wallet
335
+ };
336
+ const requestBody = {
337
+ paymentPayload: payment,
338
+ paymentRequirements: requirements
339
+ };
340
+ let headers = { "Content-Type": "application/json" };
341
+ if (this.cdpConfig.useMainnet) {
342
+ const authHeaders = await getCDPAuthHeaders(
343
+ "POST",
344
+ "/platform/v2/x402/verify",
345
+ requestBody
346
+ );
347
+ headers = { ...headers, ...authHeaders };
348
+ }
349
+ const response = await fetch(`${this.facilitatorUrl}/verify`, {
350
+ method: "POST",
351
+ headers,
352
+ body: JSON.stringify(requestBody)
353
+ });
354
+ const result = await response.json();
355
+ if (!response.ok || !result.isValid) {
356
+ return { valid: false, error: result.invalidReason || result.error || "Verification failed" };
357
+ }
358
+ return { valid: true };
359
+ } catch (err) {
360
+ return { valid: false, error: `Facilitator error: ${err.message}` };
323
361
  }
324
- const chain = getChain(this.manifest.provider.chain);
325
- const auth = payment.payload.authorization;
326
- const sig = payment.payload.signature;
327
- const { r, s, v } = import_ethers.ethers.Signature.from(sig);
328
- const usdcAbi = [
329
- "function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s)"
330
- ];
331
- const usdc = new import_ethers.ethers.Contract(chain.usdc, usdcAbi, this.wallet);
332
- const tx = await usdc.transferWithAuthorization(
333
- auth.from,
334
- auth.to,
335
- auth.value,
336
- auth.validAfter,
337
- auth.validBefore,
338
- auth.nonce,
339
- v,
340
- r,
341
- s
342
- );
343
- const receipt = await tx.wait();
344
- return receipt.hash;
362
+ }
363
+ /**
364
+ * Settle payment with facilitator (execute on-chain transfer)
365
+ */
366
+ async settleWithFacilitator(payment, config) {
367
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
368
+ const requirements = {
369
+ scheme: "exact",
370
+ network: this.networkId,
371
+ maxAmountRequired: amountInUnits,
372
+ resource: this.manifest.provider.wallet,
373
+ payTo: this.manifest.provider.wallet
374
+ };
375
+ const requestBody = {
376
+ paymentPayload: payment,
377
+ paymentRequirements: requirements
378
+ };
379
+ let headers = { "Content-Type": "application/json" };
380
+ if (this.cdpConfig.useMainnet) {
381
+ const authHeaders = await getCDPAuthHeaders(
382
+ "POST",
383
+ "/platform/v2/x402/settle",
384
+ requestBody
385
+ );
386
+ headers = { ...headers, ...authHeaders };
387
+ }
388
+ const response = await fetch(`${this.facilitatorUrl}/settle`, {
389
+ method: "POST",
390
+ headers,
391
+ body: JSON.stringify(requestBody)
392
+ });
393
+ const result = await response.json();
394
+ if (!response.ok || !result.success) {
395
+ throw new Error(result.error || result.errorReason || "Settlement failed");
396
+ }
397
+ return {
398
+ transaction: result.transaction,
399
+ status: result.status || "settled"
400
+ };
345
401
  }
346
402
  async readBody(req) {
347
403
  return new Promise((resolve, reject) => {
@@ -357,8 +413,12 @@ var MoltsPayServer = class {
357
413
  req.on("error", reject);
358
414
  });
359
415
  }
360
- sendJson(res, status, data) {
361
- res.writeHead(status, { "Content-Type": "application/json" });
416
+ sendJson(res, status, data, extraHeaders) {
417
+ const headers = { "Content-Type": "application/json" };
418
+ if (extraHeaders) {
419
+ Object.assign(headers, extraHeaders);
420
+ }
421
+ res.writeHead(status, headers);
362
422
  res.end(JSON.stringify(data, null, 2));
363
423
  }
364
424
  };