@armory-sh/middleware-hono 0.3.17 → 0.3.19
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/dist/index.d.ts +27 -4
- package/dist/index.js +178 -14
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
import { Context, Next } from 'hono';
|
|
2
|
-
import {
|
|
2
|
+
import { PaymentRequirementsV2, ValidationError, PaymentRequirements, PaymentPayloadV2 } from '@armory-sh/base';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Simplified middleware API for easy x402 payment integration
|
|
6
|
+
* Just specify payTo address and optional chains/tokens by name
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
type NetworkId = string | number;
|
|
10
|
+
type TokenId = string;
|
|
11
|
+
interface PaymentConfig {
|
|
12
|
+
payTo: string;
|
|
13
|
+
chains?: NetworkId[];
|
|
14
|
+
chain?: NetworkId;
|
|
15
|
+
tokens?: TokenId[];
|
|
16
|
+
token?: TokenId;
|
|
17
|
+
amount?: string;
|
|
18
|
+
maxTimeoutSeconds?: number;
|
|
19
|
+
}
|
|
20
|
+
interface ResolvedSimpleConfig {
|
|
21
|
+
requirements: PaymentRequirementsV2[];
|
|
22
|
+
error?: ValidationError;
|
|
23
|
+
}
|
|
24
|
+
declare function createPaymentRequirements(config: PaymentConfig): ResolvedSimpleConfig;
|
|
25
|
+
declare function paymentMiddleware(config: PaymentConfig): (c: Context, next: Next) => Promise<Response | void>;
|
|
26
|
+
|
|
27
|
+
interface AdvancedPaymentConfig {
|
|
5
28
|
requirements: PaymentRequirements;
|
|
6
29
|
facilitatorUrl?: string;
|
|
7
30
|
skipVerification?: boolean;
|
|
@@ -14,6 +37,6 @@ interface AugmentedRequest extends Request {
|
|
|
14
37
|
verified: boolean;
|
|
15
38
|
};
|
|
16
39
|
}
|
|
17
|
-
declare const
|
|
40
|
+
declare const advancedPaymentMiddleware: (config: AdvancedPaymentConfig) => (c: Context, next: Next) => Promise<Response | void>;
|
|
18
41
|
|
|
19
|
-
export { type AugmentedRequest, type
|
|
42
|
+
export { type AdvancedPaymentConfig, type AugmentedRequest, type PaymentConfig, advancedPaymentMiddleware, createPaymentRequirements, paymentMiddleware };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,185 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import {
|
|
3
3
|
createPaymentRequiredHeaders,
|
|
4
|
-
createSettlementHeaders,
|
|
5
4
|
PAYMENT_SIGNATURE_HEADER,
|
|
6
5
|
decodePaymentV2
|
|
7
6
|
} from "@armory-sh/base";
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
// src/simple.ts
|
|
9
|
+
import {
|
|
10
|
+
resolveNetwork,
|
|
11
|
+
resolveToken,
|
|
12
|
+
safeBase64Encode,
|
|
13
|
+
V2_HEADERS,
|
|
14
|
+
registerToken
|
|
15
|
+
} from "@armory-sh/base";
|
|
16
|
+
import {
|
|
17
|
+
TOKENS
|
|
18
|
+
} from "@armory-sh/base";
|
|
19
|
+
var isValidationError = (value) => {
|
|
20
|
+
return typeof value === "object" && value !== null && "code" in value;
|
|
21
|
+
};
|
|
22
|
+
function ensureTokensRegistered() {
|
|
23
|
+
for (const token of Object.values(TOKENS)) {
|
|
24
|
+
try {
|
|
25
|
+
registerToken(token);
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function resolveChainTokenInputs(chains, tokens) {
|
|
31
|
+
const resolvedNetworks = [];
|
|
32
|
+
const resolvedTokens = [];
|
|
33
|
+
const errors = [];
|
|
34
|
+
const networkInputs = chains?.length ? chains : Object.keys({
|
|
35
|
+
ethereum: 1,
|
|
36
|
+
base: 8453,
|
|
37
|
+
"base-sepolia": 84532,
|
|
38
|
+
"skale-base": 1187947933,
|
|
39
|
+
"skale-base-sepolia": 324705682,
|
|
40
|
+
"ethereum-sepolia": 11155111
|
|
41
|
+
});
|
|
42
|
+
for (const networkId of networkInputs) {
|
|
43
|
+
const resolved = resolveNetwork(networkId);
|
|
44
|
+
if (isValidationError(resolved)) {
|
|
45
|
+
errors.push(`Network "${networkId}": ${resolved.message}`);
|
|
46
|
+
} else {
|
|
47
|
+
resolvedNetworks.push(resolved);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const tokenInputs = tokens?.length ? tokens : ["usdc"];
|
|
51
|
+
for (const tokenId of tokenInputs) {
|
|
52
|
+
let found = false;
|
|
53
|
+
for (const network of resolvedNetworks) {
|
|
54
|
+
const resolved = resolveToken(tokenId, network);
|
|
55
|
+
if (isValidationError(resolved)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
resolvedTokens.push(resolved);
|
|
59
|
+
found = true;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
if (!found) {
|
|
63
|
+
errors.push(`Token "${tokenId}" not found on any specified network`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (errors.length > 0) {
|
|
67
|
+
return {
|
|
68
|
+
networks: resolvedNetworks,
|
|
69
|
+
tokens: resolvedTokens,
|
|
70
|
+
error: {
|
|
71
|
+
code: "VALIDATION_FAILED",
|
|
72
|
+
message: errors.join("; ")
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return { networks: resolvedNetworks, tokens: resolvedTokens };
|
|
77
|
+
}
|
|
78
|
+
function toAtomicUnits(amount) {
|
|
79
|
+
if (amount.includes(".")) {
|
|
80
|
+
const [whole, fractional = ""] = amount.split(".");
|
|
81
|
+
const paddedFractional = fractional.padEnd(6, "0").slice(0, 6);
|
|
82
|
+
return `${whole}${paddedFractional}`.replace(/^0+/, "") || "0";
|
|
83
|
+
}
|
|
84
|
+
return `${amount}000000`;
|
|
85
|
+
}
|
|
86
|
+
function createPaymentRequirements(config) {
|
|
87
|
+
ensureTokensRegistered();
|
|
88
|
+
const {
|
|
89
|
+
payTo,
|
|
90
|
+
chains,
|
|
91
|
+
chain,
|
|
92
|
+
tokens,
|
|
93
|
+
token,
|
|
94
|
+
amount = "1.0",
|
|
95
|
+
maxTimeoutSeconds = 300
|
|
96
|
+
} = config;
|
|
97
|
+
const chainInputs = chain ? [chain] : chains;
|
|
98
|
+
const tokenInputs = token ? [token] : tokens;
|
|
99
|
+
const { networks, tokens: resolvedTokens, error } = resolveChainTokenInputs(
|
|
100
|
+
chainInputs,
|
|
101
|
+
tokenInputs
|
|
102
|
+
);
|
|
103
|
+
if (error) {
|
|
104
|
+
return { requirements: [], error };
|
|
105
|
+
}
|
|
106
|
+
const requirements = [];
|
|
107
|
+
for (const network of networks) {
|
|
108
|
+
for (const resolvedToken of resolvedTokens) {
|
|
109
|
+
if (resolvedToken.network.config.chainId !== network.config.chainId) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const atomicAmount = toAtomicUnits(amount);
|
|
113
|
+
const tokenConfig = resolvedToken.config;
|
|
114
|
+
requirements.push({
|
|
115
|
+
scheme: "exact",
|
|
116
|
+
network: network.caip2,
|
|
117
|
+
amount: atomicAmount,
|
|
118
|
+
asset: tokenConfig.contractAddress,
|
|
119
|
+
payTo,
|
|
120
|
+
maxTimeoutSeconds,
|
|
121
|
+
extra: {
|
|
122
|
+
name: tokenConfig.name,
|
|
123
|
+
version: tokenConfig.version
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { requirements };
|
|
129
|
+
}
|
|
130
|
+
function paymentMiddleware(config) {
|
|
131
|
+
const { requirements, error } = createPaymentRequirements(config);
|
|
132
|
+
return async (c, next) => {
|
|
133
|
+
if (error) {
|
|
134
|
+
c.status(500);
|
|
135
|
+
return c.json({
|
|
136
|
+
error: "Payment middleware configuration error",
|
|
137
|
+
details: error.message
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
const paymentHeader = c.req.header(V2_HEADERS.PAYMENT_SIGNATURE);
|
|
141
|
+
if (!paymentHeader) {
|
|
142
|
+
const resource = {
|
|
143
|
+
url: c.req.url,
|
|
144
|
+
description: "API Access",
|
|
145
|
+
mimeType: "application/json"
|
|
146
|
+
};
|
|
147
|
+
const paymentRequired = {
|
|
148
|
+
x402Version: 2,
|
|
149
|
+
error: "Payment required",
|
|
150
|
+
resource,
|
|
151
|
+
accepts: requirements
|
|
152
|
+
};
|
|
153
|
+
c.status(402);
|
|
154
|
+
c.header(V2_HEADERS.PAYMENT_REQUIRED, safeBase64Encode(JSON.stringify(paymentRequired)));
|
|
155
|
+
return c.json({
|
|
156
|
+
error: "Payment required",
|
|
157
|
+
accepts: requirements
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const payload = paymentHeader;
|
|
161
|
+
let parsedPayload;
|
|
162
|
+
try {
|
|
163
|
+
const decoded = atob(payload);
|
|
164
|
+
parsedPayload = JSON.parse(decoded);
|
|
165
|
+
} catch {
|
|
166
|
+
try {
|
|
167
|
+
parsedPayload = JSON.parse(payload);
|
|
168
|
+
} catch {
|
|
169
|
+
c.status(400);
|
|
170
|
+
return c.json({ error: "Invalid payment payload" });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
c.set("payment", {
|
|
174
|
+
payload: parsedPayload,
|
|
175
|
+
verified: false
|
|
176
|
+
});
|
|
177
|
+
return next();
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/index.ts
|
|
182
|
+
var advancedPaymentMiddleware = (config) => {
|
|
9
183
|
const { requirements, facilitatorUrl, skipVerification = false, network = "base" } = config;
|
|
10
184
|
return async (c, next) => {
|
|
11
185
|
const paymentHeader = c.req.header(PAYMENT_SIGNATURE_HEADER);
|
|
@@ -22,11 +196,8 @@ var paymentMiddleware = (config) => {
|
|
|
22
196
|
}
|
|
23
197
|
let paymentPayload = null;
|
|
24
198
|
try {
|
|
25
|
-
console.log("[x402 Debug] Raw payment header:", paymentHeader);
|
|
26
199
|
paymentPayload = decodePaymentV2(paymentHeader);
|
|
27
|
-
console.log("[x402 Debug] Decoded payload:", JSON.stringify(paymentPayload, null, 2));
|
|
28
200
|
} catch (e) {
|
|
29
|
-
console.error("[x402 Debug] Decode error:", e);
|
|
30
201
|
c.status(400);
|
|
31
202
|
return c.json({ error: "Invalid payment payload", details: String(e) });
|
|
32
203
|
}
|
|
@@ -35,15 +206,6 @@ var paymentMiddleware = (config) => {
|
|
|
35
206
|
return c.json({ error: "Invalid payment payload" });
|
|
36
207
|
}
|
|
37
208
|
const payerAddress = paymentPayload.payload.authorization.from;
|
|
38
|
-
const settlement = {
|
|
39
|
-
success: true,
|
|
40
|
-
transaction: "",
|
|
41
|
-
network
|
|
42
|
-
};
|
|
43
|
-
const settlementHeaders = createSettlementHeaders(settlement);
|
|
44
|
-
for (const [key, value] of Object.entries(settlementHeaders)) {
|
|
45
|
-
c.header(key, value);
|
|
46
|
-
}
|
|
47
209
|
c.set("payment", {
|
|
48
210
|
payload: paymentPayload,
|
|
49
211
|
payerAddress,
|
|
@@ -53,5 +215,7 @@ var paymentMiddleware = (config) => {
|
|
|
53
215
|
};
|
|
54
216
|
};
|
|
55
217
|
export {
|
|
218
|
+
advancedPaymentMiddleware,
|
|
219
|
+
createPaymentRequirements,
|
|
56
220
|
paymentMiddleware
|
|
57
221
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@armory-sh/middleware-hono",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.19",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Sawyer Cutler <sawyer@dirtroad.dev>",
|
|
6
6
|
"type": "module",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"hono": "^4"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@armory-sh/base": "0.2.
|
|
31
|
+
"@armory-sh/base": "0.2.19"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"bun-types": "latest",
|