moltspay 0.9.0 → 0.9.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/dist/index.js CHANGED
@@ -30903,8 +30903,9 @@ var MoltsPayServer = class {
30903
30903
  this.useMainnet = process.env.USE_MAINNET?.toLowerCase() === "true";
30904
30904
  this.networkId = this.useMainnet ? "eip155:8453" : "eip155:84532";
30905
30905
  const facilitatorConfig = options.facilitators || {
30906
- primary: "cdp",
30907
- strategy: "failover",
30906
+ primary: process.env.FACILITATOR_PRIMARY || "cdp",
30907
+ fallback: process.env.FACILITATOR_FALLBACK?.split(",").filter(Boolean),
30908
+ strategy: process.env.FACILITATOR_STRATEGY || "failover",
30908
30909
  config: {
30909
30910
  cdp: { useMainnet: this.useMainnet }
30910
30911
  }
@@ -30942,6 +30943,7 @@ var MoltsPayServer = class {
30942
30943
  console.log(`[MoltsPay] Endpoints:`);
30943
30944
  console.log(` GET /services - List available services`);
30944
30945
  console.log(` POST /execute - Execute service (x402 payment)`);
30946
+ console.log(` POST /proxy - Proxy payment for external services`);
30945
30947
  console.log(` GET /health - Health check (incl. facilitators)`);
30946
30948
  });
30947
30949
  }
@@ -30971,6 +30973,15 @@ var MoltsPayServer = class {
30971
30973
  const paymentHeader = req.headers[PAYMENT_HEADER];
30972
30974
  return await this.handleExecute(body, paymentHeader, res);
30973
30975
  }
30976
+ if (url.pathname === "/proxy" && req.method === "POST") {
30977
+ const clientIP = req.headers["x-real-ip"] || req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.socket.remoteAddress || "";
30978
+ if (!this.isProxyAllowed(clientIP)) {
30979
+ return this.sendJson(res, 403, { error: "Forbidden: IP not allowed" });
30980
+ }
30981
+ const body = await this.readBody(req);
30982
+ const paymentHeader = req.headers[PAYMENT_HEADER];
30983
+ return await this.handleProxy(body, paymentHeader, res);
30984
+ }
30974
30985
  this.sendJson(res, 404, { error: "Not found" });
