@devlab.group/agent-commerce 1.4.1 → 1.5.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 +111 -128
- package/dist/ap2.d.ts +5 -5
- package/dist/ap2.js +19 -11
- package/dist/ap2.js.map +1 -1
- package/dist/{authorization-CPDcpJVa.d.ts → authorization-DA7mjy_O.d.ts} +4 -4
- package/dist/{chunk-ZEGFLRGV.js → chunk-5GEWKNIH.js} +5 -4
- package/dist/chunk-5GEWKNIH.js.map +1 -0
- package/dist/chunk-6D2EZ5ME.js +27 -0
- package/dist/chunk-6D2EZ5ME.js.map +1 -0
- package/dist/chunk-NKNXVDHN.js +701 -0
- package/dist/chunk-NKNXVDHN.js.map +1 -0
- package/dist/{chunk-NWIQLJNU.js → chunk-PBYN7EK2.js} +33 -31
- package/dist/chunk-PBYN7EK2.js.map +1 -0
- package/dist/{chunk-GF6KHHKL.js → chunk-US622BKF.js} +3 -3
- package/dist/{chunk-GF6KHHKL.js.map → chunk-US622BKF.js.map} +1 -1
- package/dist/cli/index.js +339 -107
- package/dist/cli/index.js.map +1 -1
- package/dist/constants-6mypC60h.d.ts +50 -0
- package/dist/index.d.ts +26 -11
- package/dist/index.js +189 -36
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +3 -3
- package/dist/mcp.js +4 -1
- package/dist/mcp.js.map +1 -1
- package/dist/mpp.d.ts +50 -0
- package/dist/mpp.js +351 -0
- package/dist/mpp.js.map +1 -0
- package/dist/{protocol-adapter-Bebmggf_.d.ts → protocol-adapter-APRjs1E2.d.ts} +2 -2
- package/dist/{runtime-CKq3d_3K.d.ts → runtime-DC5XQ9Cc.d.ts} +24 -13
- package/dist/x402.d.ts +1 -1
- package/dist/x402.js +7 -688
- package/dist/x402.js.map +1 -1
- package/package.json +13 -2
- package/dist/chunk-NWIQLJNU.js.map +0 -1
- package/dist/chunk-ZEGFLRGV.js.map +0 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one MPP profile this rail targets.
|
|
3
|
+
*
|
|
4
|
+
* MPP combines a core HTTP authentication layer, an intent, a method and
|
|
5
|
+
* optional extensions. These constants identify the selected combination and
|
|
6
|
+
* the exact specification revision.
|
|
7
|
+
*
|
|
8
|
+
* Importing this metadata does not load `mppx`.
|
|
9
|
+
*/
|
|
10
|
+
/** Upstream specification repository the pinned drafts were read from */
|
|
11
|
+
declare const MPP_SPEC_REPOSITORY = "https://github.com/tempoxyz/mpp-specs";
|
|
12
|
+
/**
|
|
13
|
+
* Upstream commit the pinned drafts were read at.
|
|
14
|
+
*
|
|
15
|
+
* A draft's content can change without its `-00` name changing, so the name
|
|
16
|
+
* alone does not identify a document; the commit does.
|
|
17
|
+
*/
|
|
18
|
+
declare const MPP_SPEC_COMMIT = "806fdb8b8c92cda5c84b8660558921b6bbeef7e0";
|
|
19
|
+
/** The core HTTP authentication draft, the `charge` intent, and its EVM method */
|
|
20
|
+
declare const MPP_SPEC_DRAFTS: {
|
|
21
|
+
readonly core: "draft-httpauth-payment-00";
|
|
22
|
+
readonly intent: "draft-payment-intent-charge-00";
|
|
23
|
+
readonly method: "draft-evm-charge-00";
|
|
24
|
+
};
|
|
25
|
+
/** Exact `mppx` release the rail is pinned to */
|
|
26
|
+
declare const MPPX_VERSION = "0.10.1";
|
|
27
|
+
/**
|
|
28
|
+
* The pinned profile.
|
|
29
|
+
*
|
|
30
|
+
* `intent`, `method` and `credentialType` copy MPP wire identifiers from
|
|
31
|
+
* `mppx` so this module has no peer import; tests detect drift. `assetSymbol`
|
|
32
|
+
* is the pricing currency an MPP resource must use. EVM charge requests carry a
|
|
33
|
+
* numeric `chainId` and token address instead.
|
|
34
|
+
*/
|
|
35
|
+
declare const MPP_PROFILE: {
|
|
36
|
+
readonly intent: "charge";
|
|
37
|
+
readonly method: "evm";
|
|
38
|
+
readonly credentialType: "authorization";
|
|
39
|
+
readonly assetSymbol: "USDC";
|
|
40
|
+
readonly assetDecimals: 6;
|
|
41
|
+
};
|
|
42
|
+
/** Supported CAIP-2 networks; gateway config applies the x402 mainnet guardrails */
|
|
43
|
+
declare const MPP_NETWORKS: readonly ["eip155:84532", "eip155:8453"];
|
|
44
|
+
type MppNetwork = (typeof MPP_NETWORKS)[number];
|
|
45
|
+
/**
|
|
46
|
+
* Compact descriptor value naming the core draft, intent and method
|
|
47
|
+
*/
|
|
48
|
+
declare const MPP_SUPPORTED_SPEC: string;
|
|
49
|
+
|
|
50
|
+
export { type MppNetwork as M, MPPX_VERSION as a, MPP_PROFILE as b, MPP_SPEC_COMMIT as c, MPP_SPEC_DRAFTS as d, MPP_SPEC_REPOSITORY as e, MPP_SUPPORTED_SPEC as f };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { f as Ap2AuthorizationConfig } from './types-CJtwLu1c.js';
|
|
2
|
-
import { I as IsoTimestamp, D as DecimalAmount, P as PaymentMethodName, a as CommerceResource, b as PaymentSubmission, B as BackendHandler, A as AdapterDescriptor, c as AdapterHealth, R as ResourceRegistry, d as PaymentProvider, L as Logger, C as Clock, e as IdGenerator } from './runtime-
|
|
3
|
-
export { f as AuthorizationMethodName, g as BackendMethod, h as DEFAULT_BACKEND_TIMEOUT_MS, J as JsonSchema, N as NOOP_LOGGER, i as
|
|
2
|
+
import { I as IsoTimestamp, D as DecimalAmount, P as PaymentMethodName, a as CommerceResource, b as PaymentSubmission, B as BackendHandler, A as AdapterDescriptor, c as AdapterHealth, R as ResourceRegistry, d as PaymentProvider, L as Logger, C as Clock, e as IdGenerator } from './runtime-DC5XQ9Cc.js';
|
|
3
|
+
export { f as AuthorizationMethodName, g as BackendMethod, h as DEFAULT_BACKEND_TIMEOUT_MS, J as JsonSchema, N as NOOP_LOGGER, i as PAYMENT_METHOD_NAMES, j as PROTOCOL_NAMES, k as PaymentChallenge, l as PaymentContext, m as PaymentRequirement, n as PaymentResult, o as PaymentSettlementContext, p as PaymentVerificationContext, q as Pricing, r as ProtocolName, s as systemClock } from './runtime-DC5XQ9Cc.js';
|
|
4
|
+
import { M as MppNetwork } from './constants-6mypC60h.js';
|
|
4
5
|
import { X as X402FacilitatorConfig } from './guardrails-Dmwi63KB.js';
|
|
5
|
-
import { a as AuthorizationRequirement, b as AuthorizationSubmission, A as AuthorizationProvider } from './authorization-
|
|
6
|
-
export { c as AuthorizationFinalizeContext, d as AuthorizationRecord, e as AuthorizationVerification, f as AuthorizationVerificationContext } from './authorization-
|
|
7
|
-
import { D as DeliveredOutcome, P as PaymentRequiredOutcome, C as CommerceEvent, a as PaymentAttempt, b as CommerceReceipt, E as ExecutionPipeline, c as ProtocolAdapter, H as HttpProtocolAdapter, A as AdapterHttpRoute, d as ProtocolAdapterContext } from './protocol-adapter-
|
|
8
|
-
export { e as COMMERCE_EVENT_TYPES, f as CanonicalRequest, g as CommerceEventType, h as EventSink, i as ExecutionOutcome, j as isHttpProtocolAdapter } from './protocol-adapter-
|
|
6
|
+
import { a as AuthorizationRequirement, b as AuthorizationSubmission, A as AuthorizationProvider } from './authorization-DA7mjy_O.js';
|
|
7
|
+
export { c as AuthorizationFinalizeContext, d as AuthorizationRecord, e as AuthorizationVerification, f as AuthorizationVerificationContext } from './authorization-DA7mjy_O.js';
|
|
8
|
+
import { D as DeliveredOutcome, P as PaymentRequiredOutcome, C as CommerceEvent, a as PaymentAttempt, b as CommerceReceipt, E as ExecutionPipeline, c as ProtocolAdapter, H as HttpProtocolAdapter, A as AdapterHttpRoute, d as ProtocolAdapterContext } from './protocol-adapter-APRjs1E2.js';
|
|
9
|
+
export { e as COMMERCE_EVENT_TYPES, f as CanonicalRequest, g as CommerceEventType, h as EventSink, i as ExecutionOutcome, j as isHttpProtocolAdapter } from './protocol-adapter-APRjs1E2.js';
|
|
9
10
|
import { FastifyInstance } from 'fastify';
|
|
10
11
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
11
12
|
|
|
@@ -278,11 +279,10 @@ declare function parseAuthorizationHeader(raw: string | readonly string[] | unde
|
|
|
278
279
|
* (MCP tool arguments, A2A message data). HTTP carries both in headers and
|
|
279
280
|
* uses the two parse helpers directly.
|
|
280
281
|
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
* on its own terms.
|
|
282
|
+
* A payment proof is labelled with the resource's first method. `createGateway`
|
|
283
|
+
* puts provider-backed methods first so the label matches pipeline selection;
|
|
284
|
+
* custom registries must preserve that ordering. Without a method, the proof is
|
|
285
|
+
* dropped and the pipeline receives an unpaid request.
|
|
286
286
|
*/
|
|
287
287
|
declare function extractReservedInputFields(rawInput: Record<string, unknown>, resource: CommerceResource | undefined, requestId?: string): {
|
|
288
288
|
input: Record<string, unknown>;
|
|
@@ -546,6 +546,21 @@ interface GatewayConfig {
|
|
|
546
546
|
readonly allowMainnet?: boolean;
|
|
547
547
|
readonly allowUnauthenticatedFacilitator?: boolean;
|
|
548
548
|
};
|
|
549
|
+
readonly mpp?: {
|
|
550
|
+
readonly enabled: boolean;
|
|
551
|
+
readonly network: MppNetwork;
|
|
552
|
+
readonly rpcUrl: string;
|
|
553
|
+
readonly asset: string;
|
|
554
|
+
readonly assetName: string;
|
|
555
|
+
readonly assetVersion: string;
|
|
556
|
+
readonly recipient: string;
|
|
557
|
+
readonly realm: string;
|
|
558
|
+
readonly challengeSecret: string;
|
|
559
|
+
readonly challengeTtlSeconds?: number;
|
|
560
|
+
readonly facilitator: X402FacilitatorConfig;
|
|
561
|
+
readonly allowMainnet?: boolean;
|
|
562
|
+
readonly allowUnauthenticatedFacilitator?: boolean;
|
|
563
|
+
};
|
|
549
564
|
};
|
|
550
565
|
/**
|
|
551
566
|
* Absent when no `authorization:` block is configured, which is how every
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { AP2_SPEC_VERSION, AP2_MODES, AP2_DEFAULT_CLOCK_SKEW_SECONDS, AP2_JWK_MEMBERS, AP2_KEY_TYPE, AP2_SIGNING_ALGORITHM, AP2_JWK_CURVE, AP2_JWK_COORDINATE_BYTES, openSqliteDatabase, AP2_MAX_CLOCK_SKEW_SECONDS } from './chunk-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { AP2_SPEC_VERSION, AP2_MODES, AP2_DEFAULT_CLOCK_SKEW_SECONDS, AP2_JWK_MEMBERS, AP2_KEY_TYPE, AP2_SIGNING_ALGORITHM, AP2_JWK_CURVE, AP2_JWK_COORDINATE_BYTES, openSqliteDatabase, AP2_MAX_CLOCK_SKEW_SECONDS } from './chunk-US622BKF.js';
|
|
2
|
+
import { MPP_DEFAULT_NETWORK, isMppNetwork, MPP_NETWORKS, MPP_MIN_CHALLENGE_SECRET_LENGTH, MPP_PROFILE } from './chunk-6D2EZ5ME.js';
|
|
3
|
+
import { resolveX402Deployment, resolveDeploymentMode, requireNetworkProfile } from './chunk-PBYN7EK2.js';
|
|
4
|
+
import { RESERVED_INPUT_FIELDS, PROTOCOL_NAMES, PAYMENT_METHOD_NAMES, PAYMENT_HEADER, AUTHORIZATION_HEADER, CommerceError, systemClock, NOOP_LOGGER, PACKAGE_VERSION, toCommerceError, DEFAULT_BACKEND_TIMEOUT_MS, toErrorEnvelope, isHttpProtocolAdapter, isCommerceError, parseAuthorizationHeader, toPaymentRequiredEnvelope, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER } from './chunk-5GEWKNIH.js';
|
|
5
|
+
export { AUTHORIZATION_HEADER, AUTHORIZATION_INPUT_FIELD, COMMERCE_ERROR_CODES, COMMERCE_ERROR_HTTP_STATUS, COMMERCE_EVENT_TYPES, CommerceError, DEFAULT_BACKEND_TIMEOUT_MS, DELIVERY_SUMMARY_META_KEY, MAX_AUTHORIZATION_HEADER_BYTES, NOOP_LOGGER, PAYMENT_HEADER, PAYMENT_INPUT_FIELD, PAYMENT_METHOD_NAMES, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, PROTOCOL_NAMES, RESERVED_INPUT_FIELDS, RETRYABLE_ERROR_CODES, extractReservedInputFields, isCommerceError, isHttpProtocolAdapter, isPaymentRequiredEnvelope, parseAuthorizationHeader, parseAuthorizationSubmission, systemClock, toCommerceError, toDeliverySummary, toErrorEnvelope, toPaymentRequiredEnvelope } from './chunk-5GEWKNIH.js';
|
|
5
6
|
import { promises, accessSync, constants } from 'fs';
|
|
6
7
|
import * as path from 'path';
|
|
7
8
|
import { parse, YAMLError } from 'yaml';
|
|
@@ -822,7 +823,8 @@ function createExecutionPipeline(options) {
|
|
|
822
823
|
const mapped = isCommerceError(error) ? error : new CommerceError("PAYMENT_INVALID", "Payment verification failed", {
|
|
823
824
|
requestId: request.requestId,
|
|
824
825
|
resourceId: resource.id,
|
|
825
|
-
cause: error
|
|
826
|
+
cause: error,
|
|
827
|
+
...retryChallenge(requirement)
|
|
826
828
|
});
|
|
827
829
|
await safeEmit(
|
|
828
830
|
buildEvent({
|
|
@@ -854,7 +856,8 @@ function createExecutionPipeline(options) {
|
|
|
854
856
|
verification.rejectionReason ?? "Payment was rejected",
|
|
855
857
|
{
|
|
856
858
|
requestId: request.requestId,
|
|
857
|
-
resourceId: resource.id
|
|
859
|
+
resourceId: resource.id,
|
|
860
|
+
...retryChallenge(requirement)
|
|
858
861
|
}
|
|
859
862
|
);
|
|
860
863
|
}
|
|
@@ -1077,7 +1080,8 @@ function createExecutionPipeline(options) {
|
|
|
1077
1080
|
amount: paymentResult.amount,
|
|
1078
1081
|
currency: paymentResult.currency,
|
|
1079
1082
|
...paymentResult.network !== void 0 ? { network: paymentResult.network } : {},
|
|
1080
|
-
...paymentResult.externalReference !== void 0 ? { externalReference: paymentResult.externalReference } : {}
|
|
1083
|
+
...paymentResult.externalReference !== void 0 ? { externalReference: paymentResult.externalReference } : {},
|
|
1084
|
+
...typeof paymentResult.metadata?.["receipt"] === "string" ? { receipt: paymentResult.metadata["receipt"] } : {}
|
|
1081
1085
|
}
|
|
1082
1086
|
}
|
|
1083
1087
|
}
|
|
@@ -1155,6 +1159,10 @@ function resolveAuthorizationProviders(providers, resource, requestId) {
|
|
|
1155
1159
|
return found;
|
|
1156
1160
|
});
|
|
1157
1161
|
}
|
|
1162
|
+
function retryChallenge(requirement) {
|
|
1163
|
+
const envelope = requirement.challenge.envelope;
|
|
1164
|
+
return envelope === void 0 ? {} : { details: { challenge: envelope } };
|
|
1165
|
+
}
|
|
1158
1166
|
function pickProvider(providers, methods) {
|
|
1159
1167
|
for (const method of methods) {
|
|
1160
1168
|
const found = providers.find((provider) => provider.name === method);
|
|
@@ -1422,8 +1430,26 @@ var X402Schema = z.object({
|
|
|
1422
1430
|
/** Accepts a mainnet facilitator that takes no credential. Never defaulted. */
|
|
1423
1431
|
allowUnauthenticatedFacilitator: BooleanOrString.optional()
|
|
1424
1432
|
}).strict();
|
|
1433
|
+
var MppSchema = z.object({
|
|
1434
|
+
enabled: BooleanOrString,
|
|
1435
|
+
network: z.string().min(1).optional(),
|
|
1436
|
+
rpcUrl: z.string().min(1),
|
|
1437
|
+
asset: z.string().min(1),
|
|
1438
|
+
assetName: z.string().min(1),
|
|
1439
|
+
assetVersion: z.string().min(1),
|
|
1440
|
+
recipient: z.string().min(1),
|
|
1441
|
+
realm: z.string().min(1),
|
|
1442
|
+
challengeSecret: z.string().min(1),
|
|
1443
|
+
challengeTtlSeconds: NumberOrString.optional(),
|
|
1444
|
+
facilitator: FacilitatorSchema,
|
|
1445
|
+
// Real funds. Never defaulted - see src/payments/x402/guardrails.ts
|
|
1446
|
+
allowMainnet: BooleanOrString.optional(),
|
|
1447
|
+
// Accepts a mainnet facilitator that takes no credential. Never defaulted
|
|
1448
|
+
allowUnauthenticatedFacilitator: BooleanOrString.optional()
|
|
1449
|
+
}).strict();
|
|
1425
1450
|
var PaymentsSchema = z.object({
|
|
1426
|
-
x402: X402Schema.optional()
|
|
1451
|
+
x402: X402Schema.optional(),
|
|
1452
|
+
mpp: MppSchema.optional()
|
|
1427
1453
|
}).strict();
|
|
1428
1454
|
var Ap2KeySchema = z.object({
|
|
1429
1455
|
kid: z.string().min(1),
|
|
@@ -1559,7 +1585,7 @@ function toBoolean(value, path2) {
|
|
|
1559
1585
|
);
|
|
1560
1586
|
}
|
|
1561
1587
|
var SUPPORTED_PROTOCOLS = new Set(PROTOCOL_NAMES);
|
|
1562
|
-
var SUPPORTED_PAYMENT_METHODS =
|
|
1588
|
+
var SUPPORTED_PAYMENT_METHODS = new Set(PAYMENT_METHOD_NAMES);
|
|
1563
1589
|
var SUPPORTED_AUTHORIZATION_METHODS = /* @__PURE__ */ new Set(["ap2"]);
|
|
1564
1590
|
function normalise(raw) {
|
|
1565
1591
|
const protocols = {
|
|
@@ -1576,12 +1602,7 @@ function normalise(raw) {
|
|
|
1576
1602
|
};
|
|
1577
1603
|
validateMountPaths(protocols);
|
|
1578
1604
|
const x402Raw = raw.payments.x402;
|
|
1579
|
-
const
|
|
1580
|
-
mode: "remote",
|
|
1581
|
-
url: x402Raw.facilitator.url,
|
|
1582
|
-
auth: x402Raw.facilitator.auth ?? { type: "none" }
|
|
1583
|
-
};
|
|
1584
|
-
const x402 = x402Raw && facilitator ? {
|
|
1605
|
+
const x402 = x402Raw !== void 0 ? {
|
|
1585
1606
|
enabled: toBoolean(x402Raw.enabled, "payments.x402.enabled"),
|
|
1586
1607
|
network: x402Raw.network,
|
|
1587
1608
|
rpcUrl: x402Raw.rpcUrl,
|
|
@@ -1600,7 +1621,7 @@ function normalise(raw) {
|
|
|
1600
1621
|
min: 1
|
|
1601
1622
|
}
|
|
1602
1623
|
),
|
|
1603
|
-
facilitator,
|
|
1624
|
+
facilitator: normaliseFacilitator(x402Raw.facilitator),
|
|
1604
1625
|
...x402Raw.allowMainnet !== void 0 ? { allowMainnet: toBoolean(x402Raw.allowMainnet, "payments.x402.allowMainnet") } : {},
|
|
1605
1626
|
...x402Raw.allowUnauthenticatedFacilitator !== void 0 ? {
|
|
1606
1627
|
allowUnauthenticatedFacilitator: toBoolean(
|
|
@@ -1623,12 +1644,13 @@ function normalise(raw) {
|
|
|
1623
1644
|
...x402.allowUnauthenticatedFacilitator !== void 0 ? { allowUnauthenticatedFacilitator: x402.allowUnauthenticatedFacilitator } : {}
|
|
1624
1645
|
});
|
|
1625
1646
|
}
|
|
1647
|
+
const mpp = normaliseMpp(raw.payments.mpp);
|
|
1626
1648
|
const ap2 = normaliseAp2(raw.authorization?.ap2);
|
|
1627
1649
|
if (ap2?.enabled) {
|
|
1628
1650
|
validateReplayStoreIsolated(ap2.replay.path, raw.storage.receipts.path, protocols.acp);
|
|
1629
1651
|
}
|
|
1630
1652
|
const resources = Object.entries(raw.resources).map(
|
|
1631
|
-
([id, entry]) => normaliseResource(id, entry, protocols, x402, ap2)
|
|
1653
|
+
([id, entry]) => normaliseResource(id, entry, protocols, x402, mpp, ap2)
|
|
1632
1654
|
);
|
|
1633
1655
|
if (protocols.acp.enabled) validateAcpCheckoutMapping(protocols.acp, resources);
|
|
1634
1656
|
return {
|
|
@@ -1650,7 +1672,8 @@ function normalise(raw) {
|
|
|
1650
1672
|
protocols,
|
|
1651
1673
|
resources,
|
|
1652
1674
|
payments: {
|
|
1653
|
-
...x402 !== void 0 ? { x402 } : {}
|
|
1675
|
+
...x402 !== void 0 ? { x402 } : {},
|
|
1676
|
+
...mpp !== void 0 ? { mpp } : {}
|
|
1654
1677
|
},
|
|
1655
1678
|
...ap2 !== void 0 ? { authorization: { ap2 } } : {}
|
|
1656
1679
|
};
|
|
@@ -2002,7 +2025,74 @@ function validateAcpOperationInput(path2, operation, resource) {
|
|
|
2002
2025
|
}
|
|
2003
2026
|
}
|
|
2004
2027
|
}
|
|
2005
|
-
function
|
|
2028
|
+
function normaliseFacilitator(raw) {
|
|
2029
|
+
return raw.mode === "local" ? { mode: "local", signerPrivateKey: raw.signerPrivateKey } : { mode: "remote", url: raw.url, auth: raw.auth ?? { type: "none" } };
|
|
2030
|
+
}
|
|
2031
|
+
function normaliseMpp(raw) {
|
|
2032
|
+
if (raw === void 0) return void 0;
|
|
2033
|
+
const network = raw.network ?? MPP_DEFAULT_NETWORK;
|
|
2034
|
+
if (!isMppNetwork(network)) {
|
|
2035
|
+
throw new CommerceError(
|
|
2036
|
+
"CONFIG_INVALID",
|
|
2037
|
+
`payments.mpp.network must be one of ${MPP_NETWORKS.join(", ")}`,
|
|
2038
|
+
{ details: { path: "payments.mpp.network" } }
|
|
2039
|
+
);
|
|
2040
|
+
}
|
|
2041
|
+
const mpp = {
|
|
2042
|
+
enabled: toBoolean(raw.enabled, "payments.mpp.enabled"),
|
|
2043
|
+
network,
|
|
2044
|
+
rpcUrl: raw.rpcUrl,
|
|
2045
|
+
asset: raw.asset,
|
|
2046
|
+
assetName: raw.assetName,
|
|
2047
|
+
assetVersion: raw.assetVersion,
|
|
2048
|
+
recipient: raw.recipient,
|
|
2049
|
+
realm: raw.realm,
|
|
2050
|
+
challengeSecret: raw.challengeSecret,
|
|
2051
|
+
...raw.challengeTtlSeconds !== void 0 ? {
|
|
2052
|
+
challengeTtlSeconds: toNumber(
|
|
2053
|
+
raw.challengeTtlSeconds,
|
|
2054
|
+
"payments.mpp.challengeTtlSeconds",
|
|
2055
|
+
{ min: 1 }
|
|
2056
|
+
)
|
|
2057
|
+
} : {},
|
|
2058
|
+
facilitator: normaliseFacilitator(raw.facilitator),
|
|
2059
|
+
...raw.allowMainnet !== void 0 ? { allowMainnet: toBoolean(raw.allowMainnet, "payments.mpp.allowMainnet") } : {},
|
|
2060
|
+
...raw.allowUnauthenticatedFacilitator !== void 0 ? {
|
|
2061
|
+
allowUnauthenticatedFacilitator: toBoolean(
|
|
2062
|
+
raw.allowUnauthenticatedFacilitator,
|
|
2063
|
+
"payments.mpp.allowUnauthenticatedFacilitator"
|
|
2064
|
+
)
|
|
2065
|
+
} : {}
|
|
2066
|
+
};
|
|
2067
|
+
validateAddress("payments.mpp.recipient", mpp.recipient);
|
|
2068
|
+
validateAddress("payments.mpp.asset", mpp.asset);
|
|
2069
|
+
if (/[\r\n]/.test(mpp.realm)) {
|
|
2070
|
+
throw new CommerceError("CONFIG_INVALID", "payments.mpp.realm must be a single line", {
|
|
2071
|
+
details: { path: "payments.mpp.realm" }
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
if (mpp.challengeSecret.length < MPP_MIN_CHALLENGE_SECRET_LENGTH) {
|
|
2075
|
+
throw new CommerceError(
|
|
2076
|
+
"CONFIG_INVALID",
|
|
2077
|
+
`payments.mpp.challengeSecret must have length at least ${MPP_MIN_CHALLENGE_SECRET_LENGTH}`,
|
|
2078
|
+
{ details: { path: "payments.mpp.challengeSecret" } }
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
resolveX402Deployment({
|
|
2082
|
+
network: mpp.network,
|
|
2083
|
+
payTo: mpp.recipient,
|
|
2084
|
+
asset: mpp.asset,
|
|
2085
|
+
assetName: mpp.assetName,
|
|
2086
|
+
assetVersion: mpp.assetVersion,
|
|
2087
|
+
facilitator: mpp.facilitator,
|
|
2088
|
+
...mpp.allowMainnet !== void 0 ? { allowMainnet: mpp.allowMainnet } : {},
|
|
2089
|
+
...mpp.allowUnauthenticatedFacilitator !== void 0 ? { allowUnauthenticatedFacilitator: mpp.allowUnauthenticatedFacilitator } : {},
|
|
2090
|
+
configPath: "payments.mpp",
|
|
2091
|
+
payToField: "recipient"
|
|
2092
|
+
});
|
|
2093
|
+
return mpp;
|
|
2094
|
+
}
|
|
2095
|
+
function normaliseResource(id, entry, protocols, x402, mpp, ap2) {
|
|
2006
2096
|
if (entry.input !== void 0) validateResourceSchemaKeywords(id, "input", entry.input);
|
|
2007
2097
|
const inputProperties = entry.input?.["properties"];
|
|
2008
2098
|
if (inputProperties && typeof inputProperties === "object") {
|
|
@@ -2078,7 +2168,7 @@ function normaliseResource(id, entry, protocols, x402, ap2) {
|
|
|
2078
2168
|
);
|
|
2079
2169
|
}
|
|
2080
2170
|
if (entry.pricing.type === "fixed") {
|
|
2081
|
-
validatePricingAmount(id, entry.pricing.amount, x402);
|
|
2171
|
+
validatePricingAmount(id, entry.pricing.amount, x402?.assetDecimals);
|
|
2082
2172
|
}
|
|
2083
2173
|
const paymentMethods = entry.payments ?? [];
|
|
2084
2174
|
if (entry.pricing.type === "fixed") {
|
|
@@ -2093,17 +2183,28 @@ function normaliseResource(id, entry, protocols, x402, ap2) {
|
|
|
2093
2183
|
if (!SUPPORTED_PAYMENT_METHODS.has(method)) {
|
|
2094
2184
|
throw new CommerceError(
|
|
2095
2185
|
"CONFIG_INVALID",
|
|
2096
|
-
`Resource "${id}" names unsupported payment method "${method}". Supported:
|
|
2186
|
+
`Resource "${id}" names unsupported payment method "${method}". Supported: ${PAYMENT_METHOD_NAMES.join(", ")}.`,
|
|
2097
2187
|
{ details: { path: `resources.${id}.payments`, resourceId: id, method } }
|
|
2098
2188
|
);
|
|
2099
2189
|
}
|
|
2100
|
-
|
|
2190
|
+
}
|
|
2191
|
+
const enabledRails = [...x402?.enabled ? ["x402"] : [], ...mpp?.enabled ? ["mpp"] : []];
|
|
2192
|
+
if (!paymentMethods.some((method) => enabledRails.includes(method))) {
|
|
2193
|
+
throw new CommerceError(
|
|
2194
|
+
"CONFIG_INVALID",
|
|
2195
|
+
`Resource "${id}" names payment method(s) "${paymentMethods.join(", ")}", none of which is configured and enabled under "payments"`,
|
|
2196
|
+
{ details: { path: `resources.${id}.payments`, resourceId: id, paymentMethods } }
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
if (mpp?.enabled && paymentMethods.includes("mpp")) {
|
|
2200
|
+
if (entry.pricing.currency !== MPP_PROFILE.assetSymbol) {
|
|
2101
2201
|
throw new CommerceError(
|
|
2102
2202
|
"CONFIG_INVALID",
|
|
2103
|
-
`Resource "${id}"
|
|
2104
|
-
{ details: { path: `resources.${id}.
|
|
2203
|
+
`Resource "${id}" is priced in ${entry.pricing.currency}, but MPP charges ${MPP_PROFILE.assetSymbol}`,
|
|
2204
|
+
{ details: { path: `resources.${id}.pricing.currency`, resourceId: id } }
|
|
2105
2205
|
);
|
|
2106
2206
|
}
|
|
2207
|
+
validatePricingAmount(id, entry.pricing.amount, MPP_PROFILE.assetDecimals);
|
|
2107
2208
|
}
|
|
2108
2209
|
}
|
|
2109
2210
|
const pricing = entry.pricing.type === "free" ? { type: "free" } : { type: "fixed", amount: entry.pricing.amount, currency: entry.pricing.currency };
|
|
@@ -2245,7 +2346,7 @@ function validateResourceSchemaKeywords(id, path2, schema) {
|
|
|
2245
2346
|
}
|
|
2246
2347
|
var PRICING_AMOUNT_PATTERN = /^\d+(?:\.\d+)?$/;
|
|
2247
2348
|
var ZERO_AMOUNT_PATTERN = /^0(?:\.0+)?$/;
|
|
2248
|
-
function validatePricingAmount(id, amount,
|
|
2349
|
+
function validatePricingAmount(id, amount, decimals) {
|
|
2249
2350
|
const path2 = `resources.${id}.pricing.amount`;
|
|
2250
2351
|
if (!PRICING_AMOUNT_PATTERN.test(amount)) {
|
|
2251
2352
|
throw new CommerceError(
|
|
@@ -2261,12 +2362,12 @@ function validatePricingAmount(id, amount, x402) {
|
|
|
2261
2362
|
{ details: { path: path2, resourceId: id } }
|
|
2262
2363
|
);
|
|
2263
2364
|
}
|
|
2264
|
-
if (
|
|
2365
|
+
if (decimals !== void 0) {
|
|
2265
2366
|
const fractionalDigits = amount.includes(".") ? amount.split(".")[1]?.length ?? 0 : 0;
|
|
2266
|
-
if (fractionalDigits >
|
|
2367
|
+
if (fractionalDigits > decimals) {
|
|
2267
2368
|
throw new CommerceError(
|
|
2268
2369
|
"CONFIG_INVALID",
|
|
2269
|
-
`Resource "${id}" has pricing.amount "${amount}" with more precision (${fractionalDigits} fractional digits) than the configured asset supports (${
|
|
2370
|
+
`Resource "${id}" has pricing.amount "${amount}" with more precision (${fractionalDigits} fractional digits) than the configured asset supports (${decimals} decimals)`,
|
|
2270
2371
|
{ details: { path: path2, resourceId: id } }
|
|
2271
2372
|
);
|
|
2272
2373
|
}
|
|
@@ -2525,6 +2626,7 @@ var SECRET_FIELD_NAMES = [
|
|
|
2525
2626
|
"seed",
|
|
2526
2627
|
"mnemonic",
|
|
2527
2628
|
"secret",
|
|
2629
|
+
"challengeSecret",
|
|
2528
2630
|
"apiKey",
|
|
2529
2631
|
"adminToken",
|
|
2530
2632
|
"token"
|
|
@@ -2590,7 +2692,7 @@ function wrapPino(instance) {
|
|
|
2590
2692
|
}
|
|
2591
2693
|
var CORS_METHODS = "GET,POST,DELETE,OPTIONS";
|
|
2592
2694
|
var CORS_HEADERS = "content-type,payment-signature,x-request-id,authorization";
|
|
2593
|
-
var CORS_EXPOSED_HEADERS = "payment-required,payment-response,x-request-id";
|
|
2695
|
+
var CORS_EXPOSED_HEADERS = "payment-required,payment-response,www-authenticate,payment-receipt,x-request-id";
|
|
2594
2696
|
function buildAccessControlHook(options) {
|
|
2595
2697
|
const allowedOrigins = new Set(options.allowedOrigins.map((origin) => origin.toLowerCase()));
|
|
2596
2698
|
const publicHostname = hostnameOf(options.publicBaseUrl);
|
|
@@ -3003,7 +3105,7 @@ async function buildWellKnownDocument(options) {
|
|
|
3003
3105
|
return { ...runtime.adapter.descriptor, health: publicHealth(health) };
|
|
3004
3106
|
})
|
|
3005
3107
|
);
|
|
3006
|
-
const x402 = options.config.payments
|
|
3108
|
+
const { x402, mpp } = options.config.payments;
|
|
3007
3109
|
return {
|
|
3008
3110
|
gateway: {
|
|
3009
3111
|
implementationVersion: PACKAGE_VERSION,
|
|
@@ -3039,6 +3141,22 @@ async function buildWellKnownDocument(options) {
|
|
|
3039
3141
|
x402.facilitator.mode
|
|
3040
3142
|
)
|
|
3041
3143
|
}
|
|
3144
|
+
} : {},
|
|
3145
|
+
...mpp !== void 0 ? {
|
|
3146
|
+
mpp: {
|
|
3147
|
+
enabled: mpp.enabled,
|
|
3148
|
+
network: mpp.network,
|
|
3149
|
+
asset: mpp.asset,
|
|
3150
|
+
assetName: mpp.assetName,
|
|
3151
|
+
assetVersion: mpp.assetVersion,
|
|
3152
|
+
assetDecimals: MPP_PROFILE.assetDecimals,
|
|
3153
|
+
recipient: mpp.recipient,
|
|
3154
|
+
facilitator: { mode: mpp.facilitator.mode },
|
|
3155
|
+
mode: resolveDeploymentMode(
|
|
3156
|
+
requireNetworkProfile(mpp.network, "payments.mpp.network"),
|
|
3157
|
+
mpp.facilitator.mode
|
|
3158
|
+
)
|
|
3159
|
+
}
|
|
3042
3160
|
} : {}
|
|
3043
3161
|
}
|
|
3044
3162
|
};
|
|
@@ -3160,9 +3278,8 @@ async function handleInvoke(request, reply, options) {
|
|
|
3160
3278
|
resourceId
|
|
3161
3279
|
});
|
|
3162
3280
|
}
|
|
3163
|
-
const paymentHeader = request.headers[PAYMENT_HEADER];
|
|
3164
|
-
const paymentValue = Array.isArray(paymentHeader) ? paymentHeader[0] : paymentHeader;
|
|
3165
3281
|
const paymentMethod = resource.paymentMethods[0];
|
|
3282
|
+
const paymentValue = paymentProof(request, paymentMethod);
|
|
3166
3283
|
const payment = paymentValue !== void 0 && paymentMethod !== void 0 ? { method: paymentMethod, payload: paymentValue } : void 0;
|
|
3167
3284
|
const authorization = parseAuthorizationHeader(
|
|
3168
3285
|
request.headers[AUTHORIZATION_HEADER],
|
|
@@ -3180,7 +3297,10 @@ async function handleInvoke(request, reply, options) {
|
|
|
3180
3297
|
const outcome = await options.pipeline.execute(canonicalRequest);
|
|
3181
3298
|
if (outcome.kind === "payment-required") {
|
|
3182
3299
|
const envelope = toPaymentRequiredEnvelope(outcome);
|
|
3183
|
-
if (envelope.payment.
|
|
3300
|
+
if (envelope.payment.provider === "mpp") {
|
|
3301
|
+
const challenge = envelope.payment.envelope?.["wwwAuthenticate"];
|
|
3302
|
+
if (typeof challenge === "string") reply.header("www-authenticate", challenge);
|
|
3303
|
+
} else if (envelope.payment.envelope !== void 0) {
|
|
3184
3304
|
reply.header(PAYMENT_REQUIRED_HEADER, encodeHeaderDocument(envelope.payment.envelope));
|
|
3185
3305
|
}
|
|
3186
3306
|
reply.header("cache-control", "no-store");
|
|
@@ -3189,6 +3309,10 @@ async function handleInvoke(request, reply, options) {
|
|
|
3189
3309
|
}
|
|
3190
3310
|
if (outcome.payment) {
|
|
3191
3311
|
reply.header(PAYMENT_RESPONSE_HEADER, encodePaymentSummary(outcome.payment));
|
|
3312
|
+
const receipt = outcome.payment.metadata?.["receipt"];
|
|
3313
|
+
if (outcome.payment.provider === "mpp" && typeof receipt === "string") {
|
|
3314
|
+
reply.header("payment-receipt", receipt);
|
|
3315
|
+
}
|
|
3192
3316
|
}
|
|
3193
3317
|
reply.status(outcome.backendStatus).send(outcome.body);
|
|
3194
3318
|
} catch (error) {
|
|
@@ -3196,15 +3320,31 @@ async function handleInvoke(request, reply, options) {
|
|
|
3196
3320
|
const settledPayment = errorPaymentSummary(commerceError);
|
|
3197
3321
|
if (settledPayment !== void 0) {
|
|
3198
3322
|
reply.header(PAYMENT_RESPONSE_HEADER, encodePaymentSummary(settledPayment));
|
|
3323
|
+
if (settledPayment.provider === "mpp" && settledPayment.receipt !== void 0) {
|
|
3324
|
+
reply.header("payment-receipt", settledPayment.receipt);
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
const challenge = commerceError.details?.["challenge"];
|
|
3328
|
+
if (commerceError.code === "PAYMENT_INVALID" && typeof challenge?.["wwwAuthenticate"] === "string") {
|
|
3329
|
+
reply.header("www-authenticate", challenge["wwwAuthenticate"]);
|
|
3330
|
+
reply.header("cache-control", "no-store");
|
|
3199
3331
|
}
|
|
3200
3332
|
reply.status(commerceError.httpStatus).send(toErrorEnvelope(commerceError));
|
|
3201
3333
|
}
|
|
3202
3334
|
}
|
|
3335
|
+
function paymentProof(request, method) {
|
|
3336
|
+
if (method === "mpp") {
|
|
3337
|
+
const value2 = request.headers.authorization;
|
|
3338
|
+
return value2 !== void 0 && /^Payment\s+\S/i.test(value2) ? value2 : void 0;
|
|
3339
|
+
}
|
|
3340
|
+
const value = request.headers[PAYMENT_HEADER];
|
|
3341
|
+
return Array.isArray(value) ? value[0] : value;
|
|
3342
|
+
}
|
|
3203
3343
|
function errorPaymentSummary(error) {
|
|
3204
3344
|
const payment = error.details?.["payment"];
|
|
3205
3345
|
if (typeof payment !== "object" || payment === null) return void 0;
|
|
3206
3346
|
const rec = payment;
|
|
3207
|
-
const { status, provider, amount, currency, network, externalReference } = rec;
|
|
3347
|
+
const { status, provider, amount, currency, network, externalReference, receipt } = rec;
|
|
3208
3348
|
if (typeof status !== "string" || typeof provider !== "string" || typeof amount !== "string" || typeof currency !== "string") {
|
|
3209
3349
|
return void 0;
|
|
3210
3350
|
}
|
|
@@ -3214,7 +3354,8 @@ function errorPaymentSummary(error) {
|
|
|
3214
3354
|
amount,
|
|
3215
3355
|
currency,
|
|
3216
3356
|
...typeof network === "string" ? { network } : {},
|
|
3217
|
-
...typeof externalReference === "string" ? { externalReference } : {}
|
|
3357
|
+
...typeof externalReference === "string" ? { externalReference } : {},
|
|
3358
|
+
...typeof receipt === "string" ? { receipt } : {}
|
|
3218
3359
|
};
|
|
3219
3360
|
}
|
|
3220
3361
|
function encodeHeaderDocument(document) {
|
|
@@ -3256,13 +3397,25 @@ function parseLimit(value) {
|
|
|
3256
3397
|
// src/gateway/server.ts
|
|
3257
3398
|
var REQUEST_ID_HEADER = "x-request-id";
|
|
3258
3399
|
var CLIENT_REQUEST_ID_PATTERN = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
3400
|
+
function withProviderBackedMethodsFirst(resource, providers) {
|
|
3401
|
+
const backed = (method) => providers.some((provider) => provider.name === method);
|
|
3402
|
+
const ordered = [
|
|
3403
|
+
...resource.paymentMethods.filter(backed),
|
|
3404
|
+
...resource.paymentMethods.filter((method) => !backed(method))
|
|
3405
|
+
];
|
|
3406
|
+
return ordered.some((method, i) => method !== resource.paymentMethods[i]) ? { ...resource, paymentMethods: ordered } : resource;
|
|
3407
|
+
}
|
|
3259
3408
|
async function createGateway(options) {
|
|
3260
3409
|
const clock = options.clock ?? systemClock;
|
|
3261
3410
|
const ids = options.ids ?? createDefaultIdGenerator();
|
|
3262
3411
|
const logger = options.logger ?? createGatewayLogger({ name: options.config.merchant.id }).core;
|
|
3263
3412
|
const backend = options.backend ?? new HttpBackendExecutor({ logger });
|
|
3264
3413
|
const authorizationProviders = options.authorizationProviders ?? [];
|
|
3265
|
-
const resources = createResourceRegistry(
|
|
3414
|
+
const resources = createResourceRegistry(
|
|
3415
|
+
options.config.resources.map(
|
|
3416
|
+
(resource) => withProviderBackedMethodsFirst(resource, options.paymentProviders)
|
|
3417
|
+
)
|
|
3418
|
+
);
|
|
3266
3419
|
const eventBus = createEventBus({ store: options.store, logger });
|
|
3267
3420
|
const pipeline = createExecutionPipeline({
|
|
3268
3421
|
resources,
|