@armory-sh/middleware-elysia 0.3.11 → 0.3.12
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 +36 -8
- package/dist/index.js +72 -64
- package/package.json +3 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { VerifyPaymentOptions } from '@armory-sh/facilitator';
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
3
2
|
import { X402PaymentPayload, X402PaymentRequirements } from '@armory-sh/base';
|
|
4
3
|
|
|
5
4
|
interface LegacyPaymentPayloadV1 {
|
|
@@ -27,8 +26,8 @@ type PaymentPayload = AnyPaymentPayload;
|
|
|
27
26
|
interface PaymentMiddlewareConfig {
|
|
28
27
|
requirements: X402PaymentRequirements;
|
|
29
28
|
facilitatorUrl?: string;
|
|
30
|
-
verifyOptions?: VerifyPaymentOptions;
|
|
31
29
|
skipVerification?: boolean;
|
|
30
|
+
defaultVersion?: 1 | 2;
|
|
32
31
|
}
|
|
33
32
|
interface PaymentInfo {
|
|
34
33
|
payload: PaymentPayload;
|
|
@@ -39,10 +38,39 @@ interface PaymentInfo {
|
|
|
39
38
|
interface PaymentContext {
|
|
40
39
|
payment: PaymentInfo;
|
|
41
40
|
}
|
|
42
|
-
declare const paymentMiddleware: (config: PaymentMiddlewareConfig) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
};
|
|
41
|
+
declare const paymentMiddleware: (config: PaymentMiddlewareConfig) => Elysia<"", {
|
|
42
|
+
decorator: {};
|
|
43
|
+
store: {};
|
|
44
|
+
derive: {};
|
|
45
|
+
resolve: {};
|
|
46
|
+
}, {
|
|
47
|
+
typebox: {};
|
|
48
|
+
error: {};
|
|
49
|
+
}, {
|
|
50
|
+
schema: {};
|
|
51
|
+
standaloneSchema: {};
|
|
52
|
+
macro: {};
|
|
53
|
+
macroFn: {};
|
|
54
|
+
parser: {};
|
|
55
|
+
response: {};
|
|
56
|
+
}, {}, {
|
|
57
|
+
derive: {};
|
|
58
|
+
resolve: {};
|
|
59
|
+
schema: {};
|
|
60
|
+
standaloneSchema: {};
|
|
61
|
+
response: {};
|
|
62
|
+
}, {
|
|
63
|
+
derive: {
|
|
64
|
+
readonly payment: PaymentInfo | undefined;
|
|
65
|
+
};
|
|
66
|
+
resolve: {};
|
|
67
|
+
schema: {};
|
|
68
|
+
standaloneSchema: {};
|
|
69
|
+
response: {
|
|
70
|
+
200: Response | {
|
|
71
|
+
headers: Record<string, string>;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
}>;
|
|
47
75
|
|
|
48
76
|
export { type PaymentContext, type PaymentInfo, type PaymentMiddlewareConfig, paymentMiddleware };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
|
|
1
4
|
// src/payment-utils.ts
|
|
2
5
|
import { extractPaymentFromHeaders, X402_HEADERS } from "@armory-sh/base";
|
|
3
|
-
import { verifyX402Payment as verifyPayment } from "@armory-sh/facilitator";
|
|
4
6
|
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
7
|
var getRequirementsVersion = (requirements) => "contractAddress" in requirements && "network" in requirements ? 1 : 2;
|
|
6
8
|
var encodeRequirements = (requirements) => JSON.stringify(requirements);
|
|
@@ -12,17 +14,20 @@ function isLegacyV2(payload) {
|
|
|
12
14
|
}
|
|
13
15
|
var decodePayload = (headerValue) => {
|
|
14
16
|
let parsed;
|
|
17
|
+
let isJsonString = false;
|
|
15
18
|
try {
|
|
16
19
|
if (headerValue.startsWith("{")) {
|
|
17
20
|
parsed = JSON.parse(headerValue);
|
|
21
|
+
isJsonString = true;
|
|
18
22
|
} else {
|
|
19
23
|
parsed = JSON.parse(atob(headerValue));
|
|
20
24
|
}
|
|
21
25
|
} catch {
|
|
22
26
|
throw new Error("Invalid payment payload");
|
|
23
27
|
}
|
|
28
|
+
const base64Value = isJsonString ? Buffer.from(headerValue).toString("base64") : headerValue;
|
|
24
29
|
const headers = new Headers();
|
|
25
|
-
headers.set(X402_HEADERS.PAYMENT,
|
|
30
|
+
headers.set(X402_HEADERS.PAYMENT, base64Value);
|
|
26
31
|
const x402Payload = extractPaymentFromHeaders(headers);
|
|
27
32
|
if (x402Payload) {
|
|
28
33
|
return { payload: x402Payload, version: 2 };
|
|
@@ -35,12 +40,12 @@ var decodePayload = (headerValue) => {
|
|
|
35
40
|
}
|
|
36
41
|
throw new Error("Unrecognized payment payload format");
|
|
37
42
|
};
|
|
38
|
-
var verifyWithFacilitator = async (facilitatorUrl, payload, requirements
|
|
43
|
+
var verifyWithFacilitator = async (facilitatorUrl, payload, requirements) => {
|
|
39
44
|
try {
|
|
40
45
|
const response = await fetch(`${facilitatorUrl}/verify`, {
|
|
41
46
|
method: "POST",
|
|
42
47
|
headers: { "Content-Type": "application/json" },
|
|
43
|
-
body: JSON.stringify({ payload, requirements
|
|
48
|
+
body: JSON.stringify({ payload, requirements })
|
|
44
49
|
});
|
|
45
50
|
if (!response.ok) {
|
|
46
51
|
const error = await response.json();
|
|
@@ -58,27 +63,19 @@ var verifyWithFacilitator = async (facilitatorUrl, payload, requirements, verify
|
|
|
58
63
|
};
|
|
59
64
|
}
|
|
60
65
|
};
|
|
61
|
-
var verifyLocally = async (payload, requirements
|
|
66
|
+
var verifyLocally = async (payload, requirements) => {
|
|
62
67
|
if (isLegacyV1(payload) || isLegacyV2(payload)) {
|
|
63
68
|
return {
|
|
64
69
|
success: false,
|
|
65
70
|
error: "Local verification not supported for legacy payload formats. Use a facilitator."
|
|
66
71
|
};
|
|
67
72
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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 };
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
payerAddress: payload.payload.authorization.from
|
|
76
|
+
};
|
|
80
77
|
};
|
|
81
|
-
var verifyPaymentWithRetry = async (payload, requirements, facilitatorUrl
|
|
78
|
+
var verifyPaymentWithRetry = async (payload, requirements, facilitatorUrl) => facilitatorUrl ? verifyWithFacilitator(facilitatorUrl, payload, requirements) : verifyLocally(payload, requirements);
|
|
82
79
|
var extractPayerAddress = (payload) => {
|
|
83
80
|
if ("payload" in payload) {
|
|
84
81
|
const x402Payload = payload;
|
|
@@ -105,59 +102,70 @@ var errorResponse = (error, status, headers) => new Response(JSON.stringify(erro
|
|
|
105
102
|
headers: { "Content-Type": "application/json", ...headers }
|
|
106
103
|
});
|
|
107
104
|
var paymentMiddleware = (config) => {
|
|
108
|
-
const { requirements, facilitatorUrl,
|
|
109
|
-
const version = getRequirementsVersion(requirements);
|
|
105
|
+
const { requirements, facilitatorUrl, skipVerification = false, defaultVersion } = config;
|
|
106
|
+
const version = defaultVersion ?? getRequirementsVersion(requirements);
|
|
110
107
|
const headers = getHeadersForVersion(version);
|
|
111
|
-
return {
|
|
112
|
-
|
|
108
|
+
return new Elysia({ name: "armory-payment" }).derive(() => ({
|
|
109
|
+
payment: void 0
|
|
110
|
+
})).onBeforeHandle(async ({ payment, request }) => {
|
|
111
|
+
try {
|
|
112
|
+
const paymentHeader = request.headers.get(headers.payment);
|
|
113
|
+
if (!paymentHeader) {
|
|
114
|
+
const requiredValue = version === 1 ? Buffer.from(JSON.stringify(requirements)).toString("base64") : JSON.stringify({
|
|
115
|
+
x402Version: version,
|
|
116
|
+
resource: {
|
|
117
|
+
url: request.url,
|
|
118
|
+
description: "API Access"
|
|
119
|
+
},
|
|
120
|
+
accepts: [requirements]
|
|
121
|
+
});
|
|
122
|
+
return errorResponse(
|
|
123
|
+
{ error: "Payment required", accepts: [requirements] },
|
|
124
|
+
402,
|
|
125
|
+
{ [headers.required]: requiredValue }
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
let payload;
|
|
129
|
+
let payloadVersion;
|
|
113
130
|
try {
|
|
114
|
-
|
|
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
|
-
};
|
|
131
|
+
({ payload, version: payloadVersion } = decodePayload(paymentHeader));
|
|
153
132
|
} catch (error) {
|
|
154
133
|
return errorResponse({
|
|
155
|
-
error: "
|
|
134
|
+
error: "Invalid payment payload",
|
|
156
135
|
message: error instanceof Error ? error.message : "Unknown error"
|
|
157
|
-
},
|
|
136
|
+
}, 400);
|
|
137
|
+
}
|
|
138
|
+
if (payloadVersion !== version) {
|
|
139
|
+
return errorResponse({
|
|
140
|
+
error: "Payment version mismatch",
|
|
141
|
+
expected: version,
|
|
142
|
+
received: payloadVersion
|
|
143
|
+
}, 400);
|
|
158
144
|
}
|
|
145
|
+
const verifyResult = skipVerification ? { success: true, payerAddress: extractPayerAddress(payload) } : await verifyPaymentWithRetry(payload, requirements, facilitatorUrl);
|
|
146
|
+
if (!verifyResult.success) {
|
|
147
|
+
return errorResponse(
|
|
148
|
+
{ error: verifyResult.error },
|
|
149
|
+
402,
|
|
150
|
+
{ [headers.required]: encodeRequirements(requirements) }
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
const payerAddress = verifyResult.payerAddress;
|
|
154
|
+
payment = { payload, payerAddress, version, verified: !skipVerification };
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return errorResponse({
|
|
157
|
+
error: "Payment middleware error",
|
|
158
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
159
|
+
}, 500);
|
|
159
160
|
}
|
|
160
|
-
}
|
|
161
|
+
}).onAfterHandle(({ payment }) => {
|
|
162
|
+
if (payment) {
|
|
163
|
+
const { payerAddress, version: version2 } = payment;
|
|
164
|
+
return {
|
|
165
|
+
headers: createResponseHeaders(payerAddress, version2)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
});
|
|
161
169
|
};
|
|
162
170
|
export {
|
|
163
171
|
paymentMiddleware
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@armory-sh/middleware-elysia",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Sawyer Cutler <sawyer@dirtroad.dev>",
|
|
6
6
|
"type": "module",
|
|
@@ -28,8 +28,7 @@
|
|
|
28
28
|
"elysia": "^1"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@armory-sh/base": "
|
|
32
|
-
"@armory-sh/facilitator": "0.2.12"
|
|
31
|
+
"@armory-sh/base": "workspace:*"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"bun-types": "latest",
|
|
@@ -37,7 +36,7 @@
|
|
|
37
36
|
"elysia": "^1"
|
|
38
37
|
},
|
|
39
38
|
"scripts": {
|
|
40
|
-
"build": "tsup",
|
|
39
|
+
"build": "rm -rf dist && tsup",
|
|
41
40
|
"test": "bun test"
|
|
42
41
|
}
|
|
43
42
|
}
|