30975
30986
  } catch (err) {
30976
30987
  console.error("[MoltsPay] Error:", err);
@@ -31180,6 +31191,156 @@ var MoltsPayServer = class {
31180
31191
  res.writeHead(status, headers);
31181
31192
  res.end(JSON.stringify(data, null, 2));
31182
31193
  }
31194
+ /**
31195
+ * Check if IP is allowed for /proxy endpoint
31196
+ */
31197
+ isProxyAllowed(clientIP) {
31198
+ const allowedIPs = process.env.PROXY_ALLOWED_IPS?.split(",").map((ip) => ip.trim()) || [];
31199
+ if (allowedIPs.length === 0) {
31200
+ console.log(`[MoltsPay] /proxy denied: no PROXY_ALLOWED_IPS configured`);
31201
+ return false;
31202
+ }
31203
+ const normalizedIP = clientIP === "::1" ? "127.0.0.1" : clientIP.replace("::ffff:", "");
31204
+ const allowed = allowedIPs.includes(normalizedIP) || allowedIPs.includes(clientIP);
31205
+ if (!allowed) {
31206
+ console.log(`[MoltsPay] /proxy denied for IP: ${clientIP} (normalized: ${normalizedIP})`);
31207
+ }
31208
+ return allowed;
31209
+ }
31210
+ /**
31211
+ * POST /proxy - Handle payment for external services (moltspay-creators)
31212
+ *
31213
+ * This endpoint allows other services to delegate x402 payment handling.
31214
+ * It does NOT execute any skill - just handles payment verification/settlement.
31215
+ *
31216
+ * Request body:
31217
+ * { wallet, amount, currency, chain, memo, serviceId, description }
31218
+ *
31219
+ * Without X-Payment header: returns 402 with payment requirements
31220
+ * With X-Payment header: verifies payment and returns result
31221
+ */
31222
+ async handleProxy(body, paymentHeader, res) {
31223
+ const { wallet, amount, currency, chain: chain2, memo, serviceId, description } = body;
31224
+ if (!wallet || !amount) {
31225
+ return this.sendJson(res, 400, { error: "Missing required fields: wallet, amount" });
31226
+ }
31227
+ if (!/^0x[a-fA-F0-9]{40}$/.test(wallet)) {
31228
+ return this.sendJson(res, 400, { error: "Invalid wallet address format" });
31229
+ }
31230
+ const amountNum = parseFloat(amount);
31231
+ if (isNaN(amountNum) || amountNum <= 0) {
31232
+ return this.sendJson(res, 400, { error: "Invalid amount" });
31233
+ }
31234
+ const proxyConfig = {
31235
+ id: serviceId || "proxy",
31236
+ name: description || "Proxy Payment",
31237
+ description: description || "",
31238
+ price: amountNum,
31239
+ currency: currency || "USDC",
31240
+ function: "",
31241
+ // Not used
31242
+ input: {},
31243
+ output: {}
31244
+ };
31245
+ const requirements = this.buildProxyPaymentRequirements(proxyConfig, wallet);
31246
+ if (!paymentHeader) {
31247
+ return this.sendProxyPaymentRequired(proxyConfig, wallet, memo, res);
31248
+ }
31249
+ let payment;
31250
+ try {
31251
+ const decoded = Buffer.from(paymentHeader, "base64").toString("utf-8");
31252
+ payment = JSON.parse(decoded);
31253
+ } catch {
31254
+ return this.sendJson(res, 400, { error: "Invalid X-Payment header" });
31255
+ }
31256
+ if (payment.x402Version !== X402_VERSION2) {
31257
+ return this.sendJson(res, 402, { error: `Unsupported x402 version: ${payment.x402Version}` });
31258
+ }
31259
+ const scheme = payment.accepted?.scheme || payment.scheme;
31260
+ const network = payment.accepted?.network || payment.network;
31261
+ if (scheme !== "exact") {
31262
+ return this.sendJson(res, 402, { error: `Unsupported scheme: ${scheme}` });
31263
+ }
31264
+ if (network !== this.networkId) {
31265
+ return this.sendJson(res, 402, { error: `Network mismatch: expected ${this.networkId}, got ${network}` });
31266
+ }
31267
+ console.log(`[MoltsPay] /proxy: Verifying payment for ${wallet}...`);
31268
+ const verifyResult = await this.registry.verify(payment, requirements);
31269
+ if (!verifyResult.valid) {
31270
+ return this.sendJson(res, 402, {
31271
+ success: false,
31272
+ error: `Payment verification failed: ${verifyResult.error}`,
31273
+ facilitator: verifyResult.facilitator
31274
+ });
31275
+ }
31276
+ console.log(`[MoltsPay] /proxy: Verified by ${verifyResult.facilitator}`);
31277
+ console.log(`[MoltsPay] /proxy: Settling payment...`);
31278
+ let settlement = null;
31279
+ try {
31280
+ settlement = await this.registry.settle(payment, requirements);
31281
+ console.log(`[MoltsPay] /proxy: Payment settled by ${settlement.facilitator}: ${settlement.transaction || "pending"}`);
31282
+ } catch (err) {
31283
+ console.error("[MoltsPay] /proxy: Settlement failed:", err.message);
31284
+ return this.sendJson(res, 500, {
31285
+ success: false,
31286
+ error: `Settlement failed: ${err.message}`
31287
+ });
31288
+ }
31289
+ this.sendJson(res, 200, {
31290
+ success: true,
31291
+ verified: true,
31292
+ settled: settlement?.success || false,
31293
+ txHash: settlement?.transaction,
31294
+ paidTo: wallet,
31295
+ amount: amountNum,
31296
+ currency: currency || "USDC",
31297
+ facilitator: settlement?.facilitator,
31298
+ memo
31299
+ });
31300
+ }
31301
+ /**
31302
+ * Build payment requirements for proxy endpoint (uses provided wallet)
31303
+ */
31304
+ buildProxyPaymentRequirements(config, wallet) {
31305
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
31306
+ const usdcAddress = USDC_ADDRESSES[this.networkId];
31307
+ return {
31308
+ scheme: "exact",
31309
+ network: this.networkId,
31310
+ asset: usdcAddress,
31311
+ amount: amountInUnits,
31312
+ payTo: wallet,
31313
+ // Use provided wallet, not manifest
31314
+ maxTimeoutSeconds: 300,
31315
+ extra: USDC_DOMAIN
31316
+ };
31317
+ }
31318
+ /**
31319
+ * Return 402 with x402 payment requirements for proxy endpoint
31320
+ */
31321
+ sendProxyPaymentRequired(config, wallet, memo, res) {
31322
+ const requirements = this.buildProxyPaymentRequirements(config, wallet);
31323
+ const paymentRequired = {
31324
+ x402Version: X402_VERSION2,
31325
+ accepts: [requirements],
31326
+ resource: {
31327
+ url: `/proxy`,
31328
+ description: `${config.name} - $${config.price} ${config.currency}`,
31329
+ mimeType: "application/json",
31330
+ memo
31331
+ }
31332
+ };
31333
+ const encoded = Buffer.from(JSON.stringify(paymentRequired)).toString("base64");
31334
+ res.writeHead(402, {
31335
+ "Content-Type": "application/json",
31336
+ [PAYMENT_REQUIRED_HEADER]: encoded
31337
+ });
31338
+ res.end(JSON.stringify({
31339
+ error: "Payment required",
31340
+ message: `Payment requires $${config.price} ${config.currency}`,
31341
+ x402: paymentRequired
31342
+ }, null, 2));
31343
+ }
31183
31344
  };
31184
31345
 
31185
31346
  // src/client/index.ts