moltspay 1.4.0 → 1.4.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.
- package/.env.example +14 -0
- package/README.md +140 -93
- package/dist/cli/index.js +59 -25
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +59 -25
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.d.mts +3 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.js +36 -18
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +36 -18
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.js +40 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +40 -20
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.js +4 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +4 -2
- package/dist/server/index.mjs.map +1 -1
- package/package.json +2 -2
package/.env.example
CHANGED
|
@@ -60,3 +60,17 @@ CDP_API_KEY_SECRET=
|
|
|
60
60
|
# https://facilitator.questflow.ai/
|
|
61
61
|
# QUESTFLOW_ENDPOINT=https://facilitator.questflow.ai
|
|
62
62
|
# QUESTFLOW_API_KEY=
|
|
63
|
+
|
|
64
|
+
# ===========================================
|
|
65
|
+
# Solana Relay Fee Payer
|
|
66
|
+
# ===========================================
|
|
67
|
+
# Private key for paying Solana transaction fees (base58 encoded)
|
|
68
|
+
# Required for server-side settlement on Solana chains
|
|
69
|
+
# SOLANA_FEE_PAYER_KEY=
|
|
70
|
+
|
|
71
|
+
# ===========================================
|
|
72
|
+
# BNB Chain Relay Fee Payer
|
|
73
|
+
# ===========================================
|
|
74
|
+
# Private key for paying BNB Chain transaction fees
|
|
75
|
+
# Required for server-side settlement on BNB chains (bsc, opbnb)
|
|
76
|
+
# BNB_SERVER_PRIVATE_KEY=
|
package/README.md
CHANGED
|
@@ -19,14 +19,14 @@ MoltsPay enables agent-to-agent commerce using the [x402 protocol](https://www.x
|
|
|
19
19
|
|
|
20
20
|
## Features
|
|
21
21
|
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
22
|
+
- **Skill Integration** - Add `moltspay.services.json` to any existing skill
|
|
23
|
+
- **x402 Protocol** - HTTP-native payments (402 Payment Required)
|
|
24
|
+
- **Gasless** - Both client and server pay no gas (facilitators handle it)
|
|
25
|
+
- **Payment Verification** - Automatic on-chain verification
|
|
26
|
+
- **Secure Wallet** - Spending limits, whitelist, and audit logging
|
|
27
|
+
- **Multi-chain** - Base, Polygon, Solana, BNB, Tempo (mainnet & testnet)
|
|
28
|
+
- **Agent-to-Agent** - Complete A2A payment flow support
|
|
29
|
+
- **Multi-VM** - EVM chains + Solana (SVM) with unified API
|
|
30
30
|
|
|
31
31
|
## Installation
|
|
32
32
|
|
|
@@ -148,24 +148,24 @@ MoltsPay supports multiple payment protocols, each optimized for different chain
|
|
|
148
148
|
|
|
149
149
|
```
|
|
150
150
|
Client Server CDP Facilitator
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
151
|
+
| | |
|
|
152
|
+
| POST /execute | |
|
|
153
|
+
| --------------------------------------------------> | |
|
|
154
|
+
| | |
|
|
155
|
+
| 402 + payment requirements | |
|
|
156
|
+
| <-------------------------------------------------- | |
|
|
157
|
+
| | |
|
|
158
|
+
| [Sign EIP-3009 - NO GAS] | |
|
|
159
|
+
| | |
|
|
160
|
+
| POST + X-Payment header | |
|
|
161
|
+
| --------------------------------------------------> | Verify signature |
|
|
162
|
+
| | --------------------------------------------------> |
|
|
163
|
+
| | |
|
|
164
|
+
| | Execute transfer (pays gas) |
|
|
165
|
+
| | <-------------------------------------------------- |
|
|
166
|
+
| | |
|
|
167
|
+
| 200 OK + result | |
|
|
168
|
+
| <-------------------------------------------------- | |
|
|
169
169
|
```
|
|
170
170
|
|
|
171
171
|
**Key insight:** Client signs a payment authorization, server submits it. Neither party pays gas - the CDP facilitator handles settlement.
|
|
@@ -176,23 +176,23 @@ MPP (Machine Payments Protocol) is simpler - client executes the transfer direct
|
|
|
176
176
|
|
|
177
177
|
```
|
|
178
178
|
Client Server
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
179
|
+
| |
|
|
180
|
+
| POST /service |
|
|
181
|
+
| --------------------------------------------------> |
|
|
182
|
+
| |
|
|
183
|
+
| 402 + WWW-Authenticate |
|
|
184
|
+
| <-------------------------------------------------- |
|
|
185
|
+
| |
|
|
186
|
+
| [Execute TIP-20 transfer] |
|
|
187
|
+
| [No gas needed on Tempo] |
|
|
188
|
+
| |
|
|
189
|
+
| POST + Authorization: Payment|
|
|
190
|
+
| --------------------------------------------------> |
|
|
191
|
+
| |
|
|
192
|
+
| [Server verifies on-chain] |
|
|
193
|
+
| |
|
|
194
|
+
| 200 OK + Payment-Receipt |
|
|
195
|
+
| <-------------------------------------------------- |
|
|
196
196
|
```
|
|
197
197
|
|
|
198
198
|
**Key insight:** On Tempo, the client executes the transfer directly (gas-free), then retries with the transaction hash. No CDP facilitator needed.
|
|
@@ -201,24 +201,24 @@ Client Server
|
|
|
201
201
|
|
|
202
202
|
```
|
|
203
203
|
Client Server (Fee Payer) Solana Network
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
204
|
+
| | |
|
|
205
|
+
| POST /execute | |
|
|
206
|
+
| --------------------------------------------------> | |
|
|
207
|
+
| | |
|
|
208
|
+
| 402 + payment requirements | |
|
|
209
|
+
| (includes solana_wallet) | |
|
|
210
|
+
| <-------------------------------------------------- | |
|
|
211
|
+
| | |
|
|
212
|
+
| [Sign SPL Transfer] | |
|
|
213
|
+
| [NO GAS - just signing] | |
|
|
214
|
+
| | |
|
|
215
|
+
| POST + X-Payment (signature) | |
|
|
216
|
+
| --------------------------------------------------> | Execute transfer |
|
|
217
|
+
| | (server pays ~$0.001 SOL) |
|
|
218
|
+
| | --------------------------------------------------> |
|
|
219
|
+
| | |
|
|
220
|
+
| 200 OK + result | |
|
|
221
|
+
| <-------------------------------------------------- | |
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
**Key insight:** Client only signs the SPL transfer (gasless). Server acts as fee payer and executes the transaction on-chain.
|
|
@@ -227,24 +227,24 @@ Client Server (Fee Payer) Solana Network
|
|
|
227
227
|
|
|
228
228
|
```
|
|
229
229
|
Client Server BNB Network
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
230
|
+
| | |
|
|
231
|
+
| POST /execute | |
|
|
232
|
+
| --------------------------------------------------> | |
|
|
233
|
+
| | |
|
|
234
|
+
| 402 + payment requirements | |
|
|
235
|
+
| (includes bnbSpender) | |
|
|
236
|
+
| <-------------------------------------------------- | |
|
|
237
|
+
| | |
|
|
238
|
+
| [Sign EIP-712 intent] | |
|
|
239
|
+
| [NO GAS - just signing] | |
|
|
240
|
+
| | |
|
|
241
|
+
| POST + X-Payment (signature) | |
|
|
242
|
+
| --------------------------------------------------> | Execute transferFrom |
|
|
243
|
+
| | (server pays ~$0.0001 gas) |
|
|
244
|
+
| | --------------------------------------------------> |
|
|
245
|
+
| | |
|
|
246
|
+
| 200 OK + result | |
|
|
247
|
+
| <-------------------------------------------------- | |
|
|
248
248
|
```
|
|
249
249
|
|
|
250
250
|
**Key insight:** Client only signs an intent (gasless). Server executes the actual transfer and pays the minimal gas (~$0.0001). This is the "pay-for-success" model - payment only happens if service succeeds.
|
|
@@ -255,9 +255,9 @@ MoltsPay reads your skill's existing structure:
|
|
|
255
255
|
|
|
256
256
|
```
|
|
257
257
|
my-skill/
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
258
|
+
+------ package.json # MoltsPay reads "main" field
|
|
259
|
+
+------ index.js # Your existing exports
|
|
260
|
+
+------ moltspay.services.json # Only file you add!
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
**Your functions stay untouched.** Just add the JSON config.
|
|
@@ -350,8 +350,8 @@ Server does NOT need a private key - the x402 facilitator handles settlement.
|
|
|
350
350
|
|
|
351
351
|
The server automatically detects which chain to verify payments on based on the client's payment header:
|
|
352
352
|
|
|
353
|
-
- Client pays with `--chain base`
|
|
354
|
-
- Client pays with `--chain base_sepolia`
|
|
353
|
+
- Client pays with `--chain base` -> Server verifies on Base mainnet
|
|
354
|
+
- Client pays with `--chain base_sepolia` -> Server verifies on Base Sepolia
|
|
355
355
|
|
|
356
356
|
**No `USE_MAINNET` env var needed!** Just configure your accepted chains in the manifest.
|
|
357
357
|
|
|
@@ -425,16 +425,63 @@ npx moltspay validate <path> # Validate manifest
|
|
|
425
425
|
```typescript
|
|
426
426
|
import { MoltsPayClient } from 'moltspay/client';
|
|
427
427
|
|
|
428
|
-
|
|
428
|
+
// Initialize client (uses wallet from ~/.moltspay/wallet.json)
|
|
429
|
+
const client = new MoltsPayClient();
|
|
429
430
|
|
|
430
|
-
//
|
|
431
|
-
const result = await client.
|
|
432
|
-
|
|
433
|
-
|
|
431
|
+
// Standard service call (params wrapped in { params: {...} })
|
|
432
|
+
const result = await client.pay(
|
|
433
|
+
'https://server.com',
|
|
434
|
+
'text-to-video',
|
|
435
|
+
{ prompt: 'a cat dancing' },
|
|
436
|
+
{ chain: 'base' }
|
|
437
|
+
);
|
|
434
438
|
|
|
435
439
|
console.log(result.video_url);
|
|
436
440
|
```
|
|
437
441
|
|
|
442
|
+
#### Custom Input Formats (rawData)
|
|
443
|
+
|
|
444
|
+
Some services have custom input formats instead of the standard `{ params: { prompt } }`.
|
|
445
|
+
Use `rawData: true` to send your data at the top level:
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// Service expects: { text: "...", target_lang: "..." }
|
|
449
|
+
// NOT: { params: { text: "...", target_lang: "..." } }
|
|
450
|
+
|
|
451
|
+
const result = await client.pay(
|
|
452
|
+
'https://server.com',
|
|
453
|
+
'translate',
|
|
454
|
+
{ text: 'Hello world', target_lang: 'es' },
|
|
455
|
+
{
|
|
456
|
+
chain: 'base_sepolia',
|
|
457
|
+
rawData: true // Send data at top level
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
// Server receives: { service: "translate", text: "Hello world", target_lang: "es", chain: "base_sepolia" }
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
#### PayOptions Reference
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
interface PayOptions {
|
|
468
|
+
token?: 'USDC' | 'USDT'; // Token to pay with (default: USDC)
|
|
469
|
+
autoSelect?: boolean; // Auto-select token based on balance
|
|
470
|
+
chain?: string; // Chain: base, polygon, solana, bnb, tempo_moderato, + testnets
|
|
471
|
+
rawData?: boolean; // Send data at top level (for custom input formats)
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### CLI Equivalent
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
# Standard format (uses { params: { prompt } })
|
|
479
|
+
npx moltspay pay https://server.com text-to-video --prompt "a cat dancing"
|
|
480
|
+
|
|
481
|
+
# Custom format (uses rawData, sends at top level)
|
|
482
|
+
npx moltspay pay https://server.com translate --data '{"text": "Hello", "target_lang": "es"}'
|
|
483
|
+
```
|
|
484
|
+
|
|
438
485
|
### Server
|
|
439
486
|
|
|
440
487
|
```typescript
|
|
@@ -493,9 +540,9 @@ A **facilitator** is the entity that executes the on-chain payment and pays the
|
|
|
493
540
|
|
|
494
541
|
| Aspect | CDP (External) | Self-hosted |
|
|
495
542
|
|--------|----------------|-------------|
|
|
496
|
-
| Single point of failure |
|
|
497
|
-
| Censorship risk |
|
|
498
|
-
| Dependency |
|
|
543
|
+
| Single point of failure | Coinbase down = everyone stuck | Each provider independent |
|
|
544
|
+
| Censorship risk | Coinbase can block accounts | Cannot be censored |
|
|
545
|
+
| Dependency | Relies on third-party | Fully autonomous |
|
|
499
546
|
|
|
500
547
|
This self-hosted approach is a key innovation: **any service provider can become their own facilitator** without relying on third-party infrastructure. Unlike CDP where all users depend on Coinbase, self-hosted facilitators create a truly decentralized network with no single point of failure.
|
|
501
548
|
|
|
@@ -628,7 +675,7 @@ npx moltspay pay https://moltspay.com/a/zen7 text-to-video --chain solana_devnet
|
|
|
628
675
|
|
|
629
676
|
Join our Discord for help, feedback, and updates:
|
|
630
677
|
|
|
631
|
-
|
|
678
|
+
--> **[MoltsPay Discord](https://discord.gg/QwCJgVBxVK)**
|
|
632
679
|
|
|
633
680
|
Or visit the [#moltspay-support](https://discord.com/channels/1472602423267819734/1480968496346304522) channel directly.
|
|
634
681
|
|
package/dist/cli/index.js
CHANGED
|
@@ -682,11 +682,26 @@ var MoltsPayClient = class {
|
|
|
682
682
|
throw new Error("Client not initialized. Run: npx moltspay init");
|
|
683
683
|
}
|
|
684
684
|
console.log(`[MoltsPay] Requesting service: ${service}`);
|
|
685
|
-
|
|
685
|
+
let executeUrl = `${serverUrl}/execute`;
|
|
686
|
+
try {
|
|
687
|
+
const services = await this.getServices(serverUrl);
|
|
688
|
+
const svc = services.services?.find((s) => s.id === service);
|
|
689
|
+
if (svc?.endpoint) {
|
|
690
|
+
executeUrl = `${serverUrl}${svc.endpoint}`;
|
|
691
|
+
console.log(`[MoltsPay] Using service endpoint: ${svc.endpoint}`);
|
|
692
|
+
}
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
let requestBody;
|
|
696
|
+
if (options.rawData) {
|
|
697
|
+
requestBody = { service, ...params };
|
|
698
|
+
} else {
|
|
699
|
+
requestBody = { service, params };
|
|
700
|
+
}
|
|
686
701
|
if (options.chain) {
|
|
687
702
|
requestBody.chain = options.chain;
|
|
688
703
|
}
|
|
689
|
-
const initialRes = await fetch(
|
|
704
|
+
const initialRes = await fetch(executeUrl, {
|
|
690
705
|
method: "POST",
|
|
691
706
|
headers: { "Content-Type": "application/json" },
|
|
692
707
|
body: JSON.stringify(requestBody)
|
|
@@ -702,7 +717,7 @@ var MoltsPayClient = class {
|
|
|
702
717
|
const paymentRequiredHeader = initialRes.headers.get(PAYMENT_REQUIRED_HEADER);
|
|
703
718
|
if (wwwAuthHeader && wwwAuthHeader.toLowerCase().includes("payment")) {
|
|
704
719
|
console.log("[MoltsPay] Detected MPP protocol, using Tempo flow...");
|
|
705
|
-
return await this.handleMPPPayment(
|
|
720
|
+
return await this.handleMPPPayment(executeUrl, service, params, wwwAuthHeader, options);
|
|
706
721
|
}
|
|
707
722
|
if (!paymentRequiredHeader) {
|
|
708
723
|
throw new Error("Missing payment header (x-payment-required or www-authenticate)");
|
|
@@ -763,7 +778,7 @@ Please specify: --chain <chain_name>`
|
|
|
763
778
|
if (!req2) {
|
|
764
779
|
throw new Error(`Failed to find payment requirement for ${selectedChain}`);
|
|
765
780
|
}
|
|
766
|
-
return await this.handleSolanaPayment(
|
|
781
|
+
return await this.handleSolanaPayment(executeUrl, service, params, req2, solanaChain, options);
|
|
767
782
|
}
|
|
768
783
|
const chainName = selectedChain;
|
|
769
784
|
const chain = getChain(chainName);
|
|
@@ -810,14 +825,14 @@ Please specify: --chain <chain_name>`
|
|
|
810
825
|
if (!bnbSpender) {
|
|
811
826
|
throw new Error("Server did not provide bnbSpender address. Server may not support BNB payments.");
|
|
812
827
|
}
|
|
813
|
-
return await this.handleBNBPayment(
|
|
828
|
+
return await this.handleBNBPayment(executeUrl, service, params, {
|
|
814
829
|
to: payTo2,
|
|
815
830
|
amount,
|
|
816
831
|
token,
|
|
817
832
|
chainName,
|
|
818
833
|
chain,
|
|
819
834
|
spender: bnbSpender
|
|
820
|
-
});
|
|
835
|
+
}, options);
|
|
821
836
|
}
|
|
822
837
|
const payTo = req.payTo || req.resource;
|
|
823
838
|
if (!payTo) {
|
|
@@ -848,11 +863,11 @@ Please specify: --chain <chain_name>`
|
|
|
848
863
|
};
|
|
849
864
|
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
850
865
|
console.log(`[MoltsPay] Sending request with payment...`);
|
|
851
|
-
const paidRequestBody = { service, params };
|
|
866
|
+
const paidRequestBody = options.rawData ? { service, ...params } : { service, params };
|
|
852
867
|
if (options.chain) {
|
|
853
868
|
paidRequestBody.chain = options.chain;
|
|
854
869
|
}
|
|
855
|
-
const paidRes = await fetch(
|
|
870
|
+
const paidRes = await fetch(executeUrl, {
|
|
856
871
|
method: "POST",
|
|
857
872
|
headers: {
|
|
858
873
|
"Content-Type": "application/json",
|
|
@@ -866,13 +881,13 @@ Please specify: --chain <chain_name>`
|
|
|
866
881
|
}
|
|
867
882
|
this.recordSpending(amount);
|
|
868
883
|
console.log(`[MoltsPay] Success! Payment: ${result.payment?.status || "claimed"}`);
|
|
869
|
-
return result.result;
|
|
884
|
+
return result.result || result;
|
|
870
885
|
}
|
|
871
886
|
/**
|
|
872
887
|
* Handle MPP (Machine Payments Protocol) payment flow
|
|
873
888
|
* Called when pay() detects WWW-Authenticate header in 402 response
|
|
874
889
|
*/
|
|
875
|
-
async handleMPPPayment(
|
|
890
|
+
async handleMPPPayment(executeUrl, service, params, wwwAuthHeader, options = {}) {
|
|
876
891
|
const { privateKeyToAccount: privateKeyToAccount2 } = await import("viem/accounts");
|
|
877
892
|
const { createWalletClient, createPublicClient, http } = await import("viem");
|
|
878
893
|
const { tempoModerato } = await import("viem/chains");
|
|
@@ -933,13 +948,14 @@ Please specify: --chain <chain_name>`
|
|
|
933
948
|
source: `did:pkh:eip155:${chainId}:${account.address}`
|
|
934
949
|
};
|
|
935
950
|
const credentialB64 = Buffer.from(JSON.stringify(credential)).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
936
|
-
const
|
|
951
|
+
const retryBody = options.rawData ? { service, ...params, chain: "tempo_moderato" } : { service, params, chain: "tempo_moderato" };
|
|
952
|
+
const paidRes = await fetch(executeUrl, {
|
|
937
953
|
method: "POST",
|
|
938
954
|
headers: {
|
|
939
955
|
"Content-Type": "application/json",
|
|
940
956
|
"Authorization": `Payment ${credentialB64}`
|
|
941
957
|
},
|
|
942
|
-
body: JSON.stringify(
|
|
958
|
+
body: JSON.stringify(retryBody)
|
|
943
959
|
});
|
|
944
960
|
const result = await paidRes.json();
|
|
945
961
|
if (!paidRes.ok) {
|
|
@@ -959,7 +975,7 @@ Please specify: --chain <chain_name>`
|
|
|
959
975
|
* 4. Server executes service
|
|
960
976
|
* 5. Server calls transferFrom if successful (pay-for-success)
|
|
961
977
|
*/
|
|
962
|
-
async handleBNBPayment(
|
|
978
|
+
async handleBNBPayment(executeUrl, service, params, paymentDetails, options = {}) {
|
|
963
979
|
const { to, amount, token, chainName, chain, spender } = paymentDetails;
|
|
964
980
|
const tokenConfig = chain.tokens[token];
|
|
965
981
|
const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
|
|
@@ -1055,13 +1071,14 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
1055
1071
|
};
|
|
1056
1072
|
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
1057
1073
|
console.log(`[MoltsPay] Sending BNB payment request...`);
|
|
1058
|
-
const
|
|
1074
|
+
const bnbRequestBody = options.rawData ? { service, ...params, chain: chainName } : { service, params, chain: chainName };
|
|
1075
|
+
const paidRes = await fetch(executeUrl, {
|
|
1059
1076
|
method: "POST",
|
|
1060
1077
|
headers: {
|
|
1061
1078
|
"Content-Type": "application/json",
|
|
1062
1079
|
"X-Payment": paymentHeader
|
|
1063
1080
|
},
|
|
1064
|
-
body: JSON.stringify(
|
|
1081
|
+
body: JSON.stringify(bnbRequestBody)
|
|
1065
1082
|
});
|
|
1066
1083
|
const result = await paidRes.json();
|
|
1067
1084
|
if (!paidRes.ok) {
|
|
@@ -1078,7 +1095,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
1078
1095
|
* 1. Client creates and signs a transfer transaction
|
|
1079
1096
|
* 2. Server submits the transaction after service completes
|
|
1080
1097
|
*/
|
|
1081
|
-
async handleSolanaPayment(
|
|
1098
|
+
async handleSolanaPayment(executeUrl, service, params, requirements, chain, options = {}) {
|
|
1082
1099
|
const solanaWallet = loadSolanaWallet(this.configDir);
|
|
1083
1100
|
if (!solanaWallet) {
|
|
1084
1101
|
throw new Error("No Solana wallet found. Run: npx moltspay init --chain solana_devnet");
|
|
@@ -1131,13 +1148,14 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
1131
1148
|
}
|
|
1132
1149
|
};
|
|
1133
1150
|
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
1134
|
-
const
|
|
1151
|
+
const solanaRequestBody = options.rawData ? { service, ...params, chain } : { service, params, chain };
|
|
1152
|
+
const paidRes = await fetch(executeUrl, {
|
|
1135
1153
|
method: "POST",
|
|
1136
1154
|
headers: {
|
|
1137
1155
|
"Content-Type": "application/json",
|
|
1138
1156
|
"X-Payment": paymentHeader
|
|
1139
1157
|
},
|
|
1140
|
-
body: JSON.stringify(
|
|
1158
|
+
body: JSON.stringify(solanaRequestBody)
|
|
1141
1159
|
});
|
|
1142
1160
|
const result = await paidRes.json();
|
|
1143
1161
|
if (!paidRes.ok) {
|
|
@@ -3273,8 +3291,10 @@ var MoltsPayServer = class {
|
|
|
3273
3291
|
isProxyAllowed(clientIP) {
|
|
3274
3292
|
const allowedIPs = process.env.PROXY_ALLOWED_IPS?.split(",").map((ip) => ip.trim()) || [];
|
|
3275
3293
|
if (allowedIPs.length === 0) {
|
|
3276
|
-
|
|
3277
|
-
|
|
3294
|
+
return true;
|
|
3295
|
+
}
|
|
3296
|
+
if (allowedIPs.includes("*")) {
|
|
3297
|
+
return true;
|
|
3278
3298
|
}
|
|
3279
3299
|
const normalizedIP = clientIP === "::1" ? "127.0.0.1" : clientIP.replace("::ffff:", "");
|
|
3280
3300
|
const allowed = allowedIPs.includes(normalizedIP) || allowedIPs.includes(clientIP);
|
|
@@ -4870,14 +4890,23 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
|
|
|
4870
4890
|
process.exit(1);
|
|
4871
4891
|
}
|
|
4872
4892
|
});
|
|
4873
|
-
program.command("pay <server> <service> [params]").description("Pay for a service and get the result").option("--prompt <text>", "Prompt for the service").option("--image <path>", "Image URL or local file path").option("--token <token>", "Token to pay with (USDC or USDT)", "USDC").option("--chain <chain>", "Chain to pay on (base, polygon, base_sepolia, tempo_moderato, solana, or solana_devnet).").option("--config-dir <dir>", "Config directory with wallet.json", DEFAULT_CONFIG_DIR2).option("--json", "Output raw JSON only").action(async (server, service, paramsJson, options) => {
|
|
4893
|
+
program.command("pay <server> <service> [params]").description("Pay for a service and get the result").option("--prompt <text>", "Prompt for the service").option("--image <path>", "Image URL or local file path").option("--data <json>", "Raw JSON data to send (for custom input formats)").option("--token <token>", "Token to pay with (USDC or USDT)", "USDC").option("--chain <chain>", "Chain to pay on (base, polygon, base_sepolia, tempo_moderato, solana, or solana_devnet).").option("--config-dir <dir>", "Config directory with wallet.json", DEFAULT_CONFIG_DIR2).option("--json", "Output raw JSON only").action(async (server, service, paramsJson, options) => {
|
|
4874
4894
|
const client = new MoltsPayClient({ configDir: options.configDir });
|
|
4875
4895
|
if (!client.isInitialized) {
|
|
4876
4896
|
console.error("\u274C Wallet not initialized. Run: npx moltspay init");
|
|
4877
4897
|
process.exit(1);
|
|
4878
4898
|
}
|
|
4879
4899
|
let params = {};
|
|
4880
|
-
|
|
4900
|
+
let useRawData = false;
|
|
4901
|
+
if (options.data) {
|
|
4902
|
+
try {
|
|
4903
|
+
params = JSON.parse(options.data);
|
|
4904
|
+
useRawData = true;
|
|
4905
|
+
} catch {
|
|
4906
|
+
console.error("\u274C Invalid JSON in --data flag");
|
|
4907
|
+
process.exit(1);
|
|
4908
|
+
}
|
|
4909
|
+
} else if (paramsJson) {
|
|
4881
4910
|
try {
|
|
4882
4911
|
params = JSON.parse(paramsJson);
|
|
4883
4912
|
} catch {
|
|
@@ -4885,7 +4914,7 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
4885
4914
|
process.exit(1);
|
|
4886
4915
|
}
|
|
4887
4916
|
}
|
|
4888
|
-
if (options.prompt) params.prompt = options.prompt;
|
|
4917
|
+
if (!useRawData && options.prompt) params.prompt = options.prompt;
|
|
4889
4918
|
if (options.image) {
|
|
4890
4919
|
const imagePath = options.image;
|
|
4891
4920
|
if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
|
|
@@ -4926,7 +4955,11 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
4926
4955
|
`);
|
|
4927
4956
|
console.log(` Server: ${server}`);
|
|
4928
4957
|
console.log(` Service: ${service}`);
|
|
4929
|
-
|
|
4958
|
+
if (useRawData) {
|
|
4959
|
+
console.log(` Data: ${JSON.stringify(params).slice(0, 50)}${JSON.stringify(params).length > 50 ? "..." : ""}`);
|
|
4960
|
+
} else {
|
|
4961
|
+
console.log(` Prompt: ${params.prompt}`);
|
|
4962
|
+
}
|
|
4930
4963
|
if (imageDisplay) console.log(` Image: ${imageDisplay}`);
|
|
4931
4964
|
console.log(` Chain: ${chain || "(auto)"}`);
|
|
4932
4965
|
console.log(` Token: ${token}`);
|
|
@@ -4936,7 +4969,8 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
4936
4969
|
try {
|
|
4937
4970
|
const result = await client.pay(server, service, params, {
|
|
4938
4971
|
token,
|
|
4939
|
-
chain
|
|
4972
|
+
chain,
|
|
4973
|
+
rawData: useRawData
|
|
4940
4974
|
});
|
|
4941
4975
|
if (options.json) {
|
|
4942
4976
|
console.log(JSON.stringify(result));
|