@armory-sh/middleware-hono 0.3.28 → 0.3.29

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +22 -33
  2. package/dist/index.js +190 -325
  3. package/package.json +5 -3
package/dist/index.d.ts CHANGED
@@ -1,56 +1,45 @@
1
+ import { X402PaymentPayload, PaymentRequirementsV2 } from '@armory-sh/base';
1
2
  import { Context, Next } from 'hono';
2
- import { PaymentRequirementsV2, ValidationError, PaymentRequirements, X402PaymentPayload } from '@armory-sh/base';
3
- import { ExtensionConfig } from './extensions.js';
4
- export { buildExtensions, extractExtension } from './extensions.js';
3
+ export { ExtensionConfig, buildExtensions, extractExtension } from './extensions.js';
5
4
 
6
- /**
7
- * Simplified middleware API for easy x402 payment integration
8
- * Just specify payTo address and optional chains/tokens by name
9
- */
5
+ interface RouteAwarePaymentConfig extends PaymentConfig {
6
+ route?: string;
7
+ routes?: string[];
8
+ perRoute?: Record<string, Partial<PaymentConfig>>;
9
+ }
10
+ declare const routeAwarePaymentMiddleware: (config: RouteAwarePaymentConfig) => (c: Context, next: Next) => Promise<Response | undefined>;
10
11
 
11
12
  type NetworkId = string | number;
12
13
  type TokenId = string;
13
14
  interface PaymentConfig {
14
- payTo: string;
15
+ payTo?: string;
16
+ requirements?: PaymentRequirementsV2 | PaymentRequirementsV2[];
15
17
  chains?: NetworkId[];
16
18
  chain?: NetworkId;
17
19
  tokens?: TokenId[];
18
20
  token?: TokenId;
19
21
  amount?: string;
20
22
  maxTimeoutSeconds?: number;
21
- payToByChain?: Record<NetworkId, string>;
22
- payToByToken?: Record<NetworkId, Record<TokenId, string>>;
23
23
  facilitatorUrl?: string;
24
- facilitatorUrlByChain?: Record<NetworkId, string>;
25
- facilitatorUrlByToken?: Record<NetworkId, Record<TokenId, string>>;
26
- extensions?: ExtensionConfig;
24
+ facilitatorUrlByChain?: Record<string, string>;
25
+ facilitatorUrlByToken?: Record<string, Record<string, string>>;
27
26
  }
