@devlab.group/agent-commerce 0.1.0-alpha.0 → 0.2.0-beta.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,25 @@
1
+ /**
2
+ * How the gateway authenticates to a remote facilitator.
3
+ *
4
+ * Deliberately generic: x402 facilitators are not a Coinbase-only category,
5
+ * and a scheme that only fits one vendor's credentials would make the
6
+ * abstraction a fiction. `bearer` covers every facilitator that takes a static
7
+ * token; a facilitator needing per-request signed credentials (CDP's JWT among
8
+ * them) is not supported yet and is refused rather than silently sent nothing.
9
+ */
10
+ type FacilitatorAuth = {
11
+ readonly type: 'none';
12
+ } | {
13
+ readonly type: 'bearer';
14
+ readonly token: string;
15
+ };
16
+ type X402FacilitatorConfig = {
17
+ readonly mode: 'local';
18
+ readonly signerPrivateKey: string;
19
+ } | {
20
+ readonly mode: 'remote';
21
+ readonly url: string;
22
+ readonly auth: FacilitatorAuth;
23
+ };
24
+
25
+ export type { FacilitatorAuth as F, X402FacilitatorConfig as X };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { I as IsoTimestamp, D as DecimalAmount, P as PaymentMethodName, B as BackendHandler, A as AdapterDescriptor, a as AdapterHealth, C as CommerceResource, R as ResourceRegistry, b as PaymentProvider, L as Logger, c as Clock, d as IdGenerator } from './runtime-LCk2TzGN.js';
2
- export { e as BackendMethod, f as DEFAULT_BACKEND_TIMEOUT_MS, J as JsonSchema, N as NOOP_LOGGER, g as PaymentChallenge, h as PaymentContext, i as PaymentRequirement, j as PaymentResult, k as PaymentSettlementContext, l as PaymentSubmission, m as PaymentVerificationContext, n as Pricing, o as ProtocolName, s as systemClock } from './runtime-LCk2TzGN.js';
3
- import { D as DeliveredOutcome, P as PaymentRequiredOutcome, C as CommerceEvent, a as PaymentAttempt, b as CommerceReceipt, E as ExecutionPipeline, c as ProtocolAdapter } from './protocol-adapter-C0goMgLK.js';
4
- export { d as COMMERCE_EVENT_TYPES, e as CanonicalRequest, f as CommerceEventType, g as EventSink, h as ExecutionOutcome, H as HttpProtocolAdapter, i as ProtocolAdapterContext, j as isHttpProtocolAdapter } from './protocol-adapter-C0goMgLK.js';
1
+ import { I as IsoTimestamp, D as DecimalAmount, P as PaymentMethodName, B as BackendHandler, A as AdapterDescriptor, a as AdapterHealth, C as CommerceResource, R as ResourceRegistry, b as PaymentProvider, L as Logger, c as Clock, d as IdGenerator } from './runtime-Bie-9FAM.js';
2
+ export { e as BackendMethod, f as DEFAULT_BACKEND_TIMEOUT_MS, J as JsonSchema, N as NOOP_LOGGER, g as PaymentChallenge, h as PaymentContext, i as PaymentRequirement, j as PaymentResult, k as PaymentSettlementContext, l as PaymentSubmission, m as PaymentVerificationContext, n as Pricing, o as ProtocolName, s as systemClock } from './runtime-Bie-9FAM.js';
3
+ import { X as X402FacilitatorConfig } from './guardrails-Di_yI_DW.js';
4
+ import { D as DeliveredOutcome, P as PaymentRequiredOutcome, C as CommerceEvent, a as PaymentAttempt, b as CommerceReceipt, E as ExecutionPipeline, c as ProtocolAdapter } from './protocol-adapter-CtB0sOnV.js';
5
+ export { d as COMMERCE_EVENT_TYPES, e as CanonicalRequest, f as CommerceEventType, g as EventSink, h as ExecutionOutcome, H as HttpProtocolAdapter, i as ProtocolAdapterContext, j as isHttpProtocolAdapter } from './protocol-adapter-CtB0sOnV.js';
5
6
  import { FastifyInstance } from 'fastify';
