@armory-sh/middleware-elysia 0.3.11 → 0.3.13
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 -32
- package/dist/index.js +68 -82
- package/package.json +3 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,48 +1,52 @@
|
|
|
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
|
-
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
4
|
interface PaymentMiddlewareConfig {
|
|
28
5
|
requirements: X402PaymentRequirements;
|
|
29
6
|
facilitatorUrl?: string;
|
|
30
|
-
verifyOptions?: VerifyPaymentOptions;
|
|
31
7
|
skipVerification?: boolean;
|
|
32
8
|
}
|
|
33
9
|
interface PaymentInfo {
|
|
34
|
-
payload:
|
|
10
|
+
payload: X402PaymentPayload;
|
|
35
11
|
payerAddress: string;
|
|
36
|
-
version: 1 | 2;
|
|
37
12
|
verified: boolean;
|
|
38
13
|
}
|
|
39
14
|
interface PaymentContext {
|
|
40
15
|
payment: PaymentInfo;
|
|
41
16
|
}
|
|
42
|
-
declare const paymentMiddleware: (config: PaymentMiddlewareConfig) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
};
|
|
17
|
+
declare const paymentMiddleware: (config: PaymentMiddlewareConfig) => Elysia<"", {
|
|
18
|
+
decorator: {};
|
|
19
|
+
store: {};
|
|
20
|
+
derive: {};
|
|
21
|
+
resolve: {};
|
|
22
|
+
}, {
|
|
23
|
+
typebox: {};
|
|
24
|
+
error: {};
|
|
25
|
+
}, {
|
|
26
|
+
schema: {};
|
|
27
|
+
standaloneSchema: {};
|
|
28
|
+
macro: {};
|
|
29
|
+
macroFn: {};
|
|
30
|
+
parser: {};
|
|
31
|
+
response: {};
|
|
32
|
+
}, {}, {
|
|
33
|
+
derive: {};
|
|
34
|
+
resolve: {};
|
|
35
|
+
schema: {};
|
|
36
|
+
standaloneSchema: {};
|
|
37
|
+
response: {};
|
|
38
|
+
}, {
|
|
39
|
+
derive: {
|
|
40
|
+
readonly payment: PaymentInfo | undefined;
|
|
41
|
+
};
|
|
42
|
+
resolve: {};
|
|
43
|
+
schema: {};
|
|
44
|
+
standaloneSchema: {};
|
|
45
|
+
response: {
|
|
46
|
+
200: Response | {
|
|
47
|
+
headers: Record<string, string>;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
}>;
|
|
47
51
|
|
|
48
52
|
export { type PaymentContext, type PaymentInfo, type PaymentMiddlewareConfig, paymentMiddleware };
|
package/dist/index.js
CHANGED
|
@@ -1,46 +1,48 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
|
|
1
4
|
// src/payment-utils.ts
|
|
2
|
-
import { extractPaymentFromHeaders,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
import { extractPaymentFromHeaders, PAYMENT_SIGNATURE_HEADER } from "@armory-sh/base";
|
|
6
|
+
var PAYMENT_HEADERS = {
|
|
7
|
+
PAYMENT: "PAYMENT-SIGNATURE",
|
|
8
|
+
REQUIRED: "PAYMENT-REQUIRED",
|
|
9
|
+
RESPONSE: "PAYMENT-RESPONSE"
|
|
10
|
+
};
|
|
6
11
|
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
12
|
function isLegacyV2(payload) {
|
|
11
13
|
return typeof payload === "object" && payload !== null && "chainId" in payload && "assetId" in payload && "signature" in payload && typeof payload.signature === "string";
|
|
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(
|
|
30
|
+
headers.set(PAYMENT_SIGNATURE_HEADER, base64Value);
|
|
26
31
|
const x402Payload = extractPaymentFromHeaders(headers);
|
|
27
32
|
if (x402Payload) {
|
|
28
|
-
return { payload: x402Payload
|
|
29
|
-
}
|
|
30
|
-
if (isLegacyV1(parsed)) {
|
|
31
|
-
return { payload: parsed, version: 1 };
|
|
33
|
+
return { payload: x402Payload };
|
|
32
34
|
}
|
|
33
35
|
if (isLegacyV2(parsed)) {
|
|
34
|
-
return { payload: parsed
|
|
36
|
+
return { payload: parsed };
|
|
35
37
|
}
|
|
36
38
|
throw new Error("Unrecognized payment payload format");
|
|
37
39
|
};
|
|
38
|
-
var verifyWithFacilitator = async (facilitatorUrl, payload, requirements
|
|
40
|
+
var verifyWithFacilitator = async (facilitatorUrl, payload, requirements) => {
|
|
39
41
|
try {
|
|
40
42
|
const response = await fetch(`${facilitatorUrl}/verify`, {
|
|
41
43
|
method: "POST",
|
|
42
44
|
headers: { "Content-Type": "application/json" },
|
|
43
|
-
body: JSON.stringify({ payload, requirements
|
|
45
|
+
body: JSON.stringify({ payload, requirements })
|
|
44
46
|
});
|
|
45
47
|
if (!response.ok) {
|
|
46
48
|
const error = await response.json();
|
|
@@ -58,27 +60,19 @@ var verifyWithFacilitator = async (facilitatorUrl, payload, requirements, verify
|
|
|
58
60
|
};
|
|
59
61
|
}
|
|
60
62
|
};
|
|
61
|
-
var verifyLocally = async (payload, requirements
|
|
62
|
-
if (
|
|
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) {
|
|
63
|
+
var verifyLocally = async (payload, requirements) => {
|
|
64
|
+
if (isLegacyV2(payload)) {
|
|
70
65
|
return {
|
|
71
66
|
success: false,
|
|
72
|
-
error:
|
|
73
|
-
error: "Payment verification failed",
|
|
74
|
-
reason: result.error.name,
|
|
75
|
-
message: result.error.message
|
|
76
|
-
})
|
|
67
|
+
error: "Local verification not supported for legacy payload format. Use a facilitator."
|
|
77
68
|
};
|
|
78
69
|
}
|
|
79
|
-
return {
|
|
70
|
+
return {
|
|
71
|
+
success: true,
|
|
72
|
+
payerAddress: payload.payload.authorization.from
|
|
73
|
+
};
|
|
80
74
|
};
|
|
81
|
-
var verifyPaymentWithRetry = async (payload, requirements, facilitatorUrl
|
|
75
|
+
var verifyPaymentWithRetry = async (payload, requirements, facilitatorUrl) => facilitatorUrl ? verifyWithFacilitator(facilitatorUrl, payload, requirements) : verifyLocally(payload, requirements);
|
|
82
76
|
var extractPayerAddress = (payload) => {
|
|
83
77
|
if ("payload" in payload) {
|
|
84
78
|
const x402Payload = payload;
|
|
@@ -91,11 +85,10 @@ var extractPayerAddress = (payload) => {
|
|
|
91
85
|
}
|
|
92
86
|
throw new Error("Unable to extract payer address from payload");
|
|
93
87
|
};
|
|
94
|
-
var createResponseHeaders = (payerAddress
|
|
95
|
-
[
|
|
88
|
+
var createResponseHeaders = (payerAddress) => ({
|
|
89
|
+
[PAYMENT_HEADERS.RESPONSE]: JSON.stringify({
|
|
96
90
|
status: "verified",
|
|
97
|
-
payerAddress
|
|
98
|
-
version
|
|
91
|
+
payerAddress
|
|
99
92
|
})
|
|
100
93
|
});
|
|
101
94
|
|
|
@@ -105,59 +98,52 @@ var errorResponse = (error, status, headers) => new Response(JSON.stringify(erro
|
|
|
105
98
|
headers: { "Content-Type": "application/json", ...headers }
|
|
106
99
|
});
|
|
107
100
|
var paymentMiddleware = (config) => {
|
|
108
|
-
const { requirements, facilitatorUrl,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
101
|
+
const { requirements, facilitatorUrl, skipVerification = false } = config;
|
|
102
|
+
return new Elysia({ name: "armory-payment" }).derive(() => ({
|
|
103
|
+
payment: void 0
|
|
104
|
+
})).onBeforeHandle(async ({ payment, request }) => {
|
|
105
|
+
try {
|
|
106
|
+
const paymentHeader = request.headers.get(PAYMENT_HEADERS.PAYMENT);
|
|
107
|
+
if (!paymentHeader) {
|
|
108
|
+
return errorResponse(
|
|
109
|
+
{ error: "Payment required", accepts: [requirements] },
|
|
110
|
+
402,
|
|
111
|
+
{ [PAYMENT_HEADERS.REQUIRED]: encodeRequirements(requirements) }
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
let payload;
|
|
113
115
|
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
|
-
};
|
|
116
|
+
({ payload } = decodePayload(paymentHeader));
|
|
153
117
|
} catch (error) {
|
|
154
118
|
return errorResponse({
|
|
155
|
-
error: "
|
|
119
|
+
error: "Invalid payment payload",
|
|
156
120
|
message: error instanceof Error ? error.message : "Unknown error"
|
|
157
|
-
},
|
|
121
|
+
}, 400);
|
|
122
|
+
}
|
|
123
|
+
const verifyResult = skipVerification ? { success: true, payerAddress: extractPayerAddress(payload) } : await verifyPaymentWithRetry(payload, requirements, facilitatorUrl);
|
|
124
|
+
if (!verifyResult.success) {
|
|
125
|
+
return errorResponse(
|
|
126
|
+
{ error: verifyResult.error },
|
|
127
|
+
402,
|
|
128
|
+
{ [PAYMENT_HEADERS.REQUIRED]: encodeRequirements(requirements) }
|
|
129
|
+
);
|
|
158
130
|
}
|
|
131
|
+
const payerAddress = verifyResult.payerAddress;
|
|
132
|
+
payment = { payload, payerAddress, verified: !skipVerification };
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return errorResponse({
|
|
135
|
+
error: "Payment middleware error",
|
|
136
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
137
|
+
}, 500);
|
|
159
138
|
}
|
|
160
|
-
}
|
|
139
|
+
}).onAfterHandle(({ payment }) => {
|
|
140
|
+
if (payment) {
|
|
141
|
+
const { payerAddress } = payment;
|
|
142
|
+
return {
|
|
143
|
+
headers: createResponseHeaders(payerAddress)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
});
|
|
161
147
|
};
|
|
162
148
|
export {
|
|
163
149
|
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.13",
|
|
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
|
}
|