moltspay 0.7.2 → 0.8.1

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,11 +69,16 @@ interface MoltsPayServerOptions {
69
69
  port?: number;
70
70
  host?: string;
71
71
  chargeExpirySecs?: number;
72
+ /** x402 Facilitator URL (default: https://x402.org/facilitator) */
73
+ facilitatorUrl?: string;
72
74
  }
73
75
 
74
76
  /**
75
77
  * MoltsPay Server - Payment infrastructure for AI Agents
76
78
  *
79
+ * Uses x402 protocol with public facilitator (https://x402.org/facilitator).
80
+ * Server does NOT need private key - facilitator handles on-chain settlement.
81
+ *
77
82
  * Usage:
78
83
  * const server = new MoltsPayServer('./moltspay.services.json');
79
84
  * server.skill('text-to-video', async (params) => { ... });
@@ -83,36 +88,47 @@ interface MoltsPayServerOptions {
83
88
  declare class MoltsPayServer {
84
89
  private manifest;
85
90
  private skills;
86
- private charges;
87
91
  private options;
92
+ private facilitatorUrl;
88
93
  constructor(servicesPath: string, options?: MoltsPayServerOptions);
89
94
  /**
90
95
  * Register a skill handler for a service
91
96
  */
92
97
  skill(serviceId: string, handler: SkillFunction): this;
93
98
  /**
94
- * Start the server
99
+ * Start HTTP server
95
100
  */
96
101
  listen(port?: number): void;
102
+ /**
103
+ * Handle incoming request
104
+ */
97
105
  private handleRequest;
98
106
  /**
99
107
  * GET /services - List available services
100
108
  */
101
109
  private handleGetServices;
102
110
  /**
103
- * POST /pay - Create payment request
111
+ * POST /execute - Execute service with x402 payment
104
112
  * Body: { service: string, params: object }
113
+ * Header: X-Payment (optional - if missing, returns 402)
114
+ */
115
+ private handleExecute;
116
+ /**
117
+ * Return 402 with x402 payment requirements
118
+ */
119
+ private sendPaymentRequired;
120
+ /**
121
+ * Basic payment validation (before calling facilitator)
105
122
  */
106
- private handlePay;
123
+ private validatePayment;
107
124
  /**
108
- * POST /verify - Verify payment and execute skill
109
- * Body: { chargeId: string, txHash: string }
125
+ * Verify payment with facilitator
110
126
  */
111
- private handleVerify;
127
+ private verifyWithFacilitator;
112
128
  /**
113
- * GET /status/:chargeId - Check charge status
129
+ * Settle payment with facilitator (execute on-chain transfer)
114
130
  */
115
- private handleStatus;
131
+ private settleWithFacilitator;
116
132
  private readBody;
117
133
  private sendJson;
118
134
  }
@@ -69,11 +69,16 @@ interface MoltsPayServerOptions {
69
69
  port?: number;
70
70
  host?: string;
71
71
  chargeExpirySecs?: number;
72
+ /** x402 Facilitator URL (default: https://x402.org/facilitator) */
73
+ facilitatorUrl?: string;
72
74
  }
73
75
 
74
76
  /**
75
77
  * MoltsPay Server - Payment infrastructure for AI Agents
76
78
  *
79
+ * Uses x402 protocol with public facilitator (https://x402.org/facilitator).
80
+ * Server does NOT need private key - facilitator handles on-chain settlement.
81
+ *
77
82
  * Usage:
78
83
  * const server = new MoltsPayServer('./moltspay.services.json');
79
84
  * server.skill('text-to-video', async (params) => { ... });
@@ -83,36 +88,47 @@ interface MoltsPayServerOptions {
83
88
  declare class MoltsPayServer {
84
89
  private manifest;
85
90
  private skills;
86
- private charges;
87
91
  private options;
92
+ private facilitatorUrl;
88
93
  constructor(servicesPath: string, options?: MoltsPayServerOptions);
89
94
  /**
90
95
  * Register a skill handler for a service
91
96
  */
92
97
  skill(serviceId: string, handler: SkillFunction): this;
93
98
  /**
94
- * Start the server
99
+ * Start HTTP server
95
100
  */
96
101
  listen(port?: number): void;