6
7
  import 'node:http';
7
8
 
@@ -93,13 +94,29 @@ declare function toCommerceError(value: unknown, fallbackCode?: CommerceErrorCod
93
94
 
94
95
  /**
95
96
  * Reserved input property carrying a payment proof on protocols that have no
96
- * header channel (MCP tool calls). Over HTTP the `X-PAYMENT` header is used.
97
+ * header channel (MCP tool calls). Over HTTP the `PAYMENT-SIGNATURE` header is
98
+ * used.
97
99
  */
98
100
  declare const PAYMENT_INPUT_FIELD = "_payment";
99
- /** HTTP header carrying the payment proof. */
100
- declare const PAYMENT_HEADER = "x-payment";
101
- /** HTTP response header carrying the settlement reference. */
102
- declare const PAYMENT_RESPONSE_HEADER = "x-payment-response";
101
+ /**
102
+ * HTTP request header carrying the payment proof.
103
+ *
104
+ * x402 v2 header names, lowercased because that is how Node presents incoming
105
+ * headers. v1's `X-PAYMENT` / `X-PAYMENT-RESPONSE` pair is not accepted: this
106
+ * gateway speaks one protocol version, and quietly honouring both would mean
107
+ * two verification paths for the same money.
108
+ */
109
+ declare const PAYMENT_HEADER = "payment-signature";
110
+ /** HTTP response header carrying the settlement result. */
111
+ declare const PAYMENT_RESPONSE_HEADER = "payment-response";
112
+ /**
113
+ * HTTP response header carrying the base64 payment challenge on a 402.
114
+ *
115
+ * The 402 body still carries {@link PaymentRequiredEnvelope} — richer, and the
116
+ * only channel MCP has — but an x402 v2 client reads the challenge from this
117
+ * header and ignores the body, so both are sent.
118
+ */
119
+ declare const PAYMENT_REQUIRED_HEADER = "payment-required";
103
120
  /**
104
121
  * Key under which a protocol adapter attaches a {@link DeliverySummary} to its
105
122
  * result metadata — MCP's `CallToolResult._meta`, and anywhere else a protocol
@@ -130,6 +147,12 @@ interface PaymentRequiredEnvelope {
130
147
  readonly expiresAt?: IsoTimestamp;
131
148
  /** Provider-native requirement objects, passed through verbatim. */
132
149
  readonly accepts: readonly Readonly<Record<string, unknown>>[];
150
+ /**
151
+ * The provider's own challenge document, verbatim — see
152
+ * {@link PaymentChallenge.envelope}. Present for providers that have one;
153
+ * a buyer's protocol client can hand this straight to its SDK.
154
+ */
155
+ readonly envelope?: Readonly<Record<string, unknown>>;
133
156
  };
134
157
  }
135
158
  interface ErrorEnvelope {
@@ -150,7 +173,7 @@ interface ErrorEnvelope {
150
173
  * `/api/receipts` is an operator route behind `server.adminToken`.
151
174
  *
152
175
  * Before this existed the two surfaces disagreed: HTTP callers received a
153
- * settlement summary in `X-PAYMENT-RESPONSE` while MCP callers received
176
+ * settlement summary in the payment-response header while MCP callers received
154
177
  * nothing, so an MCP buyer's only route to their own receipt was the merchant's
155
178
  * ledger. Both surfaces now emit this same shape.
156
179
  */
@@ -353,13 +376,8 @@ interface GatewayConfig {
353
376
  readonly assetDecimals: number;
354
377
  readonly payTo: string;
355
378
  readonly maxTimeoutSeconds: number;
356
- readonly facilitator: {
357
- readonly mode: 'local';
358
- readonly signerPrivateKey: string;
359
- } | {
360
- readonly mode: 'remote';
361
- readonly url: string;
362
- };
379
+ readonly facilitator: X402FacilitatorConfig;
380
+ readonly allowMainnet?: boolean;
363
381
  };
364
382
  };
365
383
  }
