moltspay 1.6.0 → 2.0.0
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 +1 -0
- package/dist/cdp/index.js.map +1 -1
- package/dist/cdp/index.mjs.map +1 -1
- package/dist/{cdp-DeohBe1o.d.ts → cdp-B5PhDUTC.d.ts} +1 -1
- package/dist/{cdp-p_eHuQpb.d.mts → cdp-D-5Hg3y_.d.mts} +1 -1
- package/dist/chains/index.d.mts +37 -1
- package/dist/chains/index.d.ts +37 -1
- package/dist/chains/index.js +22 -0
- package/dist/chains/index.js.map +1 -1
- package/dist/chains/index.mjs +19 -0
- package/dist/chains/index.mjs.map +1 -1
- package/dist/cli/index.js +1466 -61
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1466 -61
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.d.mts +41 -0
- package/dist/client/index.d.ts +41 -0
- package/dist/client/index.js +824 -10
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +824 -10
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/web/index.d.mts +17 -1
- package/dist/client/web/index.mjs +11 -0
- package/dist/client/web/index.mjs.map +1 -1
- package/dist/facilitators/index.d.mts +161 -4
- package/dist/facilitators/index.d.ts +161 -4
- package/dist/facilitators/index.js +370 -0
- package/dist/facilitators/index.js.map +1 -1
- package/dist/facilitators/index.mjs +365 -0
- package/dist/facilitators/index.mjs.map +1 -1
- package/dist/index.d.mts +77 -2
- package/dist/index.d.ts +77 -2
- package/dist/index.js +1412 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1406 -31
- package/dist/index.mjs.map +1 -1
- package/dist/mcp/index.js +828 -14
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/index.mjs +828 -14
- package/dist/mcp/index.mjs.map +1 -1
- package/dist/{registry-OsEO2dOu.d.mts → registry-DIo0WNoO.d.mts} +8 -1
- package/dist/{registry-OsEO2dOu.d.ts → registry-DIo0WNoO.d.ts} +8 -1
- package/dist/server/index.d.mts +95 -2
- package/dist/server/index.d.ts +95 -2
- package/dist/server/index.js +554 -14
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +554 -14
- package/dist/server/index.mjs.map +1 -1
- package/dist/verify/index.js.map +1 -1
- package/dist/verify/index.mjs.map +1 -1
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +7 -1
- package/schemas/moltspay.services.schema.json +100 -6
- package/scripts/postinstall.js +91 -0
package/dist/server/index.mjs
CHANGED
|
@@ -398,6 +398,10 @@ var CHAINS = {
|
|
|
398
398
|
requiresApproval: true
|
|
399
399
|
}
|
|
400
400
|
};
|
|
401
|
+
var ALIPAY_CHAIN_ID = "alipay";
|
|
402
|
+
function isAlipayChainId(id) {
|
|
403
|
+
return id === ALIPAY_CHAIN_ID;
|
|
404
|
+
}
|
|
401
405
|
|
|
402
406
|
// src/facilitators/tempo.ts
|
|
403
407
|
var TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
@@ -1159,6 +1163,374 @@ var SolanaFacilitator = class extends BaseFacilitator {
|
|
|
1159
1163
|
}
|
|
1160
1164
|
};
|
|
1161
1165
|
|
|
1166
|
+
// src/facilitators/alipay.ts
|
|
1167
|
+
import crypto2 from "crypto";
|
|
1168
|
+
|
|
1169
|
+
// src/facilitators/alipay/rsa2.ts
|
|
1170
|
+
import crypto from "crypto";
|
|
1171
|
+
function rsa2Sign(data, privateKeyPem) {
|
|
1172
|
+
const signer = crypto.createSign("RSA-SHA256");
|
|
1173
|
+
signer.update(data, "utf-8");
|
|
1174
|
+
signer.end();
|
|
1175
|
+
return signer.sign(privateKeyPem, "base64");
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// src/facilitators/alipay/encoding.ts
|
|
1179
|
+
function base64url(input) {
|
|
1180
|
+
return Buffer.from(input, "utf-8").toString("base64url");
|
|
1181
|
+
}
|
|
1182
|
+
function decodeBase64UrlWithPadFix(input) {
|
|
1183
|
+
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
1184
|
+
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
1185
|
+
return Buffer.from(padded, "base64").toString("utf-8");
|
|
1186
|
+
}
|
|
1187
|
+
function toPem(key, kind) {
|
|
1188
|
+
const trimmed = key.trim();
|
|
1189
|
+
if (trimmed.includes("-----BEGIN")) return trimmed;
|
|
1190
|
+
const label = kind === "PRIVATE" ? "PRIVATE KEY" : "PUBLIC KEY";
|
|
1191
|
+
const body = trimmed.replace(/\s+/g, "").match(/.{1,64}/g)?.join("\n") ?? "";
|
|
1192
|
+
return `-----BEGIN ${label}-----
|
|
1193
|
+
${body}
|
|
1194
|
+
-----END ${label}-----
|
|
1195
|
+
`;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
// src/facilitators/alipay/openapi.ts
|
|
1199
|
+
function formatAlipayTimestamp(d = /* @__PURE__ */ new Date()) {
|
|
1200
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1201
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
1202
|
+
}
|
|
1203
|
+
function buildSigningString(params) {
|
|
1204
|
+
return Object.keys(params).sort().map((k) => `${k}=${params[k]}`).join("&");
|
|
1205
|
+
}
|
|
1206
|
+
function responseWrapperKey(method) {
|
|
1207
|
+
return `${method.replace(/\./g, "_")}_response`;
|
|
1208
|
+
}
|
|
1209
|
+
async function alipayOpenApiCall(method, bizContent, config) {
|
|
1210
|
+
const publicParams = {
|
|
1211
|
+
app_id: config.app_id,
|
|
1212
|
+
method,
|
|
1213
|
+
format: "JSON",
|
|
1214
|
+
charset: "utf-8",
|
|
1215
|
+
sign_type: config.sign_type ?? "RSA2",
|
|
1216
|
+
timestamp: formatAlipayTimestamp(),
|
|
1217
|
+
version: "1.0",
|
|
1218
|
+
biz_content: JSON.stringify(bizContent)
|
|
1219
|
+
};
|
|
1220
|
+
const signingString = buildSigningString(publicParams);
|
|
1221
|
+
const sign = rsa2Sign(signingString, config.private_key_pem);
|
|
1222
|
+
const body = new URLSearchParams({ ...publicParams, sign }).toString();
|
|
1223
|
+
const response = await fetch(config.gateway_url, {
|
|
1224
|
+
method: "POST",
|
|
1225
|
+
headers: {
|
|
1226
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8"
|
|
1227
|
+
},
|
|
1228
|
+
body
|
|
1229
|
+
});
|
|
1230
|
+
if (!response.ok) {
|
|
1231
|
+
const text = await response.text().catch(() => "<unreadable>");
|
|
1232
|
+
throw new Error(
|
|
1233
|
+
`Alipay Open API HTTP ${response.status} for ${method}: ${text.slice(0, 500)}`
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
const json = await response.json();
|
|
1237
|
+
const wrapperKey = responseWrapperKey(method);
|
|
1238
|
+
const business = json[wrapperKey];
|
|
1239
|
+
if (business === void 0 || business === null || typeof business !== "object") {
|
|
1240
|
+
throw new Error(
|
|
1241
|
+
`Alipay Open API response missing "${wrapperKey}" wrapper for ${method}: ${JSON.stringify(json).slice(0, 500)}`
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
return business;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// src/facilitators/alipay.ts
|
|
1248
|
+
var ALIPAY_NETWORK = "alipay";
|
|
1249
|
+
var ALIPAY_SCHEME = "alipay-aipay";
|
|
1250
|
+
var ALIPAY_GATEWAY_PROD = "https://openapi.alipay.com/gateway.do";
|
|
1251
|
+
var ALIPAY_AMOUNT_REGEX = /^\d+(\.\d{1,2})?$/;
|
|
1252
|
+
var ALIPAY_PAY_BEFORE_MS = 30 * 60 * 1e3;
|
|
1253
|
+
var ALIPAY_SIGNING_FIELDS = [
|
|
1254
|
+
"amount",
|
|
1255
|
+
"currency",
|
|
1256
|
+
"goods_name",
|
|
1257
|
+
"out_trade_no",
|
|
1258
|
+
"pay_before",
|
|
1259
|
+
"resource_id",
|
|
1260
|
+
"seller_id",
|
|
1261
|
+
"service_id"
|
|
1262
|
+
];
|
|
1263
|
+
var AlipayFacilitator = class extends BaseFacilitator {
|
|
1264
|
+
name = "alipay";
|
|
1265
|
+
displayName = "Alipay AI \u6536";
|
|
1266
|
+
supportedNetworks = [ALIPAY_NETWORK];
|
|
1267
|
+
config;
|
|
1268
|
+
constructor(config) {
|
|
1269
|
+
super();
|
|
1270
|
+
this.config = {
|
|
1271
|
+
gateway_url: ALIPAY_GATEWAY_PROD,
|
|
1272
|
+
sign_type: "RSA2",
|
|
1273
|
+
...config
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Build the 402 challenge for a service: signs the 8-field payload with
|
|
1278
|
+
* RSA2, packages the nested `{protocol, method}` JSON as Base64URL for
|
|
1279
|
+
* `Payment-Needed`, and emits the parallel x402 `accepts[]` entry.
|
|
1280
|
+
*/
|
|
1281
|
+
async createPaymentRequirements(opts) {
|
|
1282
|
+
if (!ALIPAY_AMOUNT_REGEX.test(opts.priceCny)) {
|
|
1283
|
+
throw new Error(
|
|
1284
|
+
`AlipayFacilitator.createPaymentRequirements: priceCny "${opts.priceCny}" does not match /^\\d+(\\.\\d{1,2})?$/ (unit is \u5143, not \u5206; e.g. "1.00" not "100")`
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
const now = /* @__PURE__ */ new Date();
|
|
1288
|
+
const outTradeNo = opts.outTradeNo ?? generateOutTradeNo();
|
|
1289
|
+
const payBefore = formatPayBefore(now);
|
|
1290
|
+
const signedFields = {
|
|
1291
|
+
amount: opts.priceCny,
|
|
1292
|
+
currency: "CNY",
|
|
1293
|
+
goods_name: opts.goodsName,
|
|
1294
|
+
out_trade_no: outTradeNo,
|
|
1295
|
+
pay_before: payBefore,
|
|
1296
|
+
resource_id: opts.resourceId,
|
|
1297
|
+
seller_id: this.config.seller_id,
|
|
1298
|
+
service_id: opts.serviceId
|
|
1299
|
+
};
|
|
1300
|
+
const signingString = ALIPAY_SIGNING_FIELDS.map((k) => `${k}=${signedFields[k]}`).join("&");
|
|
1301
|
+
const seller_signature = rsa2Sign(signingString, this.config.private_key_pem);
|
|
1302
|
+
const challenge = {
|
|
1303
|
+
protocol: {
|
|
1304
|
+
out_trade_no: outTradeNo,
|
|
1305
|
+
amount: signedFields.amount,
|
|
1306
|
+
currency: signedFields.currency,
|
|
1307
|
+
resource_id: signedFields.resource_id,
|
|
1308
|
+
pay_before: payBefore,
|
|
1309
|
+
seller_signature,
|
|
1310
|
+
seller_sign_type: this.config.sign_type ?? "RSA2",
|
|
1311
|
+
seller_unique_id: this.config.seller_id
|
|
1312
|
+
},
|
|
1313
|
+
method: {
|
|
1314
|
+
seller_name: this.config.seller_name,
|
|
1315
|
+
seller_id: this.config.seller_id,
|
|
1316
|
+
seller_app_id: this.config.app_id,
|
|
1317
|
+
goods_name: signedFields.goods_name,
|
|
1318
|
+
seller_unique_id_key: "seller_id",
|
|
1319
|
+
service_id: signedFields.service_id
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
const paymentNeededHeader = base64url(JSON.stringify(challenge));
|
|
1323
|
+
const x402Accepts = {
|
|
1324
|
+
scheme: ALIPAY_SCHEME,
|
|
1325
|
+
network: ALIPAY_NETWORK,
|
|
1326
|
+
asset: "CNY",
|
|
1327
|
+
amount: opts.priceCny,
|
|
1328
|
+
payTo: this.config.seller_id,
|
|
1329
|
+
maxTimeoutSeconds: ALIPAY_PAY_BEFORE_MS / 1e3,
|
|
1330
|
+
extra: {
|
|
1331
|
+
payment_needed_header: paymentNeededHeader,
|
|
1332
|
+
out_trade_no: outTradeNo,
|
|
1333
|
+
pay_before: payBefore,
|
|
1334
|
+
service_id: signedFields.service_id
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
return { x402Accepts, paymentNeededHeader };
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Verify a `Payment-Proof` by calling
|
|
1341
|
+
* `alipay.aipay.agent.payment.verify` against the Alipay Open API.
|
|
1342
|
+
*
|
|
1343
|
+
* The proof is conveyed via `paymentPayload.payload`, which may be:
|
|
1344
|
+
* - a raw Base64URL string (the `Payment-Proof` header value), or
|
|
1345
|
+
* - an object `{ paymentProof: string }` / `{ proofHeader: string }`.
|
|
1346
|
+
*
|
|
1347
|
+
* All failure modes (malformed payload, network errors, Alipay
|
|
1348
|
+
* `code != 10000`) return `{ valid: false, error }`. No exception
|
|
1349
|
+
* escapes, regardless of client-supplied input.
|
|
1350
|
+
*/
|
|
1351
|
+
async verify(paymentPayload, _requirements) {
|
|
1352
|
+
try {
|
|
1353
|
+
const proofHeader = extractProofHeader(paymentPayload.payload);
|
|
1354
|
+
const decoded = decodeProof(proofHeader);
|
|
1355
|
+
const response = await alipayOpenApiCall(
|
|
1356
|
+
"alipay.aipay.agent.payment.verify",
|
|
1357
|
+
{
|
|
1358
|
+
payment_proof: decoded.protocol.payment_proof,
|
|
1359
|
+
trade_no: decoded.protocol.trade_no,
|
|
1360
|
+
client_session: decoded.method.client_session
|
|
1361
|
+
},
|
|
1362
|
+
this.getOpenApiConfig()
|
|
1363
|
+
);
|
|
1364
|
+
if (response.code !== "10000") {
|
|
1365
|
+
return {
|
|
1366
|
+
valid: false,
|
|
1367
|
+
error: `alipay verify ${response.code}: ${response.sub_msg ?? response.msg ?? "unknown"}`,
|
|
1368
|
+
details: {
|
|
1369
|
+
code: response.code,
|
|
1370
|
+
sub_code: response.sub_code,
|
|
1371
|
+
sub_msg: response.sub_msg
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
return {
|
|
1376
|
+
valid: true,
|
|
1377
|
+
details: {
|
|
1378
|
+
trade_no: response.trade_no ?? decoded.protocol.trade_no,
|
|
1379
|
+
amount: response.amount,
|
|
1380
|
+
out_trade_no: response.out_trade_no,
|
|
1381
|
+
resource_id: response.resource_id,
|
|
1382
|
+
active: response.active
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
} catch (e) {
|
|
1386
|
+
return {
|
|
1387
|
+
valid: false,
|
|
1388
|
+
error: e instanceof Error ? e.message : String(e)
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Settle by calling `alipay.aipay.agent.fulfillment.confirm` after the
|
|
1394
|
+
* service resource has been returned to the buyer.
|
|
1395
|
+
*
|
|
1396
|
+
* Per the design (see ALIPAY-INTEGRATION-DESIGN.md §5.1, risk row
|
|
1397
|
+
* "履约确认失败"), this is **fire-and-forget**: the caller (registry /
|
|
1398
|
+
* server) is expected to log fulfillment failures but NOT roll back
|
|
1399
|
+
* the already-delivered resource.
|
|
1400
|
+
*
|
|
1401
|
+
* Re-decodes `paymentPayload.payload` to extract `trade_no` (verify
|
|
1402
|
+
* does the same; the redundant Base64URL decode is negligible).
|
|
1403
|
+
*/
|
|
1404
|
+
async settle(paymentPayload, _requirements) {
|
|
1405
|
+
try {
|
|
1406
|
+
const proofHeader = extractProofHeader(paymentPayload.payload);
|
|
1407
|
+
const decoded = decodeProof(proofHeader);
|
|
1408
|
+
const tradeNo = decoded.protocol.trade_no;
|
|
1409
|
+
const response = await alipayOpenApiCall(
|
|
1410
|
+
"alipay.aipay.agent.fulfillment.confirm",
|
|
1411
|
+
{ trade_no: tradeNo },
|
|
1412
|
+
this.getOpenApiConfig()
|
|
1413
|
+
);
|
|
1414
|
+
if (response.code !== "10000") {
|
|
1415
|
+
return {
|
|
1416
|
+
success: false,
|
|
1417
|
+
transaction: tradeNo,
|
|
1418
|
+
error: `alipay fulfillment ${response.code}: ${response.sub_msg ?? response.msg ?? "unknown"}`,
|
|
1419
|
+
status: "fulfillment_failed"
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
return {
|
|
1423
|
+
success: true,
|
|
1424
|
+
transaction: tradeNo,
|
|
1425
|
+
status: "fulfilled"
|
|
1426
|
+
};
|
|
1427
|
+
} catch (e) {
|
|
1428
|
+
return {
|
|
1429
|
+
success: false,
|
|
1430
|
+
error: e instanceof Error ? e.message : String(e)
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Validate that the configured RSA keys parse and that the Open API
|
|
1436
|
+
* gateway is reachable. Does NOT make a real business API call (would
|
|
1437
|
+
* burn quota and could appear as a legitimate verify attempt in logs).
|
|
1438
|
+
*/
|
|
1439
|
+
async healthCheck() {
|
|
1440
|
+
const start = Date.now();
|
|
1441
|
+
try {
|
|
1442
|
+
crypto2.createPrivateKey(this.config.private_key_pem);
|
|
1443
|
+
} catch (e) {
|
|
1444
|
+
return {
|
|
1445
|
+
healthy: false,
|
|
1446
|
+
error: `merchant private_key_pem parse failed: ${e instanceof Error ? e.message : String(e)}`
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
try {
|
|
1450
|
+
crypto2.createPublicKey(this.config.alipay_public_key_pem);
|
|
1451
|
+
} catch (e) {
|
|
1452
|
+
return {
|
|
1453
|
+
healthy: false,
|
|
1454
|
+
error: `alipay_public_key_pem parse failed: ${e instanceof Error ? e.message : String(e)}`
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
const gatewayUrl = this.config.gateway_url ?? ALIPAY_GATEWAY_PROD;
|
|
1458
|
+
const controller = new AbortController();
|
|
1459
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
1460
|
+
const response = await fetch(gatewayUrl, {
|
|
1461
|
+
method: "HEAD",
|
|
1462
|
+
signal: controller.signal
|
|
1463
|
+
}).catch(() => null);
|
|
1464
|
+
clearTimeout(timeout);
|
|
1465
|
+
const latencyMs = Date.now() - start;
|
|
1466
|
+
if (!response) {
|
|
1467
|
+
return { healthy: false, error: `gateway unreachable: ${gatewayUrl}`, latencyMs };
|
|
1468
|
+
}
|
|
1469
|
+
return { healthy: true, latencyMs };
|
|
1470
|
+
}
|
|
1471
|
+
/** Bundle the facilitator config into the shape openapi.ts wants. */
|
|
1472
|
+
getOpenApiConfig() {
|
|
1473
|
+
return {
|
|
1474
|
+
gateway_url: this.config.gateway_url ?? ALIPAY_GATEWAY_PROD,
|
|
1475
|
+
app_id: this.config.app_id,
|
|
1476
|
+
private_key_pem: this.config.private_key_pem,
|
|
1477
|
+
alipay_public_key_pem: this.config.alipay_public_key_pem,
|
|
1478
|
+
sign_type: this.config.sign_type
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
function generateOutTradeNo() {
|
|
1483
|
+
return "VID" + crypto2.randomBytes(22).toString("base64url").slice(0, 29);
|
|
1484
|
+
}
|
|
1485
|
+
function formatPayBefore(now) {
|
|
1486
|
+
const expiry = new Date(now.getTime() + ALIPAY_PAY_BEFORE_MS);
|
|
1487
|
+
return expiry.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
1488
|
+
}
|
|
1489
|
+
function extractProofHeader(payload) {
|
|
1490
|
+
if (typeof payload === "string") {
|
|
1491
|
+
if (payload.length === 0) {
|
|
1492
|
+
throw new Error("alipay Payment-Proof is empty");
|
|
1493
|
+
}
|
|
1494
|
+
return payload;
|
|
1495
|
+
}
|
|
1496
|
+
if (payload !== null && typeof payload === "object") {
|
|
1497
|
+
const obj = payload;
|
|
1498
|
+
const candidate = obj.paymentProof ?? obj.proofHeader;
|
|
1499
|
+
if (typeof candidate === "string" && candidate.length > 0) {
|
|
1500
|
+
return candidate;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
throw new Error(
|
|
1504
|
+
"alipay payment payload must be a Base64URL string or {paymentProof: string} / {proofHeader: string}"
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1507
|
+
function decodeProof(proofHeader) {
|
|
1508
|
+
let parsed;
|
|
1509
|
+
try {
|
|
1510
|
+
parsed = JSON.parse(decodeBase64UrlWithPadFix(proofHeader));
|
|
1511
|
+
} catch (e) {
|
|
1512
|
+
throw new Error(
|
|
1513
|
+
`failed to decode Payment-Proof: ${e instanceof Error ? e.message : String(e)}`
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
1517
|
+
throw new Error("decoded Payment-Proof is not an object");
|
|
1518
|
+
}
|
|
1519
|
+
const obj = parsed;
|
|
1520
|
+
const protocol = obj.protocol;
|
|
1521
|
+
const method = obj.method;
|
|
1522
|
+
if (!protocol || typeof protocol.payment_proof !== "string") {
|
|
1523
|
+
throw new Error("decoded Payment-Proof missing protocol.payment_proof");
|
|
1524
|
+
}
|
|
1525
|
+
if (typeof protocol.trade_no !== "string") {
|
|
1526
|
+
throw new Error("decoded Payment-Proof missing protocol.trade_no");
|
|
1527
|
+
}
|
|
1528
|
+
if (!method || typeof method.client_session !== "string") {
|
|
1529
|
+
throw new Error("decoded Payment-Proof missing method.client_session");
|
|
1530
|
+
}
|
|
1531
|
+
return parsed;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1162
1534
|
// src/facilitators/registry.ts
|
|
1163
1535
|
import { Keypair as Keypair2 } from "@solana/web3.js";
|
|
1164
1536
|
import bs58 from "bs58";
|
|
@@ -1183,6 +1555,7 @@ var FacilitatorRegistry = class {
|
|
|
1183
1555
|
}
|
|
1184
1556
|
return new SolanaFacilitator({ feePayerKeypair });
|
|
1185
1557
|
});
|
|
1558
|
+
this.registerFactory("alipay", (config) => new AlipayFacilitator(config));
|
|
1186
1559
|
this.selection = selection || { primary: "cdp", fallback: ["tempo", "bnb", "solana"], strategy: "failover" };
|
|
1187
1560
|
}
|
|
1188
1561
|
/**
|
|
@@ -1407,6 +1780,11 @@ var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
|
1407
1780
|
var MPP_AUTH_HEADER = "authorization";
|
|
1408
1781
|
var MPP_WWW_AUTH_HEADER = "www-authenticate";
|
|
1409
1782
|
var MPP_RECEIPT_HEADER = "payment-receipt";
|
|
1783
|
+
var ALIPAY_PAYMENT_NEEDED_HEADER = "payment-needed";
|
|
1784
|
+
var ALIPAY_PAYMENT_PROOF_HEADER = "payment-proof";
|
|
1785
|
+
function headerSafe(value) {
|
|
1786
|
+
return String(value ?? "").replace(/[^\x20-\x7E]/g, (ch) => encodeURIComponent(ch)).replace(/"/g, "%22");
|
|
1787
|
+
}
|
|
1410
1788
|
var TOKEN_ADDRESSES = {
|
|
1411
1789
|
"eip155:8453": {
|
|
1412
1790
|
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
@@ -1542,6 +1920,8 @@ var MoltsPayServer = class {
|
|
|
1542
1920
|
registry;
|
|
1543
1921
|
networkId;
|
|
1544
1922
|
useMainnet;
|
|
1923
|
+
/** Alipay AI 收 facilitator instance, set when `provider.alipay` is configured (2.0.0). */
|
|
1924
|
+
alipayFacilitator = null;
|
|
1545
1925
|
constructor(servicesPath, options = {}) {
|
|
1546
1926
|
loadEnvFile2();
|
|
1547
1927
|
const content = readFileSync2(servicesPath, "utf-8");
|
|
@@ -1563,7 +1943,38 @@ var MoltsPayServer = class {
|
|
|
1563
1943
|
cdp: { useMainnet: this.useMainnet }
|
|
1564
1944
|
}
|
|
1565
1945
|
};
|
|
1946
|
+
const providerAlipay = this.manifest.provider.alipay;
|
|
1947
|
+
if (providerAlipay) {
|
|
1948
|
+
try {
|
|
1949
|
+
const baseDir = path2.dirname(servicesPath);
|
|
1950
|
+
const resolvePem = (p, kind) => toPem(readFileSync2(path2.isAbsolute(p) ? p : path2.resolve(baseDir, p), "utf-8"), kind);
|
|
1951
|
+
const alipayFacilitatorConfig = {
|
|
1952
|
+
seller_id: providerAlipay.seller_id,
|
|
1953
|
+
app_id: providerAlipay.app_id,
|
|
1954
|
+
seller_name: providerAlipay.seller_name,
|
|
1955
|
+
service_id_default: providerAlipay.service_id_default,
|
|
1956
|
+
private_key_pem: resolvePem(providerAlipay.private_key_path, "PRIVATE"),
|
|
1957
|
+
alipay_public_key_pem: resolvePem(providerAlipay.alipay_public_key_path, "PUBLIC"),
|
|
1958
|
+
gateway_url: providerAlipay.gateway_url,
|
|
1959
|
+
sign_type: providerAlipay.sign_type
|
|
1960
|
+
};
|
|
1961
|
+
facilitatorConfig.config = {
|
|
1962
|
+
...facilitatorConfig.config,
|
|
1963
|
+
alipay: alipayFacilitatorConfig
|
|
1964
|
+
};
|
|
1965
|
+
facilitatorConfig.fallback = facilitatorConfig.fallback || [];
|
|
1966
|
+
if (facilitatorConfig.primary !== "alipay" && !facilitatorConfig.fallback.includes("alipay")) {
|
|
1967
|
+
facilitatorConfig.fallback.push("alipay");
|
|
1968
|
+
}
|
|
1969
|
+
} catch (err) {
|
|
1970
|
+
throw new Error(`[MoltsPay] Alipay rail configured but key load failed: ${err.message}`);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1566
1973
|
this.registry = new FacilitatorRegistry(facilitatorConfig);
|
|
1974
|
+
if (providerAlipay) {
|
|
1975
|
+
this.alipayFacilitator = this.registry.get("alipay");
|
|
1976
|
+
console.log(`[MoltsPay] Alipay AI \u6536 rail enabled (seller ${providerAlipay.seller_id})`);
|
|
1977
|
+
}
|
|
1567
1978
|
const primaryFacilitator = this.registry.get(facilitatorConfig.primary);
|
|
1568
1979
|
console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
|
|
1569
1980
|
console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
|
|
@@ -1699,10 +2110,10 @@ var MoltsPayServer = class {
|
|
|
1699
2110
|
writeCorsHeaders(res, origin) {
|
|
1700
2111
|
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
1701
2112
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
1702
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization");
|
|
2113
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization, Payment-Proof");
|
|
1703
2114
|
res.setHeader(
|
|
1704
2115
|
"Access-Control-Expose-Headers",
|
|
1705
|
-
"X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt"
|
|
2116
|
+
"X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt, Payment-Needed"
|
|
1706
2117
|
);
|
|
1707
2118
|
}
|
|
1708
2119
|
/**
|
|
@@ -1729,7 +2140,8 @@ var MoltsPayServer = class {
|
|
|
1729
2140
|
if (url.pathname === "/execute" && req.method === "POST") {
|
|
1730
2141
|
const body = await this.readBody(req);
|
|
1731
2142
|
const paymentHeader = req.headers[PAYMENT_HEADER];
|
|
1732
|
-
|
|
2143
|
+
const proofHeader = req.headers[ALIPAY_PAYMENT_PROOF_HEADER];
|
|
2144
|
+
return await this.handleExecute(body, paymentHeader, res, proofHeader);
|
|
1733
2145
|
}
|
|
1734
2146
|
if (url.pathname === "/proxy" && req.method === "POST") {
|
|
1735
2147
|
const clientIP = req.headers["x-real-ip"] || req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.socket.remoteAddress || "";
|
|
@@ -1747,7 +2159,8 @@ var MoltsPayServer = class {
|
|
|
1747
2159
|
const body = req.method === "POST" ? await this.readBody(req) : {};
|
|
1748
2160
|
const authHeader = req.headers[MPP_AUTH_HEADER];
|
|
1749
2161
|
const x402Header = req.headers[PAYMENT_HEADER];
|
|
1750
|
-
|
|
2162
|
+
const proofHeader = req.headers[ALIPAY_PAYMENT_PROOF_HEADER];
|
|
2163
|
+
return await this.handleMPPRequest(skill, body, authHeader, x402Header, res, proofHeader);
|
|
1751
2164
|
}
|
|
1752
2165
|
this.sendJson(res, 404, { error: "Not found" });
|
|
1753
2166
|
} catch (err) {
|
|
@@ -1844,7 +2257,7 @@ var MoltsPayServer = class {
|
|
|
1844
2257
|
/**
|
|
1845
2258
|
* POST /execute - Execute service with x402 payment
|
|
1846
2259
|
*/
|
|
1847
|
-
async handleExecute(body, paymentHeader, res) {
|
|
2260
|
+
async handleExecute(body, paymentHeader, res, proofHeader) {
|
|
1848
2261
|
const { service, params } = body;
|
|
1849
2262
|
if (!service) {
|
|
1850
2263
|
return this.sendJson(res, 400, { error: "Missing service" });
|
|
@@ -1858,6 +2271,15 @@ var MoltsPayServer = class {
|
|
|
1858
2271
|
return this.sendJson(res, 400, { error: `Missing required param: ${key}` });
|
|
1859
2272
|
}
|
|
1860
2273
|
}
|
|
2274
|
+
if (proofHeader) {
|
|
2275
|
+
const alipayPayment = {
|
|
2276
|
+
x402Version: X402_VERSION2,
|
|
2277
|
+
scheme: ALIPAY_SCHEME,
|
|
2278
|
+
network: ALIPAY_NETWORK,
|
|
2279
|
+
payload: proofHeader
|
|
2280
|
+
};
|
|
2281
|
+
return this.handleAlipayExecute(skill, params || {}, alipayPayment, res);
|
|
2282
|
+
}
|
|
1861
2283
|
if (!paymentHeader) {
|
|
1862
2284
|
return this.sendPaymentRequired(skill.config, res);
|
|
1863
2285
|
}
|
|
@@ -1868,6 +2290,11 @@ var MoltsPayServer = class {
|
|
|
1868
2290
|
} catch {
|
|
1869
2291
|
return this.sendJson(res, 400, { error: "Invalid X-Payment header" });
|
|
1870
2292
|
}
|
|
2293
|
+
const payScheme = payment.accepted?.scheme || payment.scheme;
|
|
2294
|
+
const payNetwork = payment.accepted?.network || payment.network;
|
|
2295
|
+
if (payScheme === ALIPAY_SCHEME || (payNetwork ? isAlipayChainId(payNetwork) : false)) {
|
|
2296
|
+
return this.handleAlipayExecute(skill, params || {}, payment, res);
|
|
2297
|
+
}
|
|
1871
2298
|
const validation = this.validatePayment(payment, skill.config);
|
|
1872
2299
|
if (!validation.valid) {
|
|
1873
2300
|
return this.sendJson(res, 402, { error: validation.error });
|
|
@@ -1960,13 +2387,111 @@ var MoltsPayServer = class {
|
|
|
1960
2387
|
payment: settlement?.success ? { transaction: settlement.transaction, status: "settled", facilitator: settlement.facilitator } : { status: "pending" }
|
|
1961
2388
|
}, responseHeaders);
|
|
1962
2389
|
}
|
|
2390
|
+
/**
|
|
2391
|
+
* Execute a service paid via the Alipay AI 收 fiat rail (2.0.0).
|
|
2392
|
+
*
|
|
2393
|
+
* Differs from the EVM/SVM path: no token detection, no EIP-3009/permit
|
|
2394
|
+
* validation. Verify hits the Alipay Open API (`payment.verify`). Settlement
|
|
2395
|
+
* (`fulfillment.confirm`) is FIRE-AND-FORGET per ALIPAY-INTEGRATION-DESIGN
|
|
2396
|
+
* §5.1: a confirm failure is logged but does NOT fail the already-delivered
|
|
2397
|
+
* response (the buyer's payment proof was already verified).
|
|
2398
|
+
*/
|
|
2399
|
+
async handleAlipayExecute(skill, params, payment, res) {
|
|
2400
|
+
if (!this.alipayFacilitator) {
|
|
2401
|
+
return this.sendJson(res, 402, { error: "Alipay rail not configured on this server" });
|
|
2402
|
+
}
|
|
2403
|
+
const requirements = {
|
|
2404
|
+
scheme: ALIPAY_SCHEME,
|
|
2405
|
+
network: ALIPAY_NETWORK,
|
|
2406
|
+
asset: "CNY",
|
|
2407
|
+
amount: skill.config.alipay?.price_cny || "0",
|
|
2408
|
+
payTo: this.manifest.provider.alipay?.seller_id || "",
|
|
2409
|
+
maxTimeoutSeconds: 1800
|
|
2410
|
+
};
|
|
2411
|
+
console.log(`[MoltsPay] Verifying Alipay payment...`);
|
|
2412
|
+
const verifyResult = await this.registry.verify(payment, requirements);
|
|
2413
|
+
if (!verifyResult.valid) {
|
|
2414
|
+
return this.sendJson(res, 402, {
|
|
2415
|
+
error: `Payment verification failed: ${verifyResult.error}`,
|
|
2416
|
+
facilitator: verifyResult.facilitator
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
console.log(`[MoltsPay] Alipay payment verified by ${verifyResult.facilitator}`);
|
|
2420
|
+
const timeoutSeconds = parseInt(process.env.SKILL_TIMEOUT_SECONDS || "1200");
|
|
2421
|
+
console.log(`[MoltsPay] Executing skill: ${skill.id} (timeout: ${timeoutSeconds}s)`);
|
|
2422
|
+
let result;
|
|
2423
|
+
try {
|
|
2424
|
+
result = await Promise.race([
|
|
2425
|
+
skill.handler(params),
|
|
2426
|
+
new Promise(
|
|
2427
|
+
(_, reject) => setTimeout(() => reject(new Error(`Skill timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3)
|
|
2428
|
+
)
|
|
2429
|
+
]);
|
|
2430
|
+
} catch (err) {
|
|
2431
|
+
console.error("[MoltsPay] Skill execution failed:", err.message);
|
|
2432
|
+
return this.sendJson(res, 500, {
|
|
2433
|
+
error: "Service execution failed",
|
|
2434
|
+
message: err.message
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2437
|
+
let settlement;
|
|
2438
|
+
try {
|
|
2439
|
+
settlement = await this.registry.settle(payment, requirements);
|
|
2440
|
+
if (settlement.success) {
|
|
2441
|
+
console.log(`[MoltsPay] Alipay fulfillment confirmed: ${settlement.transaction}`);
|
|
2442
|
+
} else {
|
|
2443
|
+
console.error(`[MoltsPay] Alipay fulfillment confirm failed (non-fatal): ${settlement.error}`);
|
|
2444
|
+
}
|
|
2445
|
+
} catch (err) {
|
|
2446
|
+
console.error(`[MoltsPay] Alipay fulfillment confirm threw (non-fatal): ${err.message}`);
|
|
2447
|
+
settlement = { success: false, error: err.message, facilitator: "alipay" };
|
|
2448
|
+
}
|
|
2449
|
+
const responseHeaders = {};
|
|
2450
|
+
if (settlement.success) {
|
|
2451
|
+
responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(JSON.stringify({
|
|
2452
|
+
success: true,
|
|
2453
|
+
transaction: settlement.transaction,
|
|
2454
|
+
network: ALIPAY_NETWORK,
|
|
2455
|
+
facilitator: settlement.facilitator
|
|
2456
|
+
})).toString("base64");
|
|
2457
|
+
}
|
|
2458
|
+
this.sendJson(res, 200, {
|
|
2459
|
+
success: true,
|
|
2460
|
+
result,
|
|
2461
|
+
payment: settlement.success ? { transaction: settlement.transaction, status: "fulfilled", facilitator: settlement.facilitator } : { status: "delivered_unconfirmed", error: settlement.error }
|
|
2462
|
+
}, responseHeaders);
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2465
|
+
* Build the Alipay 402 challenge for a service, or null when the alipay rail
|
|
2466
|
+
* isn't configured for this server or this service. Returns the x402
|
|
2467
|
+
* `accepts[]` entry plus the Base64URL `Payment-Needed` header value so the
|
|
2468
|
+
* 402 responders can dual-emit both the x402 and legacy alipay-bot formats.
|
|
2469
|
+
*/
|
|
2470
|
+
async buildAlipayChallenge(config) {
|
|
2471
|
+
if (!this.alipayFacilitator || !config.alipay) return null;
|
|
2472
|
+
try {
|
|
2473
|
+
const req = await this.alipayFacilitator.createPaymentRequirements({
|
|
2474
|
+
serviceId: config.alipay.service_id || this.manifest.provider.alipay.service_id_default,
|
|
2475
|
+
priceCny: config.alipay.price_cny,
|
|
2476
|
+
goodsName: config.alipay.goods_name,
|
|
2477
|
+
resourceId: `/execute?service=${config.id}`
|
|
2478
|
+
});
|
|
2479
|
+
return { accepts: req.x402Accepts, paymentNeededHeader: req.paymentNeededHeader };
|
|
2480
|
+
} catch (err) {
|
|
2481
|
+
console.error(`[MoltsPay] Alipay challenge build failed for ${config.id}: ${err.message}`);
|
|
2482
|
+
return null;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
1963
2485
|
/**
|
|
1964
2486
|
* Handle MPP (Machine Payments Protocol) request
|
|
1965
2487
|
* Supports both x402 and MPP protocols on service endpoints
|
|
1966
2488
|
*/
|
|
1967
|
-
async handleMPPRequest(skill, body, authHeader, x402Header, res) {
|
|
2489
|
+
async handleMPPRequest(skill, body, authHeader, x402Header, res, proofHeader) {
|
|
1968
2490
|
const config = skill.config;
|
|
1969
2491
|
const params = body || {};
|
|
2492
|
+
if (proofHeader) {
|
|
2493
|
+
return await this.handleExecute({ service: config.id, params }, void 0, res, proofHeader);
|
|
2494
|
+
}
|
|
1970
2495
|
if (x402Header) {
|
|
1971
2496
|
return await this.handleExecute({ service: config.id, params }, x402Header, res);
|
|
1972
2497
|
}
|
|
@@ -2072,7 +2597,7 @@ var MoltsPayServer = class {
|
|
|
2072
2597
|
/**
|
|
2073
2598
|
* Return 402 with both x402 and MPP payment requirements
|
|
2074
2599
|
*/
|
|
2075
|
-
sendMPPPaymentRequired(config, res) {
|
|
2600
|
+
async sendMPPPaymentRequired(config, res) {
|
|
2076
2601
|
const acceptedTokens = getAcceptedCurrencies(config);
|
|
2077
2602
|
const providerChains = this.getProviderChains();
|
|
2078
2603
|
const accepts = [];
|
|
@@ -2083,6 +2608,10 @@ var MoltsPayServer = class {
|
|
|
2083
2608
|
}
|
|
2084
2609
|
}
|
|
2085
2610
|
}
|
|
2611
|
+
const alipayChallenge = await this.buildAlipayChallenge(config);
|
|
2612
|
+
if (alipayChallenge) {
|
|
2613
|
+
accepts.push(alipayChallenge.accepts);
|
|
2614
|
+
}
|
|
2086
2615
|
const x402PaymentRequired = {
|
|
2087
2616
|
x402Version: X402_VERSION2,
|
|
2088
2617
|
accepts,
|
|
@@ -2110,7 +2639,7 @@ var MoltsPayServer = class {
|
|
|
2110
2639
|
};
|
|
2111
2640
|
const mppRequestEncoded = Buffer.from(JSON.stringify(mppRequest)).toString("base64");
|
|
2112
2641
|
const expiresAt = new Date(Date.now() + 5 * 60 * 1e3).toISOString();
|
|
2113
|
-
mppWwwAuth = `Payment id="${challengeId}", realm="${this.manifest.provider.name}", method="tempo", intent="charge", request="${mppRequestEncoded}", description="${config.name}", expires="${expiresAt}"`;
|
|
2642
|
+
mppWwwAuth = `Payment id="${challengeId}", realm="${headerSafe(this.manifest.provider.name)}", method="tempo", intent="charge", request="${mppRequestEncoded}", description="${headerSafe(config.name)}", expires="${expiresAt}"`;
|
|
2114
2643
|
}
|
|
2115
2644
|
const headers = {
|
|
2116
2645
|
"Content-Type": "application/problem+json",
|
|
@@ -2119,6 +2648,9 @@ var MoltsPayServer = class {
|
|
|
2119
2648
|
if (mppWwwAuth) {
|
|
2120
2649
|
headers[MPP_WWW_AUTH_HEADER] = mppWwwAuth;
|
|
2121
2650
|
}
|
|
2651
|
+
if (alipayChallenge) {
|
|
2652
|
+
headers[ALIPAY_PAYMENT_NEEDED_HEADER] = alipayChallenge.paymentNeededHeader;
|
|
2653
|
+
}
|
|
2122
2654
|
res.writeHead(402, headers);
|
|
2123
2655
|
res.end(JSON.stringify({
|
|
2124
2656
|
type: "https://paymentauth.org/problems/payment-required",
|
|
@@ -2145,7 +2677,7 @@ var MoltsPayServer = class {
|
|
|
2145
2677
|
* Return 402 with x402 payment requirements (v2 format)
|
|
2146
2678
|
* Includes requirements for all chains and all accepted currencies
|
|
2147
2679
|
*/
|
|
2148
|
-
sendPaymentRequired(config, res) {
|
|
2680
|
+
async sendPaymentRequired(config, res) {
|
|
2149
2681
|
const acceptedTokens = getAcceptedCurrencies(config);
|
|
2150
2682
|
const providerChains = this.getProviderChains();
|
|
2151
2683
|
const accepts = [];
|
|
@@ -2156,6 +2688,10 @@ var MoltsPayServer = class {
|
|
|
2156
2688
|
}
|
|
2157
2689
|
}
|
|
2158
2690
|
}
|
|
2691
|
+
const alipayChallenge = await this.buildAlipayChallenge(config);
|
|
2692
|
+
if (alipayChallenge) {
|
|
2693
|
+
accepts.push(alipayChallenge.accepts);
|
|
2694
|
+
}
|
|
2159
2695
|
const acceptedChains = providerChains.map((c) => {
|
|
2160
2696
|
if (c.network === "eip155:8453") return "base";
|
|
2161
2697
|
if (c.network === "eip155:137") return "polygon";
|
|
@@ -2173,10 +2709,14 @@ var MoltsPayServer = class {
|
|
|
2173
2709
|
}
|
|
2174
2710
|
};
|
|
2175
2711
|
const encoded = Buffer.from(JSON.stringify(paymentRequired)).toString("base64");
|
|
2176
|
-
|
|
2712
|
+
const headers = {
|
|
2177
2713
|
"Content-Type": "application/json",
|
|
2178
2714
|
[PAYMENT_REQUIRED_HEADER]: encoded
|
|
2179
|
-
}
|
|
2715
|
+
};
|
|
2716
|
+
if (alipayChallenge) {
|
|
2717
|
+
headers[ALIPAY_PAYMENT_NEEDED_HEADER] = alipayChallenge.paymentNeededHeader;
|
|
2718
|
+
}
|
|
2719
|
+
res.writeHead(402, headers);
|
|
2180
2720
|
res.end(JSON.stringify({
|
|
2181
2721
|
error: "Payment required",
|
|
2182
2722
|
message: `Service requires $${config.price} ${config.currency}`,
|
|
@@ -2283,12 +2823,12 @@ var MoltsPayServer = class {
|
|
|
2283
2823
|
return accepted.includes(token);
|
|
2284
2824
|
}
|
|
2285
2825
|
async readBody(req) {
|
|
2286
|
-
return new Promise((
|
|
2826
|
+
return new Promise((resolve2, reject) => {
|
|
2287
2827
|
let body = "";
|
|
2288
2828
|
req.on("data", (chunk) => body += chunk);
|
|
2289
2829
|
req.on("end", () => {
|
|
2290
2830
|
try {
|
|
2291
|
-
|
|
2831
|
+
resolve2(body ? JSON.parse(body) : {});
|
|
2292
2832
|
} catch {
|
|
2293
2833
|
reject(new Error("Invalid JSON"));
|
|
2294
2834
|
}
|
|
@@ -2546,7 +3086,7 @@ var MoltsPayServer = class {
|
|
|
2546
3086
|
};
|
|
2547
3087
|
const mppRequestEncoded = Buffer.from(JSON.stringify(mppRequest)).toString("base64");
|
|
2548
3088
|
const expiresAt = new Date(Date.now() + 5 * 60 * 1e3).toISOString();
|
|
2549
|
-
const wwwAuth = `Payment id="${challengeId}", realm="MoltsPay Proxy", method="tempo", intent="charge", request="${mppRequestEncoded}", description="${config.name}", expires="${expiresAt}"`;
|
|
3089
|
+
const wwwAuth = `Payment id="${challengeId}", realm="MoltsPay Proxy", method="tempo", intent="charge", request="${mppRequestEncoded}", description="${headerSafe(config.name)}", expires="${expiresAt}"`;
|
|
2550
3090
|
res.writeHead(402, {
|
|
2551
3091
|
"Content-Type": "application/problem+json",
|
|
2552
3092
|
[MPP_WWW_AUTH_HEADER]: wwwAuth
|