moltspay 0.5.4 → 0.7.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/README.md +0 -124
- package/dist/cdp/index.d.mts +111 -0
- package/dist/cdp/index.d.ts +111 -0
- package/dist/cdp/index.js +30655 -0
- package/dist/cdp/index.js.map +1 -0
- package/dist/cdp/index.mjs +30631 -0
- package/dist/cdp/index.mjs.map +1 -0
- package/dist/chains/index.d.mts +1 -1
- package/dist/chains/index.d.ts +1 -1
- package/dist/cli/index.js +990 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +967 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/client/index.d.mts +134 -0
- package/dist/client/index.d.ts +134 -0
- package/dist/client/index.js +331 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +296 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/createWallet-D53qu7ie.d.mts +77 -0
- package/dist/createWallet-D53qu7ie.d.ts +77 -0
- package/dist/index-Dg8n6wdW.d.mts +32 -0
- package/dist/index-Dg8n6wdW.d.ts +32 -0
- package/dist/index.d.mts +6 -1483
- package/dist/index.d.ts +6 -1483
- package/dist/index.js +31039 -4254
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31042 -4203
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +120 -0
- package/dist/server/index.d.ts +120 -0
- package/dist/server/index.js +418 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +393 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/wallet/index.d.mts +3 -451
- package/dist/wallet/index.d.ts +3 -451
- package/dist/wallet/index.js +5 -1021
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +16 -1015
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +19 -19
- package/dist/cli.js +0 -1984
- package/dist/cli.js.map +0 -1
- package/dist/cli.mjs +0 -1969
- package/dist/cli.mjs.map +0 -1
- package/dist/guide/index.d.mts +0 -39
- package/dist/guide/index.d.ts +0 -39
- package/dist/guide/index.js +0 -181
- package/dist/guide/index.js.map +0 -1
- package/dist/guide/index.mjs +0 -152
- package/dist/guide/index.mjs.map +0 -1
- package/dist/index-CyFg9s2m.d.mts +0 -161
- package/dist/index-CyFg9s2m.d.ts +0 -161
- package/dist/orders/index.d.mts +0 -97
- package/dist/orders/index.d.ts +0 -97
- package/dist/orders/index.js +0 -162
- package/dist/orders/index.js.map +0 -1
- package/dist/orders/index.mjs +0 -136
- package/dist/orders/index.mjs.map +0 -1
- package/dist/permit/index.d.mts +0 -49
- package/dist/permit/index.d.ts +0 -49
- package/dist/permit/index.js +0 -273
- package/dist/permit/index.js.map +0 -1
- package/dist/permit/index.mjs +0 -246
- package/dist/permit/index.mjs.map +0 -1
- /package/dist/{cli.d.mts → cli/index.d.mts} +0 -0
- /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MoltsPay Server Types
|
|
3
|
+
*/
|
|
4
|
+
interface ServiceConfig {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
price: number;
|
|
9
|
+
currency: string;
|
|
10
|
+
input: Record<string, InputField>;
|
|
11
|
+
output: Record<string, OutputField>;
|
|
12
|
+
/** Shell command to execute for this service. Params passed as JSON to stdin. */
|
|
13
|
+
command?: string;
|
|
14
|
+
}
|
|
15
|
+
interface InputField {
|
|
16
|
+
type: 'string' | 'number' | 'boolean' | 'object';
|
|
17
|
+
required?: boolean;
|
|
18
|
+
description?: string;
|
|
19
|
+
}
|
|
20
|
+
interface OutputField {
|
|
21
|
+
type: 'string' | 'number' | 'boolean' | 'object';
|
|
22
|
+
description?: string;
|
|
23
|
+
}
|
|
24
|
+
interface ProviderConfig {
|
|
25
|
+
name: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
wallet: string;
|
|
28
|
+
chain: string;
|
|
29
|
+
}
|
|
30
|
+
interface ServicesManifest {
|
|
31
|
+
provider: ProviderConfig;
|
|
32
|
+
services: ServiceConfig[];
|
|
33
|
+
}
|
|
34
|
+
type SkillFunction = (params: Record<string, any>) => Promise<Record<string, any>>;
|
|
35
|
+
interface RegisteredSkill {
|
|
36
|
+
id: string;
|
|
37
|
+
config: ServiceConfig;
|
|
38
|
+
handler: SkillFunction;
|
|
39
|
+
}
|
|
40
|
+
interface PaymentRequest {
|
|
41
|
+
chargeId: string;
|
|
42
|
+
service: string;
|
|
43
|
+
amount: number;
|
|
44
|
+
currency: string;
|
|
45
|
+
wallet: string;
|
|
46
|
+
chain: string;
|
|
47
|
+
expiresAt: number;
|
|
48
|
+
}
|
|
49
|
+
interface VerifyRequest {
|
|
50
|
+
chargeId: string;
|
|
51
|
+
txHash: string;
|
|
52
|
+
}
|
|
53
|
+
type ChargeStatus = 'pending' | 'paid' | 'completed' | 'expired' | 'failed';
|
|
54
|
+
interface Charge {
|
|
55
|
+
id: string;
|
|
56
|
+
service: string;
|
|
57
|
+
params: Record<string, any>;
|
|
58
|
+
amount: number;
|
|
59
|
+
currency: string;
|
|
60
|
+
status: ChargeStatus;
|
|
61
|
+
txHash?: string;
|
|
62
|
+
result?: Record<string, any>;
|
|
63
|
+
createdAt: number;
|
|
64
|
+
expiresAt: number;
|
|
65
|
+
paidAt?: number;
|
|
66
|
+
completedAt?: number;
|
|
67
|
+
}
|
|
68
|
+
interface MoltsPayServerOptions {
|
|
69
|
+
port?: number;
|
|
70
|
+
host?: string;
|
|
71
|
+
chargeExpirySecs?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* MoltsPay Server - Payment infrastructure for AI Agents
|
|
76
|
+
*
|
|
77
|
+
* Usage:
|
|
78
|
+
* const server = new MoltsPayServer('./moltspay.services.json');
|
|
79
|
+
* server.skill('text-to-video', async (params) => { ... });
|
|
80
|
+
* server.listen(3000);
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
declare class MoltsPayServer {
|
|
84
|
+
private manifest;
|
|
85
|
+
private skills;
|
|
86
|
+
private charges;
|
|
87
|
+
private options;
|
|
88
|
+
constructor(servicesPath: string, options?: MoltsPayServerOptions);
|
|
89
|
+
/**
|
|
90
|
+
* Register a skill handler for a service
|
|
91
|
+
*/
|
|
92
|
+
skill(serviceId: string, handler: SkillFunction): this;
|
|
93
|
+
/**
|
|
94
|
+
* Start the server
|
|
95
|
+
*/
|
|
96
|
+
listen(port?: number): void;
|
|
97
|
+
private handleRequest;
|
|
98
|
+
/**
|
|
99
|
+
* GET /services - List available services
|
|
100
|
+
*/
|
|
101
|
+
private handleGetServices;
|
|
102
|
+
/**
|
|
103
|
+
* POST /pay - Create payment request
|
|
104
|
+
* Body: { service: string, params: object }
|
|
105
|
+
*/
|
|
106
|
+
private handlePay;
|
|
107
|
+
/**
|
|
108
|
+
* POST /verify - Verify payment and execute skill
|
|
109
|
+
* Body: { chargeId: string, txHash: string }
|
|
110
|
+
*/
|
|
111
|
+
private handleVerify;
|
|
112
|
+
/**
|
|
113
|
+
* GET /status/:chargeId - Check charge status
|
|
114
|
+
*/
|
|
115
|
+
private handleStatus;
|
|
116
|
+
private readBody;
|
|
117
|
+
private sendJson;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export { type Charge, type ChargeStatus, type InputField, MoltsPayServer, type MoltsPayServerOptions, type OutputField, type PaymentRequest, type ProviderConfig, type RegisteredSkill, type ServiceConfig, type ServicesManifest, type SkillFunction, type VerifyRequest };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MoltsPay Server Types
|
|
3
|
+
*/
|
|
4
|
+
interface ServiceConfig {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
price: number;
|
|
9
|
+
currency: string;
|
|
10
|
+
input: Record<string, InputField>;
|
|
11
|
+
output: Record<string, OutputField>;
|
|
12
|
+
/** Shell command to execute for this service. Params passed as JSON to stdin. */
|
|
13
|
+
command?: string;
|
|
14
|
+
}
|
|
15
|
+
interface InputField {
|
|
16
|
+
type: 'string' | 'number' | 'boolean' | 'object';
|
|
17
|
+
required?: boolean;
|
|
18
|
+
description?: string;
|
|
19
|
+
}
|
|
20
|
+
interface OutputField {
|
|
21
|
+
type: 'string' | 'number' | 'boolean' | 'object';
|
|
22
|
+
description?: string;
|
|
23
|
+
}
|
|
24
|
+
interface ProviderConfig {
|
|
25
|
+
name: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
wallet: string;
|
|
28
|
+
chain: string;
|
|
29
|
+
}
|
|
30
|
+
interface ServicesManifest {
|
|
31
|
+
provider: ProviderConfig;
|
|
32
|
+
services: ServiceConfig[];
|
|
33
|
+
}
|
|
34
|
+
type SkillFunction = (params: Record<string, any>) => Promise<Record<string, any>>;
|
|
35
|
+
interface RegisteredSkill {
|
|
36
|
+
id: string;
|
|
37
|
+
config: ServiceConfig;
|
|
38
|
+
handler: SkillFunction;
|
|
39
|
+
}
|
|
40
|
+
interface PaymentRequest {
|
|
41
|
+
chargeId: string;
|
|
42
|
+
service: string;
|
|
43
|
+
amount: number;
|
|
44
|
+
currency: string;
|
|
45
|
+
wallet: string;
|
|
46
|
+
chain: string;
|
|
47
|
+
expiresAt: number;
|
|
48
|
+
}
|
|
49
|
+
interface VerifyRequest {
|
|
50
|
+
chargeId: string;
|
|
51
|
+
txHash: string;
|
|
52
|
+
}
|
|
53
|
+
type ChargeStatus = 'pending' | 'paid' | 'completed' | 'expired' | 'failed';
|
|
54
|
+
interface Charge {
|
|
55
|
+
id: string;
|
|
56
|
+
service: string;
|
|
57
|
+
params: Record<string, any>;
|
|
58
|
+
amount: number;
|
|
59
|
+
currency: string;
|
|
60
|
+
status: ChargeStatus;
|
|
61
|
+
txHash?: string;
|
|
62
|
+
result?: Record<string, any>;
|
|
63
|
+
createdAt: number;
|
|
64
|
+
expiresAt: number;
|
|
65
|
+
paidAt?: number;
|
|
66
|
+
completedAt?: number;
|
|
67
|
+
}
|
|
68
|
+
interface MoltsPayServerOptions {
|
|
69
|
+
port?: number;
|
|
70
|
+
host?: string;
|
|
71
|
+
chargeExpirySecs?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* MoltsPay Server - Payment infrastructure for AI Agents
|
|
76
|
+
*
|
|
77
|
+
* Usage:
|
|
78
|
+
* const server = new MoltsPayServer('./moltspay.services.json');
|
|
79
|
+
* server.skill('text-to-video', async (params) => { ... });
|
|
80
|
+
* server.listen(3000);
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
declare class MoltsPayServer {
|
|
84
|
+
private manifest;
|
|
85
|
+
private skills;
|
|
86
|
+
private charges;
|
|
87
|
+
private options;
|
|
88
|
+
constructor(servicesPath: string, options?: MoltsPayServerOptions);
|
|
89
|
+
/**
|
|
90
|
+
* Register a skill handler for a service
|
|
91
|
+
*/
|
|
92
|
+
skill(serviceId: string, handler: SkillFunction): this;
|
|
93
|
+
/**
|
|
94
|
+
* Start the server
|
|
95
|
+
*/
|
|
96
|
+
listen(port?: number): void;
|
|
97
|
+
private handleRequest;
|
|
98
|
+
/**
|
|
99
|
+
* GET /services - List available services
|
|
100
|
+
*/
|
|
101
|
+
private handleGetServices;
|
|
102
|
+
/**
|
|
103
|
+
* POST /pay - Create payment request
|
|
104
|
+
* Body: { service: string, params: object }
|
|
105
|
+
*/
|
|
106
|
+
private handlePay;
|
|
107
|
+
/**
|
|
108
|
+
* POST /verify - Verify payment and execute skill
|
|
109
|
+
* Body: { chargeId: string, txHash: string }
|
|
110
|
+
*/
|
|
111
|
+
private handleVerify;
|
|
112
|
+
/**
|
|
113
|
+
* GET /status/:chargeId - Check charge status
|
|
114
|
+
*/
|
|
115
|
+
private handleStatus;
|
|
116
|
+
private readBody;
|
|
117
|
+
private sendJson;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export { type Charge, type ChargeStatus, type InputField, MoltsPayServer, type MoltsPayServerOptions, type OutputField, type PaymentRequest, type ProviderConfig, type RegisteredSkill, type ServiceConfig, type ServicesManifest, type SkillFunction, type VerifyRequest };
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/server/index.ts
|
|
21
|
+
var server_exports = {};
|
|
22
|
+
__export(server_exports, {
|
|
23
|
+
MoltsPayServer: () => MoltsPayServer
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(server_exports);
|
|
26
|
+
var import_fs = require("fs");
|
|
27
|
+
var import_http = require("http");
|
|
28
|
+
|
|
29
|
+
// src/verify/index.ts
|
|
30
|
+
var import_ethers = require("ethers");
|
|
31
|
+
|
|
32
|
+
// src/chains/index.ts
|
|
33
|
+
var CHAINS = {
|
|
34
|
+
// ============ Mainnet ============
|
|
35
|
+
base: {
|
|
36
|
+
name: "Base",
|
|
37
|
+
chainId: 8453,
|
|
38
|
+
rpc: "https://mainnet.base.org",
|
|
39
|
+
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
40
|
+
explorer: "https://basescan.org/address/",
|
|
41
|
+
explorerTx: "https://basescan.org/tx/",
|
|
42
|
+
avgBlockTime: 2
|
|
43
|
+
},
|
|
44
|
+
polygon: {
|
|
45
|
+
name: "Polygon",
|
|
46
|
+
chainId: 137,
|
|
47
|
+
rpc: "https://polygon-rpc.com",
|
|
48
|
+
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
49
|
+
explorer: "https://polygonscan.com/address/",
|
|
50
|
+
explorerTx: "https://polygonscan.com/tx/",
|
|
51
|
+
avgBlockTime: 2
|
|
52
|
+
},
|
|
53
|
+
ethereum: {
|
|
54
|
+
name: "Ethereum",
|
|
55
|
+
chainId: 1,
|
|
56
|
+
rpc: "https://eth.llamarpc.com",
|
|
57
|
+
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
58
|
+
explorer: "https://etherscan.io/address/",
|
|
59
|
+
explorerTx: "https://etherscan.io/tx/",
|
|
60
|
+
avgBlockTime: 12
|
|
61
|
+
},
|
|
62
|
+
// ============ Testnet ============
|
|
63
|
+
base_sepolia: {
|
|
64
|
+
name: "Base Sepolia",
|
|
65
|
+
chainId: 84532,
|
|
66
|
+
rpc: "https://sepolia.base.org",
|
|
67
|
+
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
68
|
+
explorer: "https://sepolia.basescan.org/address/",
|
|
69
|
+
explorerTx: "https://sepolia.basescan.org/tx/",
|
|
70
|
+
avgBlockTime: 2
|
|
71
|
+
},
|
|
72
|
+
sepolia: {
|
|
73
|
+
name: "Sepolia",
|
|
74
|
+
chainId: 11155111,
|
|
75
|
+
rpc: "https://rpc.sepolia.org",
|
|
76
|
+
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
77
|
+
explorer: "https://sepolia.etherscan.io/address/",
|
|
78
|
+
explorerTx: "https://sepolia.etherscan.io/tx/",
|
|
79
|
+
avgBlockTime: 12
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function getChain(name) {
|
|
83
|
+
const config = CHAINS[name];
|
|
84
|
+
if (!config) {
|
|
85
|
+
throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(", ")}`);
|
|
86
|
+
}
|
|
87
|
+
return config;
|
|
88
|
+
}
|
|
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
|
+
|
|
163
|
+
// src/server/index.ts
|
|
164
|
+
function generateChargeId() {
|
|
165
|
+
return "ch_" + Math.random().toString(36).substring(2, 15);
|
|
166
|
+
}
|
|
167
|
+
var MoltsPayServer = class {
|
|
168
|
+
manifest;
|
|
169
|
+
skills = /* @__PURE__ */ new Map();
|
|
170
|
+
charges = /* @__PURE__ */ new Map();
|
|
171
|
+
options;
|
|
172
|
+
constructor(servicesPath, options = {}) {
|
|
173
|
+
const content = (0, import_fs.readFileSync)(servicesPath, "utf-8");
|
|
174
|
+
this.manifest = JSON.parse(content);
|
|
175
|
+
this.options = {
|
|
176
|
+
port: options.port || 3e3,
|
|
177
|
+
host: options.host || "0.0.0.0",
|
|
178
|
+
chargeExpirySecs: options.chargeExpirySecs || 300
|
|
179
|
+
// 5 minutes
|
|
180
|
+
};
|
|
181
|
+
console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
|
|
182
|
+
console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
|
|
183
|
+
console.log(`[MoltsPay] Wallet: ${this.manifest.provider.wallet}`);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Register a skill handler for a service
|
|
187
|
+
*/
|
|
188
|
+
skill(serviceId, handler) {
|
|
189
|
+
const config = this.manifest.services.find((s) => s.id === serviceId);
|
|
190
|
+
if (!config) {
|
|
191
|
+
throw new Error(`Service '${serviceId}' not found in manifest`);
|
|
192
|
+
}
|
|
193
|
+
this.skills.set(serviceId, {
|
|
194
|
+
id: serviceId,
|
|
195
|
+
config,
|
|
196
|
+
handler
|
|
197
|
+
});
|
|
198
|
+
console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Start the server
|
|
203
|
+
*/
|
|
204
|
+
listen(port) {
|
|
205
|
+
const p = port || this.options.port;
|
|
206
|
+
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}`);
|
|
209
|
+
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`);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
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
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
221
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
222
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
223
|
+
if (method === "OPTIONS") {
|
|
224
|
+
res.writeHead(204);
|
|
225
|
+
res.end();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
if (method === "GET" && path === "/services") {
|
|
230
|
+
return this.handleGetServices(res);
|
|
231
|
+
}
|
|
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") {
|
|
237
|
+
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);
|
|
243
|
+
}
|
|
244
|
+
this.sendJson(res, 404, { error: "Not found" });
|
|
245
|
+
} catch (err) {
|
|
246
|
+
console.error("[MoltsPay] Error:", err);
|
|
247
|
+
this.sendJson(res, 500, { error: err.message || "Internal error" });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* GET /services - List available services
|
|
252
|
+
*/
|
|
253
|
+
handleGetServices(res) {
|
|
254
|
+
const services = this.manifest.services.map((s) => ({
|
|
255
|
+
id: s.id,
|
|
256
|
+
name: s.name,
|
|
257
|
+
description: s.description,
|
|
258
|
+
price: s.price,
|
|
259
|
+
currency: s.currency,
|
|
260
|
+
input: s.input,
|
|
261
|
+
output: s.output,
|
|
262
|
+
available: this.skills.has(s.id)
|
|
263
|
+
}));
|
|
264
|
+
this.sendJson(res, 200, {
|
|
265
|
+
provider: this.manifest.provider,
|
|
266
|
+
services
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* POST /pay - Create payment request
|
|
271
|
+
* Body: { service: string, params: object }
|
|
272
|
+
*/
|
|
273
|
+
handlePay(body, res) {
|
|
274
|
+
const { service, params } = body;
|
|
275
|
+
if (!service) {
|
|
276
|
+
return this.sendJson(res, 400, { error: "Missing service" });
|
|
277
|
+
}
|
|
278
|
+
const skill = this.skills.get(service);
|
|
279
|
+
if (!skill) {
|
|
280
|
+
return this.sendJson(res, 404, { error: `Service '${service}' not found or not registered` });
|
|
281
|
+
}
|
|
282
|
+
for (const [key, field] of Object.entries(skill.config.input)) {
|
|
283
|
+
if (field.required && (!params || params[key] === void 0)) {
|
|
284
|
+
return this.sendJson(res, 400, { error: `Missing required param: ${key}` });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
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
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* POST /verify - Verify payment and execute skill
|
|
316
|
+
* Body: { chargeId: string, txHash: string }
|
|
317
|
+
*/
|
|
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" });
|
|
322
|
+
}
|
|
323
|
+
const charge = this.charges.get(chargeId);
|
|
324
|
+
if (!charge) {
|
|
325
|
+
return this.sendJson(res, 404, { error: "Charge not found" });
|
|
326
|
+
}
|
|
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
|
+
});
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
const verification = await verifyPayment({
|
|
339
|
+
txHash,
|
|
340
|
+
expectedTo: this.manifest.provider.wallet,
|
|
341
|
+
expectedAmount: charge.amount,
|
|
342
|
+
chain: this.manifest.provider.chain
|
|
343
|
+
});
|
|
344
|
+
if (!verification.verified) {
|
|
345
|
+
charge.status = "failed";
|
|
346
|
+
return this.sendJson(res, 400, {
|
|
347
|
+
error: "Payment verification failed",
|
|
348
|
+
reason: verification.error
|
|
349
|
+
});
|
|
350
|
+
}
|
|
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
|
+
});
|
|
366
|
+
} 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
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* GET /status/:chargeId - Check charge status
|
|
377
|
+
*/
|
|
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
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
async readBody(req) {
|
|
396
|
+
return new Promise((resolve, reject) => {
|
|
397
|
+
let body = "";
|
|
398
|
+
req.on("data", (chunk) => body += chunk);
|
|
399
|
+
req.on("end", () => {
|
|
400
|
+
try {
|
|
401
|
+
resolve(body ? JSON.parse(body) : {});
|
|
402
|
+
} catch {
|
|
403
|
+
reject(new Error("Invalid JSON"));
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
req.on("error", reject);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
sendJson(res, status, data) {
|
|
410
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
411
|
+
res.end(JSON.stringify(data, null, 2));
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
415
|
+
0 && (module.exports = {
|
|
416
|
+
MoltsPayServer
|
|
417
|
+
});
|
|
418
|
+
//# sourceMappingURL=index.js.map
|