@@ -409,4 +427,4 @@ interface SqliteReceiptStoreOptions {
409
427
  }
410
428
  declare function createSqliteReceiptStore(options: SqliteReceiptStoreOptions): ReceiptStore;
411
429
 
412
- export { AdapterDescriptor, AdapterHealth, type BackendExecutor, BackendHandler, type BackendRequest, type BackendResponse, COMMERCE_ERROR_CODES, COMMERCE_ERROR_HTTP_STATUS, Clock, CommerceError, type CommerceErrorCode, type CommerceErrorInfo, type CommerceErrorOptions, CommerceEvent, CommerceReceipt, CommerceResource, DELIVERY_SUMMARY_META_KEY, DecimalAmount, DeliveredOutcome, type DeliverySummary, type ErrorEnvelope, ExecutionPipeline, type GatewayConfig, type GatewayInstance, type GatewayOptions, IdGenerator, IsoTimestamp, type ListOptions, Logger, PAYMENT_HEADER, PAYMENT_INPUT_FIELD, PAYMENT_RESPONSE_HEADER, PaymentAttempt, type PaymentAttemptReservation, type PaymentAttemptUpdate, PaymentMethodName, PaymentProvider, type PaymentRequiredEnvelope, PaymentRequiredOutcome, ProtocolAdapter, RETRYABLE_ERROR_CODES, type ReceiptStore, ResourceRegistry, createGateway, createSqliteReceiptStore, isCommerceError, isPaymentRequiredEnvelope, loadConfig, parseConfig, createSqliteReceiptStore as receipts, toCommerceError, toDeliverySummary, toErrorEnvelope, toPaymentRequiredEnvelope };
430
+ export { AdapterDescriptor, AdapterHealth, type BackendExecutor, BackendHandler, type BackendRequest, type BackendResponse, COMMERCE_ERROR_CODES, COMMERCE_ERROR_HTTP_STATUS, Clock, CommerceError, type CommerceErrorCode, type CommerceErrorInfo, type CommerceErrorOptions, CommerceEvent, CommerceReceipt, CommerceResource, DELIVERY_SUMMARY_META_KEY, DecimalAmount, DeliveredOutcome, type DeliverySummary, type ErrorEnvelope, ExecutionPipeline, type GatewayConfig, type GatewayInstance, type GatewayOptions, IdGenerator, IsoTimestamp, type ListOptions, Logger, PAYMENT_HEADER, PAYMENT_INPUT_FIELD, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, PaymentAttempt, type PaymentAttemptReservation, type PaymentAttemptUpdate, PaymentMethodName, PaymentProvider, type PaymentRequiredEnvelope, PaymentRequiredOutcome, ProtocolAdapter, RETRYABLE_ERROR_CODES, type ReceiptStore, ResourceRegistry, createGateway, createSqliteReceiptStore, isCommerceError, isPaymentRequiredEnvelope, loadConfig, parseConfig, createSqliteReceiptStore as receipts, toCommerceError, toDeliverySummary, toErrorEnvelope, toPaymentRequiredEnvelope };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { CommerceError, PAYMENT_INPUT_FIELD, systemClock, NOOP_LOGGER, PACKAGE_VERSION, toCommerceError, DEFAULT_BACKEND_TIMEOUT_MS, toErrorEnvelope, isHttpProtocolAdapter, isCommerceError, PAYMENT_HEADER, toPaymentRequiredEnvelope, PAYMENT_RESPONSE_HEADER } from './chunk-CZ6ZDAYD.js';
2
- export { COMMERCE_ERROR_CODES, COMMERCE_ERROR_HTTP_STATUS, COMMERCE_EVENT_TYPES, CommerceError, DEFAULT_BACKEND_TIMEOUT_MS, DELIVERY_SUMMARY_META_KEY, NOOP_LOGGER, PAYMENT_HEADER, PAYMENT_INPUT_FIELD, PAYMENT_RESPONSE_HEADER, RETRYABLE_ERROR_CODES, isCommerceError, isHttpProtocolAdapter, isPaymentRequiredEnvelope, systemClock, toCommerceError, toDeliverySummary, toErrorEnvelope, toPaymentRequiredEnvelope } from './chunk-CZ6ZDAYD.js';
1
+ import { resolveX402Deployment, resolveDeploymentMode, requireNetworkProfile } from './chunk-MQMKQGAB.js';
2
+ import { CommerceError, PAYMENT_INPUT_FIELD, systemClock, NOOP_LOGGER, PACKAGE_VERSION, toCommerceError, DEFAULT_BACKEND_TIMEOUT_MS, toErrorEnvelope, isHttpProtocolAdapter, isCommerceError, PAYMENT_HEADER, toPaymentRequiredEnvelope, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER } from './chunk-ASK67X56.js';
3
+ export { COMMERCE_ERROR_CODES, COMMERCE_ERROR_HTTP_STATUS, COMMERCE_EVENT_TYPES, CommerceError, DEFAULT_BACKEND_TIMEOUT_MS, DELIVERY_SUMMARY_META_KEY, NOOP_LOGGER, PAYMENT_HEADER, PAYMENT_INPUT_FIELD, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, RETRYABLE_ERROR_CODES, isCommerceError, isHttpProtocolAdapter, isPaymentRequiredEnvelope, systemClock, toCommerceError, toDeliverySummary, toErrorEnvelope, toPaymentRequiredEnvelope } from './chunk-ASK67X56.js';
3
4
  import { promises, existsSync, mkdirSync, accessSync, constants, chmodSync } from 'fs';