102
+ /**
103
+ * Handle incoming request
104
+ */
97
105
  private handleRequest;
98
106
  /**
99
107
  * GET /services - List available services
100
108
  */
101
109
  private handleGetServices;
102
110
  /**
103
- * POST /pay - Create payment request
111
+ * POST /execute - Execute service with x402 payment
104
112
  * Body: { service: string, params: object }
113
+ * Header: X-Payment (optional - if missing, returns 402)
114
+ */
115
+ private handleExecute;
116
+ /**
117
+ * Return 402 with x402 payment requirements
118
+ */
119
+ private sendPaymentRequired;
120
+ /**
121
+ * Basic payment validation (before calling facilitator)
105
122
  */
106
- private handlePay;
123
+ private validatePayment;
107
124
  /**
108
- * POST /verify - Verify payment and execute skill
109
- * Body: { chargeId: string, txHash: string }
125
+ * Verify payment with facilitator
110
126
  */
111
- private handleVerify;
127
+ private verifyWithFacilitator;
112
128
  /**
113
- * GET /status/:chargeId - Check charge status
129
+ * Settle payment with facilitator (execute on-chain transfer)
114
130
  */
115
- private handleStatus;
131
+ private settleWithFacilitator;
116
132
  private readBody;
117
133
  private sendJson;
118
134
  }
@@ -26,9 +26,6 @@ module.exports = __toCommonJS(server_exports);
26
26
  var import_fs = require("fs");
27
27
  var import_http = require("http");
28
28
 
29
- // src/verify/index.ts
30
- var import_ethers = require("ethers");
31
-
32
29
  // src/chains/index.ts
33
30
  var CHAINS = {
34
31
  // ============ Mainnet ============
@@ -86,101 +83,31 @@ function getChain(name) {
86
83
  }
87
84
  return config;
88
85
  }
89
- function getChainById(chainId) {
90
- return Object.values(CHAINS).find((c) => c.chainId === chainId);
91
- }
92
-
93
- // src/verify/index.ts
94
- var TRANSFER_EVENT_TOPIC = import_ethers.ethers.id("Transfer(address,address,uint256)");
95
- async function verifyPayment(params) {
96
- const { txHash, expectedAmount, expectedTo } = params;
97
- let chain;
98
- try {
99
- if (typeof params.chain === "number") {
100
- chain = getChainById(params.chain);
101
- } else {
102
- chain = getChain(params.chain || "base");
103
- }
104
- if (!chain) {
105
- return { verified: false, error: `Unsupported chain: ${params.chain}` };
106
- }
107
- } catch (e) {
108
- return { verified: false, error: `Unsupported chain: ${params.chain}` };
109
- }
110
- try {
111
- const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
112
- const receipt = await provider.getTransactionReceipt(txHash);
113
- if (!receipt) {
114
- return { verified: false, error: "Transaction not found or not confirmed" };
115
- }
116
- if (receipt.status !== 1) {
117
- return { verified: false, error: "Transaction failed" };
118
- }
119
- const usdcAddress = chain.usdc?.toLowerCase();
120
- if (!usdcAddress) {
121
- return { verified: false, error: `Chain ${chain.name} USDC address not configured` };
122
- }
123
- for (const log of receipt.logs) {
124
- if (log.address.toLowerCase() !== usdcAddress) {
125
- continue;
126
- }
127
- if (log.topics.length < 3 || log.topics[0] !== TRANSFER_EVENT_TOPIC) {
128
- continue;
129
- }
130
- const from = "0x" + log.topics[1].slice(-40);
131
- const to = "0x" + log.topics[2].slice(-40);
132
- const amountRaw = BigInt(log.data);
133
- const amount = Number(amountRaw) / 1e6;
134
- if (expectedTo && to.toLowerCase() !== expectedTo.toLowerCase()) {
135
- continue;
136
- }
137
- if (amount < expectedAmount) {
138
- return {
139
- verified: false,
140
- error: `Insufficient amount: received ${amount} USDC, expected ${expectedAmount} USDC`,
141
- amount,
142
- from,
143
- to,
144
- txHash,
145
- blockNumber: receipt.blockNumber
146
- };
147
- }
148
- return {
149
- verified: true,
150
- amount,
151
- from,
152
- to,
153
- txHash,
154
- blockNumber: receipt.blockNumber
155
- };
156
- }
157
- return { verified: false, error: "No USDC transfer found" };
158
- } catch (e) {
159
- return { verified: false, error: e.message || String(e) };
160
- }
161
- }
162
86
 
