@armory-sh/middleware-elysia 0.3.2

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,48 @@
1
+ import { Context } from 'elysia';
2
+ import { VerifyPaymentOptions } from '@armory-sh/facilitator';
3
+ import { X402PaymentPayload, X402PaymentRequirements } from '@armory-sh/base';
4
+
5
+ interface LegacyPaymentPayloadV1 {
6
+ amount: string;
7
+ network: string;
8
+ contractAddress: string;
9
+ payTo: string;
10
+ from: string;
11
+ expiry: number;
12
+ signature: string;
13
+ }
14
+ interface LegacyPaymentPayloadV2 {
15
+ to: string;
16
+ from: string;
17
+ amount: string;
18
+ chainId: string;
19
+ assetId: string;
20
+ nonce: string;
21
+ expiry: number;
22
+ signature: string;
23
+ }
24
+ type AnyPaymentPayload = X402PaymentPayload | LegacyPaymentPayloadV1 | LegacyPaymentPayloadV2;
25
+ type PaymentPayload = AnyPaymentPayload;
26
+
27
+ interface PaymentMiddlewareConfig {
28
+ requirements: X402PaymentRequirements;
29
+ facilitatorUrl?: string;
30
+ verifyOptions?: VerifyPaymentOptions;
31
+ skipVerification?: boolean;
32
+ }
33
+ interface PaymentInfo {
34
+ payload: PaymentPayload;
35
+ payerAddress: string;
36
+ version: 1 | 2;
37
+ verified: boolean;
38
+ }
39
+ interface PaymentContext {
40
+ payment: PaymentInfo;
41
+ }
42
+ declare const paymentMiddleware: (config: PaymentMiddlewareConfig) => {
43
+ beforeHandle: (context: Context & {
44
+ store: Record<string, unknown>;
45
+ }) => Promise<unknown>;
46
+ };
47
+
48
+ export { type PaymentContext, type PaymentInfo, type PaymentMiddlewareConfig, paymentMiddleware };
package/dist/index.js ADDED
@@ -0,0 +1,164 @@
1
+ // src/payment-utils.ts
2
+ import { extractPaymentFromHeaders, X402_HEADERS } from "@armory-sh/base";
3
+ import { verifyX402Payment as verifyPayment } from "@armory-sh/facilitator";
4
+ var getHeadersForVersion = (version) => version === 1 ? { payment: "X-PAYMENT", required: "X-PAYMENT-REQUIRED", response: "X-PAYMENT-RESPONSE" } : { payment: "PAYMENT-SIGNATURE", required: "PAYMENT-REQUIRED", response: "PAYMENT-RESPONSE" };
5
+ var getRequirementsVersion = (requirements) => "contractAddress" in requirements && "network" in requirements ? 1 : 2;
6
+ var encodeRequirements = (requirements) => JSON.stringify(requirements);
7
+ function isLegacyV1(payload) {
8
+ return typeof payload === "object" && payload !== null && "contractAddress" in payload && "network" in payload && "signature" in payload && typeof payload.signature === "string";
9
+ }
10
+ function isLegacyV2(payload) {
11
+ return typeof payload === "object" && payload !== null && "chainId" in payload && "assetId" in payload && "signature" in payload && typeof payload.signature === "string";
12
+ }
13
+ var decodePayload = (headerValue) => {
14
+ let parsed;
15
+ try {
16
+ if (headerValue.startsWith("{")) {
17
+ parsed = JSON.parse(headerValue);
18
+ } else {
19
+ parsed = JSON.parse(atob(headerValue));
20
+ }
21
+ } catch {
22
+ throw new Error("Invalid payment payload");
23
+ }
24
+ const headers = new Headers();
25
+ headers.set(X402_HEADERS.PAYMENT, headerValue);
26
+ const x402Payload = extractPaymentFromHeaders(headers);
27
+ if (x402Payload) {
28
+ return { payload: x402Payload, version: 2 };
29
+ }
30
+ if (isLegacyV1(parsed)) {
31
+ return { payload: parsed, version: 1 };
32
+ }
33
+ if (isLegacyV2(parsed)) {
34
+ return { payload: parsed, version: 2 };
35
+ }
36
+ throw new Error("Unrecognized payment payload format");
37
+ };
38
+ var verifyWithFacilitator = async (facilitatorUrl, payload, requirements, verifyOptions) => {
39
+ try {
40
+ const response = await fetch(`${facilitatorUrl}/verify`, {
41
+ method: "POST",
42
+ headers: { "Content-Type": "application/json" },
43
+ body: JSON.stringify({ payload, requirements, options: verifyOptions })
44
+ });
45
+ if (!response.ok) {
46
+ const error = await response.json();
47
+ return { success: false, error: JSON.stringify(error) };
48
+ }
49
+ const result = await response.json();
50
+ if (!result.success) {
51
+ return { success: false, error: result.error ?? "Verification failed" };
52
+ }
53
+ return { success: true, payerAddress: result.payerAddress ?? "" };
54
+ } catch (error) {
55
+ return {
56
+ success: false,
57
+ error: error instanceof Error ? error.message : "Unknown facilitator error"
58
+ };
59
+ }
60
+ };
61
+ var verifyLocally = async (payload, requirements, verifyOptions) => {
62
+ if (isLegacyV1(payload) || isLegacyV2(payload)) {
63
+ return {
64
+ success: false,
65
+ error: "Local verification not supported for legacy payload formats. Use a facilitator."
66
+ };
67
+ }
68
+ const result = await verifyPayment(payload, requirements, verifyOptions);
69
+ if (!result.success) {
70
+ return {
71
+ success: false,
72
+ error: JSON.stringify({
73
+ error: "Payment verification failed",
74
+ reason: result.error.name,
75
+ message: result.error.message
76
+ })
77
+ };
78
+ }
79
+ return { success: true, payerAddress: result.payerAddress };
80
+ };
81
+ var verifyPaymentWithRetry = async (payload, requirements, facilitatorUrl, verifyOptions) => facilitatorUrl ? verifyWithFacilitator(facilitatorUrl, payload, requirements, verifyOptions) : verifyLocally(payload, requirements, verifyOptions);
82
+ var extractPayerAddress = (payload) => {
83
+ if ("payload" in payload) {
84
+ const x402Payload = payload;
85
+ if ("authorization" in x402Payload.payload) {
86
+ return x402Payload.payload.authorization.from;
87
+ }
88
+ }
89
+ if ("from" in payload && typeof payload.from === "string") {
90
+ return payload.from;
91
+ }
92
+ throw new Error("Unable to extract payer address from payload");
93
+ };
94
+ var createResponseHeaders = (payerAddress, version) => ({
95
+ [getHeadersForVersion(version).response]: JSON.stringify({
96
+ status: "verified",
97
+ payerAddress,
98
+ version
99
+ })
100
+ });
101
+
102
+ // src/index.ts
103
+ var errorResponse = (error, status, headers) => new Response(JSON.stringify(error), {
104
+ status,
105
+ headers: { "Content-Type": "application/json", ...headers }
106
+ });
107
+ var paymentMiddleware = (config) => {
108
+ const { requirements, facilitatorUrl, verifyOptions, skipVerification = false } = config;
109
+ const version = getRequirementsVersion(requirements);
110
+ const headers = getHeadersForVersion(version);
111
+ return {
112
+ beforeHandle: async (context) => {
113
+ try {
114
+ const paymentHeader = context.request.headers.get(headers.payment);
115
+ if (!paymentHeader) {
116
+ return errorResponse(
117
+ { error: "Payment required", requirements },
118
+ 402,
119
+ { [headers.required]: encodeRequirements(requirements) }
120
+ );
121
+ }
122
+ let payload;
123
+ let payloadVersion;
124
+ try {
125
+ ({ payload, version: payloadVersion } = decodePayload(paymentHeader));
126
+ } catch (error) {
127
+ return errorResponse({
128
+ error: "Invalid payment payload",
129
+ message: error instanceof Error ? error.message : "Unknown error"
130
+ }, 400);
131
+ }
132
+ if (payloadVersion !== version) {
133
+ return errorResponse({
134
+ error: "Payment version mismatch",
135
+ expected: version,
136
+ received: payloadVersion
137
+ }, 400);
138
+ }
139
+ const verifyResult = skipVerification ? { success: true, payerAddress: extractPayerAddress(payload) } : await verifyPaymentWithRetry(payload, requirements, facilitatorUrl, verifyOptions);
140
+ if (!verifyResult.success) {
141
+ return errorResponse(
142
+ { error: verifyResult.error },
143
+ 402,
144
+ { [headers.required]: encodeRequirements(requirements) }
145
+ );
146
+ }
147
+ const payerAddress = verifyResult.payerAddress;
148
+ context.store.payment = { payload, payerAddress, version, verified: !skipVerification };
149
+ context.set.headers = {
150
+ ...context.set.headers,
151
+ ...createResponseHeaders(payerAddress, version)
152
+ };
153
+ } catch (error) {
154
+ return errorResponse({
155
+ error: "Payment middleware error",
156
+ message: error instanceof Error ? error.message : "Unknown error"
157
+ }, 500);
158
+ }
159
+ }
160
+ };
161
+ };
162
+ export {
163
+ paymentMiddleware
164
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@armory-sh/middleware-elysia",
3
+ "version": "0.3.2",
4
+ "license": "MIT",
5
+ "author": "Sawyer Cutler <sawyer@dirtroad.dev>",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "bun": "./src/index.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/thegreataxios/armory.git",
25
+ "directory": "packages/middleware-elysia"
26
+ },
27
+ "peerDependencies": {
28
+ "elysia": "^1"
29
+ },
30
+ "dependencies": {
31
+ "@armory-sh/base": "0.2.4",
32
+ "@armory-sh/facilitator": "0.2.4"
33
+ },
34
+ "devDependencies": {
35
+ "bun-types": "latest",
36
+ "typescript": "5.9.3",
37
+ "elysia": "^1"
38
+ },
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "test": "bun test"
42
+ }
43
+ }