@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.
@@ -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-CKq3d_3K.js';
3
- export { f as AuthorizationMethodName, g as BackendMethod, h as DEFAULT_BACKEND_TIMEOUT_MS, J as JsonSchema, N as NOOP_LOGGER, i as PROTOCOL_NAMES, j as PaymentChallenge, k as PaymentContext, l as PaymentRequirement, m as PaymentResult, n as PaymentSettlementContext, o as PaymentVerificationContext, p as Pricing, q as ProtocolName, s as systemClock } from './runtime-CKq3d_3K.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-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-CPDcpJVa.js';
6
- export { c as AuthorizationFinalizeContext, d as AuthorizationRecord, e as AuthorizationVerification, f as AuthorizationVerificationContext } from './authorization-CPDcpJVa.js';
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-Bebmggf_.js';
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-Bebmggf_.js';
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
- * The payment method comes from the resource's own declaration. This is not
282
- * the place that decides which rail a resource uses, so a proof for a
283
- * resource with no configured rail is dropped rather than forwarded under an
284
- * invented method. The pipeline then treats the request as unpaid and decides
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-GF6KHHKL.js';
2
- import { resolveX402Deployment, resolveDeploymentMode, requireNetworkProfile } from './chunk-NWIQLJNU.js';
3
- import { RESERVED_INPUT_FIELDS, PROTOCOL_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-ZEGFLRGV.js';
4
- 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_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-ZEGFLRGV.js';
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 = /* @__PURE__ */ new Set(["x402"]);
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 facilitator = x402Raw === void 0 ? void 0 : x402Raw.facilitator.mode === "local" ? { mode: "local", signerPrivateKey: x402Raw.facilitator.signerPrivateKey } : {
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 normaliseResource(id, entry, protocols, x402, ap2) {
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: x402.`,
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
- if (method === "x402" && (!x402 || !x402.enabled)) {
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}" names payment method "x402" which is not configured or not enabled under payments.x402`,
2104
- { details: { path: `resources.${id}.payments`, resourceId: id, method } }
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, x402) {
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 (x402 !== void 0) {
2365
+ if (decimals !== void 0) {
2265
2366
  const fractionalDigits = amount.includes(".") ? amount.split(".")[1]?.length ?? 0 : 0;
2266
- if (fractionalDigits > x402.assetDecimals) {
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 (${x402.assetDecimals} decimals)`,
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.x402;
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.envelope !== void 0) {
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(options.config.resources);
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,