28
- interface ResolvedSimpleConfig {
27
+ interface ResolvedRequirementsConfig {
29
28
  requirements: PaymentRequirementsV2[];
30
- error?: ValidationError;
31
- }
32
- declare function createPaymentRequirements(config: PaymentConfig): ResolvedSimpleConfig;
33
- declare function paymentMiddleware(config: PaymentConfig): (c: Context, next: Next) => Promise<Response | void>;
34
-
35
- interface RouteAwarePaymentConfig extends PaymentConfig {
36
- route?: string;
37
- routes?: string[];
38
- perRoute?: Record<string, Partial<PaymentConfig>>;
39
- }
40
- declare const routeAwarePaymentMiddleware: (config: RouteAwarePaymentConfig) => (c: Context, next: Next) => Promise<Response | void>;
41
-
42
- interface AdvancedPaymentConfig {
43
- requirements: PaymentRequirements;
44
- facilitatorUrl: string;
45
- network?: string;
29
+ error?: {
30
+ code: string;
31
+ message: string;
32
+ };
46
33
  }
47
- interface AugmentedRequest extends Request {
34
+ interface AugmentedContext extends Context {
48
35
  payment?: {
49
36
  payload: X402PaymentPayload;
50
37
  payerAddress: string;
51
38
  verified: boolean;
52
39
  };
53
40
  }
54
- declare const advancedPaymentMiddleware: (config: AdvancedPaymentConfig) => (c: Context, next: Next) => Promise<Response | void>;
41
+ declare function resolveFacilitatorUrlFromRequirement(config: PaymentConfig, requirement: PaymentRequirementsV2): string | undefined;
42
+ declare function createPaymentRequirements(config: PaymentConfig): ResolvedRequirementsConfig;
43
+ declare function paymentMiddleware(config: PaymentConfig): (c: Context, next: Next) => Promise<Response | undefined>;
55
44
 
56
- export { type AdvancedPaymentConfig, type AugmentedRequest, ExtensionConfig, type PaymentConfig, type RouteAwarePaymentConfig, advancedPaymentMiddleware, createPaymentRequirements, paymentMiddleware, routeAwarePaymentMiddleware };
45
+ export { type AugmentedContext, type PaymentConfig, type ResolvedRequirementsConfig, type RouteAwarePaymentConfig, createPaymentRequirements, paymentMiddleware, resolveFacilitatorUrlFromRequirement, routeAwarePaymentMiddleware };
package/dist/index.js CHANGED
@@ -1,294 +1,6 @@
1
- import { buildExtensions } from './chunk-XYM6YXAQ.js';
2
1
  export { buildExtensions, extractExtension } from './chunk-XYM6YXAQ.js';
3
- import { V2_HEADERS, safeBase64Encode, decodePayloadHeader, verifyPayment, createPaymentRequiredHeaders, settlePayment, createSettlementHeaders, matchRoute, PAYMENT_SIGNATURE_HEADER, TOKENS, registerToken, resolveNetwork, resolveToken, validateRouteConfig } from '@armory-sh/base';
2
+ import { matchRoute, V2_HEADERS, safeBase64Encode, decodePayloadHeader, findRequirementByAccepted, verifyPayment, createPaymentRequiredHeaders, settlePayment, createSettlementHeaders, resolveNetwork, isValidationError, resolveToken, createPaymentRequirements as createPaymentRequirements$1, PAYMENT_SIGNATURE_HEADER, PAYMENT_REQUIRED_HEADER, validateRouteConfig, TOKENS, registerToken } from '@armory-sh/base';
4
3
 
5
- var isValidationError = (value) => {
6
- return typeof value === "object" && value !== null && "code" in value;
7
- };
8
- function ensureTokensRegistered() {
9
- for (const token of Object.values(TOKENS)) {
10
- try {
11
- registerToken(token);
12
- } catch {
13
- }
14
- }
15
- }
16
- function resolveChainTokenInputs(chains, tokens) {
17
- const resolvedNetworks = [];
18
- const resolvedTokens = [];
19
- const errors = [];
20
- const networkInputs = chains?.length ? chains : Object.keys({
21
- ethereum: 1,
22
- base: 8453,
23
- "base-sepolia": 84532,
24
- "skale-base": 1187947933,
25
- "skale-base-sepolia": 324705682,
26
- "ethereum-sepolia": 11155111
27
- });
28
- for (const networkId of networkInputs) {
29
- const resolved = resolveNetwork(networkId);
30
- if (isValidationError(resolved)) {
31
- errors.push(`Network "${networkId}": ${resolved.message}`);
32
- } else {
33
- resolvedNetworks.push(resolved);
34
- }
35
- }
36
- const tokenInputs = tokens?.length ? tokens : ["usdc"];
37
- for (const tokenId of tokenInputs) {
38
- let found = false;
39
- for (const network of resolvedNetworks) {
40
- const resolved = resolveToken(tokenId, network);
41
- if (isValidationError(resolved)) {
42
- continue;
43
- }
44
- resolvedTokens.push(resolved);
45
- found = true;
46
- break;
47
- }
48
- if (!found) {
49
- errors.push(`Token "${tokenId}" not found on any specified network`);
50
- }
51
- }
52
- if (errors.length > 0) {
53
- return {
54
- networks: resolvedNetworks,
55
- tokens: resolvedTokens,
56
- error: {
57
- code: "VALIDATION_FAILED",
58
- message: errors.join("; ")
59
- }
60
- };
61
- }
62
- return { networks: resolvedNetworks, tokens: resolvedTokens };
63
- }
64
- function toAtomicUnits(amount) {
65
- if (amount.includes(".")) {
66
- const [whole, fractional = ""] = amount.split(".");
67
- const paddedFractional = fractional.padEnd(6, "0").slice(0, 6);
68
- return `${whole}${paddedFractional}`.replace(/^0+/, "") || "0";
69
- }
70
- return `${amount}000000`;
71
- }
72
- function resolvePayTo(config, network, token) {
73
- const chainId = network.config.chainId;
74
- if (config.payToByToken) {
75
- for (const [chainKey, tokenMap] of Object.entries(config.payToByToken)) {
76
- const resolvedChain = resolveNetwork(chainKey);
77
- if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
78
- for (const [tokenKey, address] of Object.entries(tokenMap)) {
79
- const resolvedToken = resolveToken(tokenKey, network);
80
- if (!isValidationError(resolvedToken) && resolvedToken.config.contractAddress.toLowerCase() === token.config.contractAddress.toLowerCase()) {
81
- return address;
82
- }
83
- }
84
- }
85
- }
86
- }
87
- if (config.payToByChain) {
88
- for (const [chainKey, address] of Object.entries(config.payToByChain)) {
89
- const resolvedChain = resolveNetwork(chainKey);
90
- if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
91
- return address;
92
- }
93
- }
94
- }
95
- return config.payTo;
96
- }
97
- function resolveFacilitatorUrl(config, network, token) {
98
- const chainId = network.config.chainId;
99
- if (config.facilitatorUrlByToken) {
100
- for (const [chainKey, tokenMap] of Object.entries(config.facilitatorUrlByToken)) {
101
- const resolvedChain = resolveNetwork(chainKey);
102
- if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
103
- for (const [tokenKey, url] of Object.entries(tokenMap)) {
104
- const resolvedToken = resolveToken(tokenKey, network);
105
- if (!isValidationError(resolvedToken) && resolvedToken.config.contractAddress.toLowerCase() === token.config.contractAddress.toLowerCase()) {
106
- return url;
107
- }
108
- }
109
- }
110
- }
111
- }
112
- if (config.facilitatorUrlByChain) {
113
- for (const [chainKey, url] of Object.entries(config.facilitatorUrlByChain)) {
114
- const resolvedChain = resolveNetwork(chainKey);
115
- if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
116
- return url;
117
- }
118
- }
119
- }
120
- return config.facilitatorUrl;
121
- }
122
- function resolveFacilitatorUrlFromRequirement(config, requirement) {
123
- const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
124
- const assetAddress = requirement.asset.toLowerCase();
125
- if (config.facilitatorUrlByToken) {
126
- for (const [chainKey, tokenMap] of Object.entries(config.facilitatorUrlByToken)) {
127
- const resolvedChain = resolveNetwork(chainKey);
128
- if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
129
- for (const [, url] of Object.entries(tokenMap)) {
130
- const network = resolveNetwork(chainKey);
131
- if (!isValidationError(network)) {
132
- for (const tokenKey of Object.keys(tokenMap)) {
133
- const resolvedToken = resolveToken(tokenKey, network);
134
- if (!isValidationError(resolvedToken) && resolvedToken.config.contractAddress.toLowerCase() === assetAddress) {
135
- return url;
136
- }
137
- }
138
- }
139
- }
140
- }
141
- }
142
- }
143
- if (config.facilitatorUrlByChain) {
144
- for (const [chainKey, url] of Object.entries(config.facilitatorUrlByChain)) {
145
- const resolvedChain = resolveNetwork(chainKey);
146
- if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
147
- return url;
148
- }
149
- }
150
- }
151
- return config.facilitatorUrl;
152
- }
153
- function createPaymentRequirements(config) {
154
- ensureTokensRegistered();
155
- const {
156
- payTo,
157
- chains,
158
- chain,
159
- tokens,
160
- token,
161
- amount = "1.0",
162
- maxTimeoutSeconds = 300
163
- } = config;
164
- const chainInputs = chain ? [chain] : chains;
165
- const tokenInputs = token ? [token] : tokens;
166
- const { networks, tokens: resolvedTokens, error } = resolveChainTokenInputs(
167
- chainInputs,
168
- tokenInputs
169
- );
170
- if (error) {
171
- return { requirements: [], error };
172
- }
173
- const requirements = [];
174
- for (const network of networks) {
175
- for (const resolvedToken of resolvedTokens) {
176
- if (resolvedToken.network.config.chainId !== network.config.chainId) {
177
- continue;
178
- }
179
- const atomicAmount = toAtomicUnits(amount);
180
- const tokenConfig = resolvedToken.config;
181
- const resolvedPayTo = resolvePayTo(config, network, resolvedToken);
182
- resolveFacilitatorUrl(config, network, resolvedToken);
183
- requirements.push({
184
- scheme: "exact",
185
- network: network.caip2,
186
- amount: atomicAmount,
187
- asset: tokenConfig.contractAddress,
188
- payTo: resolvedPayTo,
189
- maxTimeoutSeconds,
190
- extra: {
191
- name: tokenConfig.name,
192
- version: tokenConfig.version
193
- }
194
- });
195
- }
196
- }
197
- return { requirements };
198
- }
199
- function paymentMiddleware(config) {
200
- const { requirements, error } = createPaymentRequirements(config);
201
- console.log("[payment-middleware] Initialized with requirements:", JSON.stringify(requirements, null, 2));
202
- return async (c, next) => {
203
- if (error) {
204
- console.log("[payment-middleware] Configuration error:", error.message);
205
- c.status(500);
206
- return c.json({
207
- error: "Payment middleware configuration error",
208
- details: error.message
209
- });
210
- }
211
- const paymentHeader = c.req.header(V2_HEADERS.PAYMENT_SIGNATURE);
212
- console.log("[payment-middleware] Payment header present:", !!paymentHeader);
213
- if (!paymentHeader) {
214
- const resource = {
215
- url: c.req.url,
216
- description: "API Access",
217
- mimeType: "application/json"
218
- };
219
- const paymentRequired = {
220
- x402Version: 2,
221
- error: "Payment required",
222
- resource,
223
- accepts: requirements,
224
- extensions: config.extensions ? buildExtensions(config.extensions) : void 0
225
- };
226
- console.log("[payment-middleware] Returning 402 with requirements:", JSON.stringify(paymentRequired.accepts, null, 2));
227
- c.status(402);
228
- c.header(V2_HEADERS.PAYMENT_REQUIRED, safeBase64Encode(JSON.stringify(paymentRequired)));
229
- return c.json({
230
- error: "Payment required",
231
- accepts: requirements
232
- });
233
- }
234
- const primaryRequirement = requirements[0];
235
- if (!primaryRequirement) {
236
- console.log("[payment-middleware] No requirements configured");
237
- c.status(500);
238
- return c.json({ error: "Payment middleware configuration error", details: "No payment requirements configured" });
239
- }
240
- console.log("[payment-middleware] Primary requirement:", JSON.stringify(primaryRequirement, null, 2));
241
- let parsedPayload;
242
- try {
243
- parsedPayload = decodePayloadHeader(paymentHeader, {
244
- accepted: primaryRequirement
245
- });
246
- } catch {
247
- c.status(400);
248
- return c.json({ error: "Invalid payment payload" });
249
- }
250
- const facilitatorUrl = resolveFacilitatorUrlFromRequirement(config, primaryRequirement);
251
- console.log("[payment-middleware] Facilitator URL:", facilitatorUrl);
252
- if (!facilitatorUrl) {
253
- console.log("[payment-middleware] No facilitator URL configured");
254
- c.status(500);
255
- return c.json({ error: "Payment middleware configuration error", message: "Facilitator URL is required for verification" });
256
- }
257
- console.log("[payment-middleware] Verifying payment with facilitator...");
258
- const verifyResult = await verifyPayment(parsedPayload, primaryRequirement, { url: facilitatorUrl });
259
- console.log("[payment-middleware] Verify result:", JSON.stringify(verifyResult, null, 2));
260
- if (!verifyResult.isValid) {
261
- console.log("[payment-middleware] Verification failed:", verifyResult.invalidReason);
262
- c.status(402);
263
- c.header(V2_HEADERS.PAYMENT_REQUIRED, createPaymentRequiredHeaders(requirements)[V2_HEADERS.PAYMENT_REQUIRED]);
264
- return c.json({ error: "Payment verification failed", message: verifyResult.invalidReason });
265
- }
266
- console.log("[payment-middleware] Payment verified, setting payment context");
267
- c.set("payment", {
268
- payload: parsedPayload,
269
- payerAddress: verifyResult.payer,
270
- verified: true
271
- });
272
- await next();
273
- if (c.res.status >= 400) {
274
- return;
275
- }
276
- console.log("[payment-middleware] Settling payment...");
277
- const settleResult = await settlePayment(parsedPayload, primaryRequirement, { url: facilitatorUrl });
278
- console.log("[payment-middleware] Settle result:", JSON.stringify(settleResult, null, 2));
279
- if (!settleResult.success) {
280
- console.log("[payment-middleware] Settlement failed:", settleResult.errorReason);
281
- c.status(502);
282
- c.res = c.json({ error: "Settlement failed", details: settleResult.errorReason }, 502);
283
- return;
284
- }
285
- console.log("[payment-middleware] Payment settled, tx:", settleResult.transaction);
286
- const headers = createSettlementHeaders(settleResult);
287
- for (const [key, value] of Object.entries(headers)) {
288
- c.header(key, value);
289
- }
290
- };
291
- }
292
4
  var resolveRouteConfig = (config) => {
293
5
  const validationError = validateRouteConfig(config);
294
6
  if (validationError) {
@@ -323,7 +35,8 @@ var routeAwarePaymentMiddleware = (config) => {
323
35
  const path = new URL(c.req.url).pathname;
324
36
  const matchedRoute = routes.find((r) => matchRoute(r.pattern, path));
325
37
  if (!matchedRoute) {
326
- return next();
38
+ await next();
39
+ return;
327
40
  }
328
41
  const { requirements, error: requirementsError } = createPaymentRequirements(matchedRoute.config);
329
42
  if (requirementsError) {
@@ -359,7 +72,10 @@ var routeAwarePaymentMiddleware = (config) => {
359
72
  const primaryRequirement = requirements[0];
360
73
  if (!primaryRequirement) {
361
74
  c.status(500);
362
- return c.json({ error: "Payment middleware configuration error", details: "No payment requirements configured" });
75
+ return c.json({
76
+ error: "Payment middleware configuration error",
77
+ details: "No payment requirements configured"
78
+ });
363
79
  }
364
80
  let parsedPayload;
365
81
  try {
@@ -370,16 +86,43 @@ var routeAwarePaymentMiddleware = (config) => {
370
86
  c.status(400);
371
87
  return c.json({ error: "Invalid payment payload" });
372
88
  }
373
- const facilitatorUrl = matchedRoute.config.facilitatorUrl ?? resolveFacilitatorUrlFromRequirement(matchedRoute.config, primaryRequirement);
89
+ const selectedRequirement = findRequirementByAccepted(
90
+ requirements,
91
+ parsedPayload.accepted
92
+ );
93
+ if (!selectedRequirement) {
94
+ c.status(400);
95
+ return c.json({
96
+ error: "Invalid payment payload",
97
+ message: "Accepted requirement is not configured for this route"
98
+ });
99
+ }
100
+ const facilitatorUrl = matchedRoute.config.facilitatorUrl ?? resolveFacilitatorUrlFromRequirement(
101
+ matchedRoute.config,
102
+ selectedRequirement
103
+ );
374
104
  if (!facilitatorUrl) {
375
105
  c.status(500);
376
- return c.json({ error: "Payment middleware configuration error", message: "Facilitator URL is required for verification" });
106
+ return c.json({
107
+ error: "Payment middleware configuration error",
108
+ message: "Facilitator URL is required for verification"
109
+ });
377
110
  }
378
- const verifyResult = await verifyPayment(parsedPayload, primaryRequirement, { url: facilitatorUrl });
111
+ const verifyResult = await verifyPayment(
112
+ parsedPayload,
113
+ selectedRequirement,
114
+ { url: facilitatorUrl }
115
+ );
379
116
  if (!verifyResult.isValid) {
380
117
  c.status(402);
381
- c.header(V2_HEADERS.PAYMENT_REQUIRED, createPaymentRequiredHeaders(requirements)[V2_HEADERS.PAYMENT_REQUIRED]);
382
- return c.json({ error: "Payment verification failed", message: verifyResult.invalidReason });
118
+ c.header(
119
+ V2_HEADERS.PAYMENT_REQUIRED,
120
+ createPaymentRequiredHeaders(requirements)[V2_HEADERS.PAYMENT_REQUIRED]
121
+ );
122
+ return c.json({
123
+ error: "Payment verification failed",
124
+ message: verifyResult.invalidReason
125
+ });
383
126
  }
384
127
  c.set("payment", {
385
128
  payload: parsedPayload,
@@ -391,10 +134,17 @@ var routeAwarePaymentMiddleware = (config) => {
391
134
  if (c.res.status >= 400) {
392
135
  return;
393
136
  }
394
- const settleResult = await settlePayment(parsedPayload, primaryRequirement, { url: facilitatorUrl });
137
+ const settleResult = await settlePayment(
138
+ parsedPayload,
139
+ selectedRequirement,
140
+ { url: facilitatorUrl }
141
+ );
395
142
  if (!settleResult.success) {
396
143
  c.status(502);
397
- c.res = c.json({ error: "Settlement failed", details: settleResult.errorReason }, 502);
144
+ c.res = c.json(
145
+ { error: "Settlement failed", details: settleResult.errorReason },
146
+ 502
147
+ );
398
148
  return;
399
149
  }
400
150
  const headers = createSettlementHeaders(settleResult);
@@ -405,67 +155,182 @@ var routeAwarePaymentMiddleware = (config) => {
405
155
  };
406
156
 
407
157
  // src/index.ts
408
- var advancedPaymentMiddleware = (config) => {
409
- const { requirements, facilitatorUrl } = config;
158
+ function ensureTokensRegistered() {
159
+ for (const token of Object.values(TOKENS)) {
160
+ try {
161
+ registerToken(token);
162
+ } catch {
163
+ }
164
+ }
165
+ }
166
+ function resolveFacilitatorUrlFromRequirement(config, requirement) {
167
+ const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
168
+ const assetAddress = requirement.asset.toLowerCase();
169
+ if (config.facilitatorUrlByToken) {
170
+ for (const [chainKey, tokenMap] of Object.entries(
171
+ config.facilitatorUrlByToken
172
+ )) {
173
+ const resolvedChain = resolveNetwork(chainKey);
174
+ if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
175
+ for (const [, url] of Object.entries(tokenMap)) {
176
+ const network = resolveNetwork(chainKey);
177
+ if (!isValidationError(network)) {
178
+ for (const tokenKey of Object.keys(tokenMap)) {
179
+ const resolvedToken = resolveToken(tokenKey, network);
180
+ if (!isValidationError(resolvedToken) && resolvedToken.config.contractAddress.toLowerCase() === assetAddress) {
181
+ return url;
182
+ }
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+ if (config.facilitatorUrlByChain) {
190
+ for (const [chainKey, url] of Object.entries(
191
+ config.facilitatorUrlByChain
192
+ )) {
193
+ const resolvedChain = resolveNetwork(chainKey);
194
+ if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
195
+ return url;
196
+ }
197
+ }
198
+ }
199
+ return config.facilitatorUrl;
200
+ }
201
+ function createPaymentRequirements(config) {
202
+ if (config.requirements) {
203
+ return {
204
+ requirements: Array.isArray(config.requirements) ? config.requirements : [config.requirements]
205
+ };
206
+ }
207
+ if (!config.payTo) {
208
+ return {
209
+ requirements: [],
210
+ error: {
211
+ code: "VALIDATION_FAILED",
212
+ message: "Missing payment configuration: provide payTo or explicit requirements"
213
+ }
214
+ };
215
+ }
216
+ ensureTokensRegistered();
217
+ return createPaymentRequirements$1(config);
218
+ }
219
+ function paymentMiddleware(config) {
220
+ const { requirements, error } = createPaymentRequirements(config);
410
221
  return async (c, next) => {
222
+ if (error) {
223
+ c.status(500);
224
+ return c.json({
225
+ error: "Payment middleware configuration error",
226
+ details: error.message
227
+ });
228
+ }
229
+ const primaryRequirement = requirements[0];
230
+ if (!primaryRequirement) {
231
+ c.status(500);
232
+ return c.json({
233
+ error: "Payment middleware configuration error",
234
+ details: "No payment requirements configured"
235
+ });
236
+ }
411
237
  const paymentHeader = c.req.header(PAYMENT_SIGNATURE_HEADER);
412
238
  if (!paymentHeader) {
413
- const requiredHeaders = createPaymentRequiredHeaders(requirements);
239
+ const resource = {
240
+ url: c.req.url,
241
+ description: "API Access",
242
+ mimeType: "application/json"
243
+ };
244
+ const paymentRequired = {
245
+ x402Version: 2,
246
+ error: "Payment required",
247
+ resource,
248
+ accepts: requirements
249
+ };
414
250
  c.status(402);
415
- for (const [key, value] of Object.entries(requiredHeaders)) {
416
- c.header(key, value);
417
- }
251
+ c.header(
252
+ PAYMENT_REQUIRED_HEADER,
253
+ safeBase64Encode(JSON.stringify(paymentRequired))
254
+ );
418
255
  return c.json({
419
256
  error: "Payment required",
420
- accepts: [requirements]
257
+ accepts: requirements
421
258
  });
422
259
  }
423
- let paymentPayload;
260
+ let parsedPayload;
424
261
  try {
425
- paymentPayload = decodePayloadHeader(paymentHeader, {
426
- accepted: requirements
262
+ parsedPayload = decodePayloadHeader(paymentHeader, {
263
+ accepted: primaryRequirement
427
264
  });
428
- } catch (e) {
265
+ } catch {
429
266
  c.status(400);
430
- return c.json({ error: "Invalid payment payload", details: String(e) });
267
+ return c.json({ error: "Invalid payment payload" });
431
268
  }
269
+ const selectedRequirement = findRequirementByAccepted(
270
+ requirements,
271
+ parsedPayload.accepted
272
+ );
273
+ if (!selectedRequirement) {
274
+ c.status(400);
275
+ return c.json({
276
+ error: "Invalid payment payload",
277
+ message: "Accepted requirement is not configured for this endpoint"
278
+ });
279
+ }
280
+ const facilitatorUrl = resolveFacilitatorUrlFromRequirement(
281
+ config,
282
+ selectedRequirement
283
+ );
432
284
  if (!facilitatorUrl) {
433
285
  c.status(500);
434
- return c.json({ error: "Payment middleware configuration error", message: "Facilitator URL is required for verification" });
286
+ return c.json({
287
+ error: "Payment middleware configuration error",
288
+ message: "Facilitator URL is required for verification"
289
+ });
435
290
  }
436
- const verifyResult = await verifyPayment(paymentPayload, requirements, { url: facilitatorUrl });
291
+ const verifyResult = await verifyPayment(
292
+ parsedPayload,
293
+ selectedRequirement,
294
+ { url: facilitatorUrl }
295
+ );
437
296
  if (!verifyResult.isValid) {
438
- const requiredHeaders = createPaymentRequiredHeaders(requirements);
439
297
  c.status(402);
440
- for (const [key, value] of Object.entries(requiredHeaders)) {
441
- c.header(key, value);
442
- }
298
+ c.header(
299
+ PAYMENT_REQUIRED_HEADER,
300
+ createPaymentRequiredHeaders(requirements)[PAYMENT_REQUIRED_HEADER]
301
+ );
443
302
  return c.json({
444
303
  error: "Payment verification failed",
445
304
  message: verifyResult.invalidReason
446
305
  });
447
306
  }
448
- const payerAddress = verifyResult.payer ?? paymentPayload.payload.authorization.from;
449
307
  c.set("payment", {
450
- payload: paymentPayload,
451
- payerAddress,
308
+ payload: parsedPayload,
309
+ payerAddress: verifyResult.payer,
452
310
  verified: true
453
311
  });
454
312
  await next();
455
313
  if (c.res.status >= 400) {
456
314
  return;
457
315
  }
458
- const settleResult = await settlePayment(paymentPayload, requirements, { url: facilitatorUrl });
316
+ const settleResult = await settlePayment(
317
+ parsedPayload,
318
+ selectedRequirement,
319
+ { url: facilitatorUrl }
320
+ );
459
321
  if (!settleResult.success) {
460
322
  c.status(502);
461
- c.res = c.json({ error: "Settlement failed", details: settleResult.errorReason }, 502);
323
+ c.res = c.json(
324
+ { error: "Settlement failed", details: settleResult.errorReason },
325
+ 502
326
+ );
462
327
  return;
463
328
  }
464
- const settlementHeaders = createSettlementHeaders(settleResult);
465
- for (const [key, value] of Object.entries(settlementHeaders)) {
329
+ const headers = createSettlementHeaders(settleResult);
330
+ for (const [key, value] of Object.entries(headers)) {
466
331
  c.header(key, value);
467
332
  }
468
333
  };
469
- };
334
+ }
470
335
 
471
- export { advancedPaymentMiddleware, createPaymentRequirements, paymentMiddleware, routeAwarePaymentMiddleware };
336
+ export { createPaymentRequirements, paymentMiddleware, resolveFacilitatorUrlFromRequirement, routeAwarePaymentMiddleware };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armory-sh/middleware-hono",
3
- "version": "0.3.28",
3
+ "version": "0.3.29",
4
4
  "license": "MIT",
5
5
  "author": "Sawyer Cutler <sawyer@dirtroad.dev>",
6
6
  "keywords": [
@@ -52,8 +52,8 @@
52
52
  "hono": "^4"
53
53
  },
54
54
  "dependencies": {
55
- "@armory-sh/base": "0.2.28",
56
- "@armory-sh/extensions": "0.1.9"
55
+ "@armory-sh/base": "0.2.29",
56
+ "@armory-sh/extensions": "0.1.10"
57
57
  },
58
58
  "devDependencies": {
59
59
  "bun-types": "latest",
@@ -64,6 +64,8 @@
64
64
  "scripts": {
65
65
  "build": "rm -rf dist && tsup",
66
66
  "build:dts": "rm -rf dist && tsc --declaration --skipLibCheck",
67
+ "lint": "bun run build",
68
+ "format": "bun run lint",
67
69
  "test": "bun test"
68
70
  }
69
71
  }