4
5
  import * as path from 'path';
5
6
  import { dirname } from 'path';
@@ -952,6 +953,7 @@ function createExecutionPipeline(options) {
952
953
  provider: paymentResult.provider,
953
954
  amount: paymentResult.amount,
954
955
  currency: paymentResult.currency,
956
+ ...paymentResult.network !== void 0 ? { network: paymentResult.network } : {},
955
957
  ...paymentResult.externalReference !== void 0 ? { externalReference: paymentResult.externalReference } : {}
956
958
  }
957
959
  }
@@ -1148,6 +1150,10 @@ var ResourceEntrySchema = z.object({
1148
1150
  payments: z.array(z.string().min(1)).optional()
1149
1151
  }).strict();
1150
1152
  var ResourcesMapSchema = z.record(z.string().min(1), ResourceEntrySchema);
1153
+ var FacilitatorAuthSchema = z.discriminatedUnion("type", [
1154
+ z.object({ type: z.literal("none") }).strict(),
1155
+ z.object({ type: z.literal("bearer"), token: z.string().min(1) }).strict()
1156
+ ]);
1151
1157
  var FacilitatorSchema = z.discriminatedUnion("mode", [
1152
1158
  z.object({
1153
1159
  mode: z.literal("local"),
@@ -1155,7 +1161,12 @@ var FacilitatorSchema = z.discriminatedUnion("mode", [
1155
1161
  }).strict(),
1156
1162
  z.object({
1157
1163
  mode: z.literal("remote"),
1158
- url: z.string().min(1)
1164
+ url: z.string().min(1),
1165
+ // Absent means "this facilitator takes no credential" — an explicit
1166
+ // statement, normalised to `{ type: 'none' }` below. On a mainnet that
1167
+ // combination is refused outright, so the default can never quietly
1168
+ // become an unauthenticated production facilitator.
1169
+ auth: FacilitatorAuthSchema.optional()
1159
1170
  }).strict()
1160
1171
  ]);
1161
1172
  var X402Schema = z.object({
@@ -1168,7 +1179,9 @@ var X402Schema = z.object({
1168
1179
  assetDecimals: NumberOrString,
1169
1180
  payTo: z.string().min(1),
1170
1181
  maxTimeoutSeconds: NumberOrString,
1171
- facilitator: FacilitatorSchema
1182
+ facilitator: FacilitatorSchema,
1183
+ /** Real funds. Never defaulted — see src/payments/x402/guardrails.ts. */
1184
+ allowMainnet: BooleanOrString.optional()
1172
1185
  }).strict();
1173
1186
  var PaymentsSchema = z.object({
1174
1187
  x402: X402Schema.optional()
@@ -1290,14 +1303,12 @@ function normalise(raw) {
1290
1303
  }
1291
1304
  };
1292
1305
  const x402Raw = raw.payments.x402;
1293
- if (x402Raw?.facilitator.mode === "remote") {
1294
- throw new CommerceError(
1295
- "CONFIG_INVALID",
1296
- 'payments.x402.facilitator.mode "remote" is not supported in v0.1.0-alpha; use "local" (the deterministic dev-chain facilitator)',
1297
- { details: { path: "payments.x402.facilitator.mode" } }
1298
- );
1299
- }
1300
- const x402 = x402Raw ? {
1306
+ const facilitator = x402Raw === void 0 ? void 0 : x402Raw.facilitator.mode === "local" ? { mode: "local", signerPrivateKey: x402Raw.facilitator.signerPrivateKey } : {
1307
+ mode: "remote",
1308
+ url: x402Raw.facilitator.url,
1309
+ auth: x402Raw.facilitator.auth ?? { type: "none" }
1310
+ };
1311
+ const x402 = x402Raw && facilitator ? {
1301
1312
  enabled: toBoolean(x402Raw.enabled, "payments.x402.enabled"),
1302
1313
  network: x402Raw.network,
1303
1314
  rpcUrl: x402Raw.rpcUrl,
@@ -1309,18 +1320,26 @@ function normalise(raw) {
1309
1320
  max: 36
1310
1321
  }),
1311
1322
  payTo: x402Raw.payTo,
1312
- maxTimeoutSeconds: toNumber(x402Raw.maxTimeoutSeconds, "payments.x402.maxTimeoutSeconds", {
1313
- min: 1
1314
- }),
1315
- // Only "local" can reach here — "remote" was rejected above.
1316
- facilitator: {
1317
- mode: "local",
1318
- signerPrivateKey: x402Raw.facilitator.signerPrivateKey
1319
- }
1323
+ maxTimeoutSeconds: toNumber(
1324
+ x402Raw.maxTimeoutSeconds,
1325
+ "payments.x402.maxTimeoutSeconds",
1326
+ {
1327
+ min: 1
1328
+ }
1329
+ ),
1330
+ facilitator,
1331
+ ...x402Raw.allowMainnet !== void 0 ? { allowMainnet: toBoolean(x402Raw.allowMainnet, "payments.x402.allowMainnet") } : {}
1320
1332
  } : void 0;
1321
1333
  if (x402) {
1322
1334
  validateAddress("payments.x402.payTo", x402.payTo);
1323
1335
  validateAddress("payments.x402.asset", x402.asset);
1336
+ resolveX402Deployment({
1337
+ network: x402.network,
1338
+ payTo: x402.payTo,
1339
+ asset: x402.asset,
1340
+ facilitator: x402.facilitator,
1341
+ ...x402.allowMainnet !== void 0 ? { allowMainnet: x402.allowMainnet } : {}
1342
+ });
1324
1343
  }
1325
1344
  const resources = Object.entries(raw.resources).map(
1326
1345
  ([id, entry]) => normaliseResource(id, entry, protocols, x402)
@@ -1365,7 +1384,7 @@ function normaliseResource(id, entry, protocols, x402) {
1365
1384
  }
1366
1385
  for (const protocol of entry.expose) {
1367
1386
  if (!SUPPORTED_PROTOCOLS.has(protocol)) {
1368
- const hint = protocol === "ucp" ? " (UCP is planned, not supported in v0.1.0-alpha)" : "";
1387
+ const hint = protocol === "ucp" ? " (UCP is planned, not supported in this release)" : "";
1369
1388
  throw new CommerceError(
1370
1389
  "CONFIG_INVALID",
1371
1390
  `Resource "${id}" exposes unsupported protocol "${protocol}"${hint}. Supported: http, mcp.`,
@@ -1401,7 +1420,7 @@ function normaliseResource(id, entry, protocols, x402) {
1401
1420
  if (entry.pricing.type === "dynamic") {
1402
1421
  throw new CommerceError(
1403
1422
  "CONFIG_INVALID",
1404
- `Resource "${id}" uses pricing.type "dynamic", which is not supported in v0.1.0-alpha`,
1423
+ `Resource "${id}" uses pricing.type "dynamic", which is not supported in this release`,
1405
1424
  { details: { path: `resources.${id}.pricing.type`, resourceId: id } }
1406
1425
  );
1407
1426
  }
@@ -1755,7 +1774,7 @@ var SECRET_FIELD_NAMES = [
1755
1774
  ];
1756
1775
  var REDACT_PATHS = [
1757
1776
  "req.headers.authorization",
1758
- 'req.headers["x-payment"]',
1777
+ 'req.headers["payment-signature"]',
1759
1778
  ...SECRET_FIELD_NAMES,
1760
1779
  ...SECRET_FIELD_NAMES.map((name) => `*.${name}`)
1761
1780
  ];
@@ -1813,7 +1832,8 @@ function wrapPino(instance) {
1813
1832
  return logger;
1814
1833
  }
1815
1834
  var CORS_METHODS = "GET,POST,DELETE,OPTIONS";
1816
- var CORS_HEADERS = "content-type,x-payment,x-request-id,authorization";
1835
+ var CORS_HEADERS = "content-type,payment-signature,x-request-id,authorization";
1836
+ var CORS_EXPOSED_HEADERS = "payment-required,payment-response,x-request-id";
1817
1837
  function buildAccessControlHook(options) {
1818
1838
  const allowedOrigins = new Set(options.allowedOrigins.map((origin) => origin.toLowerCase()));
1819
1839
  const publicHostname = hostnameOf(options.publicBaseUrl);
@@ -1832,6 +1852,7 @@ function buildAccessControlHook(options) {
1832
1852
  reply.header("access-control-allow-origin", origin);
1833
1853
  reply.header("access-control-allow-methods", CORS_METHODS);
1834
1854
  reply.header("access-control-allow-headers", CORS_HEADERS);
1855
+ reply.header("access-control-expose-headers", CORS_EXPOSED_HEADERS);
1835
1856
  reply.header("vary", "Origin");
1836
1857
  if (request.method === "OPTIONS") {
1837
1858
  reply.status(204).send();
@@ -2142,7 +2163,7 @@ function createReadinessProbe(options, ttlMs = READINESS_TTL_MS) {
2142
2163
  }
2143
2164
 
2144
2165
  // src/gateway/well-known.ts
2145
- var GATEWAY_SUPPORTED_SPEC = "agent-commerce/v0.1.0-alpha";
2166
+ var GATEWAY_SUPPORTED_SPEC = "agent-commerce/v0.2.0-beta";
2146
2167
  async function buildWellKnownDocument(options) {
2147
2168
  const adapters = await Promise.all(
2148
2169
  options.adapterRuntimes.map(async (runtime) => ({
@@ -2172,9 +2193,18 @@ async function buildWellKnownDocument(options) {
2172
2193
  assetDecimals: x402.assetDecimals,
2173
2194
  payTo: x402.payTo,
2174
2195
  maxTimeoutSeconds: x402.maxTimeoutSeconds,
2175
- // Never the signerPrivateKey — only whether the facilitator is
2176
- // local (dev-chain only) or remote, plus its (non-secret) URL.
2177
- facilitator: x402.facilitator.mode === "local" ? { mode: "local" } : { mode: "remote", url: x402.facilitator.url }
2196
+ // Never the signerPrivateKey, and never the facilitator URL:
2197
+ // a facilitator endpoint is a per-deployment operational detail
2198
+ // that can carry a tenant path or an API key, exactly like
2199
+ // `rpcUrl` above. Only whether it is local or remote is public.
2200
+ facilitator: { mode: x402.facilitator.mode },
2201
+ // Chain id 84532 is shared between the local dev chain and
2202
+ // public Base Sepolia, so the network id alone cannot say which
2203
+ // one a client is talking to. This can.
2204
+ mode: resolveDeploymentMode(
2205
+ requireNetworkProfile(x402.network, "payments.x402.network"),
2206
+ x402.facilitator.mode
2207
+ )
2178
2208
  }
2179
2209
  } : {}
2180
2210
  }
@@ -2309,7 +2339,12 @@ async function handleInvoke(request, reply, options) {
2309
2339
  };
2310
2340
  const outcome = await options.pipeline.execute(canonicalRequest);
2311
2341
  if (outcome.kind === "payment-required") {
2312
- reply.status(402).send(toPaymentRequiredEnvelope(outcome));
2342
+ const envelope = toPaymentRequiredEnvelope(outcome);
2343
+ if (envelope.payment.envelope !== void 0) {
2344
+ reply.header(PAYMENT_REQUIRED_HEADER, encodeHeaderDocument(envelope.payment.envelope));
2345
+ }
2346
+ reply.header("cache-control", "no-store");
2347
+ reply.status(402).send(envelope);
2313
2348
  return;
2314
2349
  }
2315
2350
  if (outcome.payment) {
@@ -2329,7 +2364,7 @@ function errorPaymentSummary(error) {
2329
2364
  const payment = error.details?.["payment"];
2330
2365
  if (typeof payment !== "object" || payment === null) return void 0;
2331
2366
  const rec = payment;
2332
- const { status, provider, amount, currency, externalReference } = rec;
2367
+ const { status, provider, amount, currency, network, externalReference } = rec;
2333
2368
  if (typeof status !== "string" || typeof provider !== "string" || typeof amount !== "string" || typeof currency !== "string") {
2334
2369
  return void 0;
2335
2370
  }
@@ -2338,18 +2373,24 @@ function errorPaymentSummary(error) {
2338
2373
  provider,
2339
2374
  amount,
2340
2375
  currency,
2376
+ ...typeof network === "string" ? { network } : {},
2341
2377
  ...typeof externalReference === "string" ? { externalReference } : {}
2342
2378
  };
2343
2379
  }
2380
+ function encodeHeaderDocument(document) {
2381
+ return Buffer.from(JSON.stringify(document), "utf8").toString("base64");
2382
+ }
2344
2383
  function encodePaymentSummary(payment) {
2345
- const summary = {
2384
+ return encodeHeaderDocument({
2385
+ success: payment.status === "settled",
2386
+ transaction: payment.externalReference ?? "",
2387
+ network: payment.network ?? "",
2346
2388
  status: payment.status,
2347
2389
  provider: payment.provider,
2348
2390
  amount: payment.amount,
2349
2391
  currency: payment.currency,
2350
2392
  ...payment.externalReference !== void 0 ? { externalReference: payment.externalReference } : {}
2351
- };
2352
- return Buffer.from(JSON.stringify(summary), "utf8").toString("base64");
2393
+ });
2353
2394
  }
2354
2395
  function parseLimit(value) {
2355
2396
  if (Array.isArray(value)) return parseLimit(value[0]);