163
87
  // src/server/index.ts
164
- function generateChargeId() {
165
- return "ch_" + Math.random().toString(36).substring(2, 15);
166
- }
88
+ var X402_VERSION = 2;
89
+ var PAYMENT_REQUIRED_HEADER = "x-payment-required";
90
+ var PAYMENT_HEADER = "x-payment";
91
+ var PAYMENT_RESPONSE_HEADER = "x-payment-response";
92
+ var DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
167
93
  var MoltsPayServer = class {
168
94
  manifest;
169
95
  skills = /* @__PURE__ */ new Map();
170
- charges = /* @__PURE__ */ new Map();
171
96
  options;
97
+ facilitatorUrl;
172
98
  constructor(servicesPath, options = {}) {
173
99
  const content = (0, import_fs.readFileSync)(servicesPath, "utf-8");
174
100
  this.manifest = JSON.parse(content);
175
101
  this.options = {
176
102
  port: options.port || 3e3,
177
- host: options.host || "0.0.0.0",
178
- chargeExpirySecs: options.chargeExpirySecs || 300
179
- // 5 minutes
103
+ host: options.host || "0.0.0.0"
180
104
  };
105
+ this.facilitatorUrl = options.facilitatorUrl || DEFAULT_FACILITATOR_URL;
181
106
  console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
182
107
  console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
183
- console.log(`[MoltsPay] Wallet: ${this.manifest.provider.wallet}`);
108
+ console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
109
+ console.log(`[MoltsPay] Facilitator: ${this.facilitatorUrl}`);
110
+ console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
184
111
  }
185
112
  /**
186
113
  * Register a skill handler for a service
@@ -190,56 +117,45 @@ var MoltsPayServer = class {
190
117
  if (!config) {
191
118
  throw new Error(`Service '${serviceId}' not found in manifest`);
192
119
  }
193
- this.skills.set(serviceId, {
194
- id: serviceId,
195
- config,
196
- handler
197
- });
198
- console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);
120
+ this.skills.set(serviceId, { id: serviceId, config, handler });
199
121
  return this;
200
122
  }
201
123
  /**
202
- * Start the server
124
+ * Start HTTP server
203
125
  */
204
126
  listen(port) {
205
- const p = port || this.options.port;
127
+ const p = port || this.options.port || 3e3;
128
+ const host = this.options.host || "0.0.0.0";
206
129
  const server = (0, import_http.createServer)((req, res) => this.handleRequest(req, res));
207
- server.listen(p, this.options.host, () => {
208
- console.log(`[MoltsPay] Server listening on http://${this.options.host}:${p}`);
130
+ server.listen(p, host, () => {
131
+ console.log(`[MoltsPay] Server listening on http://${host}:${p}`);
209
132
  console.log(`[MoltsPay] Endpoints:`);
210
- console.log(` GET /services - List available services`);
211
- console.log(` POST /pay - Create payment & execute service`);
212
- console.log(` POST /verify - Verify payment & get result`);
213
- console.log(` GET /status/:id - Check charge status`);
133
+ console.log(` GET /services - List available services`);
134
+ console.log(` POST /execute - Execute service (x402 payment)`);
214
135
  });
215
136
  }
137
+ /**
138
+ * Handle incoming request
139
+ */
216
140
  async handleRequest(req, res) {
217
- const url = new URL(req.url || "/", `http://${req.headers.host}`);
218
- const path = url.pathname;
219
- const method = req.method || "GET";
220
141
  res.setHeader("Access-Control-Allow-Origin", "*");
221
142
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
222
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
223
- if (method === "OPTIONS") {
143
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment");
144
+ res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response");
145
+ if (req.method === "OPTIONS") {
224
146
  res.writeHead(204);
225
147
  res.end();
226
148
  return;
227
149
  }
228
150
  try {
229
- if (method === "GET" && path === "/services") {
151
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
152
+ if (url.pathname === "/services" && req.method === "GET") {
230
153
  return this.handleGetServices(res);
231
154
  }
232
- if (method === "POST" && path === "/pay") {
233
- const body = await this.readBody(req);
234
- return this.handlePay(body, res);
235
- }
236
- if (method === "POST" && path === "/verify") {
155
+ if (url.pathname === "/execute" && req.method === "POST") {
237
156
  const body = await this.readBody(req);
238
- return this.handleVerify(body, res);
239
- }
240
- if (method === "GET" && path.startsWith("/status/")) {
241
- const chargeId = path.replace("/status/", "");
242
- return this.handleStatus(chargeId, res);
157
+ const paymentHeader = req.headers[PAYMENT_HEADER];
158
+ return await this.handleExecute(body, paymentHeader, res);
243
159
  }
244
160
  this.sendJson(res, 404, { error: "Not found" });
245
161
  } catch (err) {
@@ -251,6 +167,7 @@ var MoltsPayServer = class {
251
167
  * GET /services - List available services
252
168
  */
253
169
  handleGetServices(res) {
170
+ const chain = getChain(this.manifest.provider.chain);
254
171
  const services = this.manifest.services.map((s) => ({
255
172
  id: s.id,
256
173
  name: s.name,
@@ -263,14 +180,21 @@ var MoltsPayServer = class {
263
180
  }));
264
181
  this.sendJson(res, 200, {
265
182
  provider: this.manifest.provider,
266
- services
183
+ services,
184
+ x402: {
185
+ version: X402_VERSION,
186
+ network: `eip155:${chain.chainId}`,
187
+ schemes: ["exact"],
188
+ facilitator: this.facilitatorUrl
189
+ }
267
190
  });
268
191
  }
269
192
  /**
270
- * POST /pay - Create payment request
193
+ * POST /execute - Execute service with x402 payment
271
194
  * Body: { service: string, params: object }
195
+ * Header: X-Payment (optional - if missing, returns 402)
272
196
  */
273
- handlePay(body, res) {
197
+ async handleExecute(body, paymentHeader, res) {
274
198
  const { service, params } = body;
275
199
  if (!service) {
276
200
  return this.sendJson(res, 400, { error: "Missing service" });
@@ -284,113 +208,162 @@ var MoltsPayServer = class {
284
208
  return this.sendJson(res, 400, { error: `Missing required param: ${key}` });
285
209
  }
286
210
  }
287
- const chargeId = generateChargeId();
288
- const now = Date.now();
289
- const charge = {
290
- id: chargeId,
291
- service,
292
- params: params || {},
293
- amount: skill.config.price,
294
- currency: skill.config.currency,
295
- status: "pending",
296
- createdAt: now,
297
- expiresAt: now + this.options.chargeExpirySecs * 1e3
298
- };
299
- this.charges.set(chargeId, charge);
300
- const paymentRequest = {
301
- chargeId,
302
- service,
303
- amount: charge.amount,
304
- currency: charge.currency,
305
- wallet: this.manifest.provider.wallet,
306
- chain: this.manifest.provider.chain,
307
- expiresAt: charge.expiresAt
308
- };
309
- this.sendJson(res, 402, {
310
- message: "Payment required",
311
- payment: paymentRequest
211
+ if (!paymentHeader) {
212
+ return this.sendPaymentRequired(skill.config, res);
213
+ }
214
+ let payment;
215
+ try {
216
+ const decoded = Buffer.from(paymentHeader, "base64").toString("utf-8");
217
+ payment = JSON.parse(decoded);
218
+ } catch {
219
+ return this.sendJson(res, 400, { error: "Invalid X-Payment header" });
220
+ }
221
+ const validation = this.validatePayment(payment, skill.config);
222
+ if (!validation.valid) {
223
+ return this.sendJson(res, 402, { error: validation.error });
224
+ }
225
+ console.log(`[MoltsPay] Verifying payment with facilitator...`);
226
+ const verifyResult = await this.verifyWithFacilitator(payment, skill.config);
227
+ if (!verifyResult.valid) {
228
+ return this.sendJson(res, 402, { error: `Payment verification failed: ${verifyResult.error}` });
229
+ }
230
+ console.log(`[MoltsPay] Executing skill: ${service}`);
231
+ let result;
232
+ try {
233
+ result = await skill.handler(params || {});
234
+ } catch (err) {
235
+ console.error("[MoltsPay] Skill execution failed:", err.message);
236
+ return this.sendJson(res, 500, {
237
+ error: "Service execution failed",
238
+ message: err.message
239
+ });
240
+ }
241
+ console.log(`[MoltsPay] Skill succeeded, settling payment...`);
242
+ let settlement = null;
243
+ try {
244
+ settlement = await this.settleWithFacilitator(payment, skill.config);
245
+ console.log(`[MoltsPay] Payment settled: ${settlement.transaction || "pending"}`);
246
+ } catch (err) {
247
+ console.error("[MoltsPay] Settlement failed:", err.message);
248
+ }
249
+ const responseHeaders = {};
250
+ if (settlement) {
251
+ const responsePayload = {
252
+ success: true,
253
+ transaction: settlement.transaction,
254
+ network: payment.network
255
+ };
256
+ responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(
257
+ JSON.stringify(responsePayload)
258
+ ).toString("base64");
259
+ }
260
+ this.sendJson(res, 200, {
261
+ success: true,
262
+ result,
263
+ payment: settlement ? { transaction: settlement.transaction, status: "settled" } : { status: "pending" }
264
+ }, responseHeaders);
265
+ }
266
+ /**
267
+ * Return 402 with x402 payment requirements
268
+ */
269
+ sendPaymentRequired(config, res) {
270
+ const chain = getChain(this.manifest.provider.chain);
271
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
272
+ const requirements = [{
273
+ scheme: "exact",
274
+ network: `eip155:${chain.chainId}`,
275
+ maxAmountRequired: amountInUnits,
276
+ resource: this.manifest.provider.wallet,
277
+ description: `${config.name} - $${config.price} ${config.currency}`,
278
+ // Include facilitator info for client
279
+ extra: JSON.stringify({ facilitator: this.facilitatorUrl })
280
+ }];
281
+ const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
282
+ res.writeHead(402, {
283
+ "Content-Type": "application/json",
284
+ [PAYMENT_REQUIRED_HEADER]: encoded
312
285
  });
286
+ res.end(JSON.stringify({
287
+ error: "Payment required",
288
+ message: `Service requires $${config.price} ${config.currency}`,
289
+ x402: requirements[0]
290
+ }, null, 2));
313
291
  }
314
292
  /**
315
- * POST /verify - Verify payment and execute skill
316
- * Body: { chargeId: string, txHash: string }
293
+ * Basic payment validation (before calling facilitator)
317
294
  */
318
- async handleVerify(body, res) {
319
- const { chargeId, txHash } = body;
320
- if (!chargeId || !txHash) {
321
- return this.sendJson(res, 400, { error: "Missing chargeId or txHash" });
295
+ validatePayment(payment, config) {
296
+ if (payment.x402Version !== X402_VERSION) {
297
+ return { valid: false, error: `Unsupported x402 version: ${payment.x402Version}` };
322
298
  }
323
- const charge = this.charges.get(chargeId);
324
- if (!charge) {
325
- return this.sendJson(res, 404, { error: "Charge not found" });
299
+ if (payment.scheme !== "exact") {
300
+ return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };
326
301
  }
327
- if (Date.now() > charge.expiresAt) {
328
- charge.status = "expired";
329
- return this.sendJson(res, 400, { error: "Charge expired" });
330
- }
331
- if (charge.status === "completed") {
332
- return this.sendJson(res, 200, {
333
- status: "completed",
334
- result: charge.result
335
- });
302
+ const chain = getChain(this.manifest.provider.chain);
303
+ const expectedNetwork = `eip155:${chain.chainId}`;
304
+ if (payment.network !== expectedNetwork) {
305
+ return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };
336
306
  }
307
+ return { valid: true };
308
+ }
309
+ /**
310
+ * Verify payment with facilitator
311
+ */
312
+ async verifyWithFacilitator(payment, config) {
337
313
  try {
338
- const verification = await verifyPayment({
339
- txHash,
340
- expectedTo: this.manifest.provider.wallet,
341
- expectedAmount: charge.amount,
342
- chain: this.manifest.provider.chain
314
+ const chain = getChain(this.manifest.provider.chain);
315
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
316
+ const requirements = {
317
+ scheme: "exact",
318
+ network: `eip155:${chain.chainId}`,
319
+ maxAmountRequired: amountInUnits,
320
+ resource: this.manifest.provider.wallet
321
+ };
322
+ const response = await fetch(`${this.facilitatorUrl}/verify`, {
323
+ method: "POST",
324
+ headers: { "Content-Type": "application/json" },
325
+ body: JSON.stringify({
326
+ paymentPayload: payment,
327
+ paymentRequirements: requirements
328
+ })
343
329
  });
344
- if (!verification.verified) {
345
- charge.status = "failed";
346
- return this.sendJson(res, 400, {
347
- error: "Payment verification failed",
348
- reason: verification.error
349
- });
330
+ const result = await response.json();
331
+ if (!response.ok || !result.isValid) {
332
+ return { valid: false, error: result.invalidReason || "Verification failed" };
350
333
  }
351
- charge.status = "paid";
352
- charge.txHash = txHash;
353
- charge.paidAt = Date.now();
354
- const skill = this.skills.get(charge.service);
355
- console.log(`[MoltsPay] Executing skill: ${charge.service}`);
356
- const result = await skill.handler(charge.params);
357
- charge.status = "completed";
358
- charge.result = result;
359
- charge.completedAt = Date.now();
360
- this.sendJson(res, 200, {
361
- status: "completed",
362
- chargeId,
363
- txHash,
364
- result
365
- });
334
+ return { valid: true };
366
335
  } catch (err) {
367
- console.error("[MoltsPay] Skill execution error:", err);
368
- charge.status = "failed";
369
- this.sendJson(res, 500, {
370
- error: "Skill execution failed",
371
- message: err.message
372
- });
336
+ return { valid: false, error: `Facilitator error: ${err.message}` };
373
337
  }
374
338
  }
375
339
  /**
376
- * GET /status/:chargeId - Check charge status
340
+ * Settle payment with facilitator (execute on-chain transfer)
377
341
  */
378
- handleStatus(chargeId, res) {
379
- const charge = this.charges.get(chargeId);
380
- if (!charge) {
381
- return this.sendJson(res, 404, { error: "Charge not found" });
382
- }
383
- this.sendJson(res, 200, {
384
- chargeId: charge.id,
385
- service: charge.service,
386
- amount: charge.amount,
387
- currency: charge.currency,
388
- status: charge.status,
389
- txHash: charge.txHash,
390
- result: charge.status === "completed" ? charge.result : void 0,
391
- createdAt: charge.createdAt,
392
- expiresAt: charge.expiresAt
342
+ async settleWithFacilitator(payment, config) {
343
+ const chain = getChain(this.manifest.provider.chain);
344
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
345
+ const requirements = {
346
+ scheme: "exact",
347
+ network: `eip155:${chain.chainId}`,
348
+ maxAmountRequired: amountInUnits,
349
+ resource: this.manifest.provider.wallet
350
+ };
351
+ const response = await fetch(`${this.facilitatorUrl}/settle`, {
352
+ method: "POST",
353
+ headers: { "Content-Type": "application/json" },
354
+ body: JSON.stringify({
355
+ paymentPayload: payment,
356
+ paymentRequirements: requirements
357
+ })
393
358
  });
359
+ const result = await response.json();
360
+ if (!response.ok) {
361
+ throw new Error(result.error || "Settlement failed");
362
+ }
363
+ return {
364
+ transaction: result.transaction,
365
+ status: result.status || "settled"
366
+ };
394
367
  }
395
368
  async readBody(req) {
396
369
  return new Promise((resolve, reject) => {
@@ -406,8 +379,12 @@ var MoltsPayServer = class {
406
379
  req.on("error", reject);
407
380
  });
408
381
  }
409
- sendJson(res, status, data) {
410
- res.writeHead(status, { "Content-Type": "application/json" });
382
+ sendJson(res, status, data, extraHeaders) {
383
+ const headers = { "Content-Type": "application/json" };
384
+ if (extraHeaders) {
385
+ Object.assign(headers, extraHeaders);
386
+ }
387
+ res.writeHead(status, headers);
411
388
  res.end(JSON.stringify(data, null, 2));
412
389
  }
413
390
  };