@perkos/service-x402 1.0.0
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.mts +94 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.js +301 -0
- package/dist/index.mjs +280 -0
- package/package.json +49 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Address, X402VerifyRequest, VerifyResponse, PaymentPayload, PaymentRequirements } from '@perkos/types-x402';
|
|
2
|
+
export { Address, DeferredPayload, ExactPayload, Hex, PaymentPayload, PaymentRequirements, SettleResponse, VerifyResponse, Voucher, X402SettleRequest, X402VerifyRequest } from '@perkos/types-x402';
|
|
3
|
+
import { SupportedNetwork } from '@perkos/util-chains';
|
|
4
|
+
export { SUPPORTED_NETWORKS, SupportedNetwork, getChainIdFromNetwork } from '@perkos/util-chains';
|
|
5
|
+
import { ExactSchemeVerifier } from '@perkos/scheme-exact';
|
|
6
|
+
export { ExactSchemeVerifier, createEIP712Domain as createExactEIP712Domain, generateNonce, parseSignature as parseExactSignature } from '@perkos/scheme-exact';
|
|
7
|
+
import { DeferredSchemeVerifier } from '@perkos/scheme-deferred';
|
|
8
|
+
export { DeferredSchemeVerifier, createEIP712Domain as createDeferredEIP712Domain, createVoucherMessage, createVoucherTuple, generateVoucherId, parseSignature as parseDeferredSignature } from '@perkos/scheme-deferred';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @perkos/service-x402
|
|
12
|
+
* Unified x402 payment protocol verification and settlement service
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
interface X402ServiceConfig {
|
|
16
|
+
/** Default network to use */
|
|
17
|
+
defaultNetwork?: SupportedNetwork;
|
|
18
|
+
/** RPC URL overrides by network */
|
|
19
|
+
rpcUrls?: Partial<Record<SupportedNetwork, string>>;
|
|
20
|
+
/** Enable deferred scheme */
|
|
21
|
+
deferredEnabled?: boolean;
|
|
22
|
+
/** Escrow addresses for deferred scheme by network */
|
|
23
|
+
escrowAddresses?: Partial<Record<SupportedNetwork, Address>>;
|
|
24
|
+
/** Token name overrides for exact scheme (by chainId) */
|
|
25
|
+
tokenNames?: Record<number, string>;
|
|
26
|
+
/** Token version overrides for exact scheme (by chainId) */
|
|
27
|
+
tokenVersions?: Record<number, string>;
|
|
28
|
+
}
|
|
29
|
+
interface SupportedResponse {
|
|
30
|
+
kinds: Array<{
|
|
31
|
+
scheme: "exact" | "deferred";
|
|
32
|
+
network: SupportedNetwork;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
declare class X402Service {
|
|
36
|
+
private config;
|
|
37
|
+
private exactSchemes;
|
|
38
|
+
private deferredSchemes;
|
|
39
|
+
constructor(config?: X402ServiceConfig);
|
|
40
|
+
/**
|
|
41
|
+
* Convert CAIP-2 network format to legacy format
|
|
42
|
+
*/
|
|
43
|
+
caip2ToLegacy(caip2: string): SupportedNetwork | null;
|
|
44
|
+
/**
|
|
45
|
+
* Convert legacy network format to CAIP-2 format
|
|
46
|
+
*/
|
|
47
|
+
legacyToCaip2(network: SupportedNetwork): string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Normalize network format (CAIP-2 or legacy) to legacy format
|
|
50
|
+
*/
|
|
51
|
+
normalizeNetwork(network: string): SupportedNetwork | null;
|
|
52
|
+
/**
|
|
53
|
+
* Verify payment payload
|
|
54
|
+
*/
|
|
55
|
+
verify(request: X402VerifyRequest): Promise<VerifyResponse>;
|
|
56
|
+
/**
|
|
57
|
+
* Verify payment without full request wrapper
|
|
58
|
+
*/
|
|
59
|
+
verifyPayload(payload: PaymentPayload, requirements: PaymentRequirements): Promise<VerifyResponse>;
|
|
60
|
+
/**
|
|
61
|
+
* Get supported schemes and networks
|
|
62
|
+
*/
|
|
63
|
+
getSupported(): SupportedResponse;
|
|
64
|
+
/**
|
|
65
|
+
* Check if a network is supported
|
|
66
|
+
*/
|
|
67
|
+
isNetworkSupported(network: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Check if deferred scheme is enabled for a network
|
|
70
|
+
*/
|
|
71
|
+
isDeferredEnabled(network: string): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Get exact scheme verifier for network
|
|
74
|
+
*/
|
|
75
|
+
getExactScheme(network: SupportedNetwork): ExactSchemeVerifier | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Get deferred scheme verifier for network
|
|
78
|
+
*/
|
|
79
|
+
getDeferredScheme(network: SupportedNetwork): DeferredSchemeVerifier | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Get default network
|
|
82
|
+
*/
|
|
83
|
+
getDefaultNetwork(): SupportedNetwork;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create a new X402 service instance
|
|
87
|
+
*/
|
|
88
|
+
declare function createX402Service(config?: X402ServiceConfig): X402Service;
|
|
89
|
+
/**
|
|
90
|
+
* Create a minimal X402 service for verification only (no deferred scheme)
|
|
91
|
+
*/
|
|
92
|
+
declare function createVerificationService(networks?: SupportedNetwork[], rpcUrls?: Partial<Record<SupportedNetwork, string>>): X402Service;
|
|
93
|
+
|
|
94
|
+
export { type SupportedResponse, X402Service, type X402ServiceConfig, createVerificationService, createX402Service };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Address, X402VerifyRequest, VerifyResponse, PaymentPayload, PaymentRequirements } from '@perkos/types-x402';
|
|
2
|
+
export { Address, DeferredPayload, ExactPayload, Hex, PaymentPayload, PaymentRequirements, SettleResponse, VerifyResponse, Voucher, X402SettleRequest, X402VerifyRequest } from '@perkos/types-x402';
|
|
3
|
+
import { SupportedNetwork } from '@perkos/util-chains';
|
|
4
|
+
export { SUPPORTED_NETWORKS, SupportedNetwork, getChainIdFromNetwork } from '@perkos/util-chains';
|
|
5
|
+
import { ExactSchemeVerifier } from '@perkos/scheme-exact';
|
|
6
|
+
export { ExactSchemeVerifier, createEIP712Domain as createExactEIP712Domain, generateNonce, parseSignature as parseExactSignature } from '@perkos/scheme-exact';
|
|
7
|
+
import { DeferredSchemeVerifier } from '@perkos/scheme-deferred';
|
|
8
|
+
export { DeferredSchemeVerifier, createEIP712Domain as createDeferredEIP712Domain, createVoucherMessage, createVoucherTuple, generateVoucherId, parseSignature as parseDeferredSignature } from '@perkos/scheme-deferred';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @perkos/service-x402
|
|
12
|
+
* Unified x402 payment protocol verification and settlement service
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
interface X402ServiceConfig {
|
|
16
|
+
/** Default network to use */
|
|
17
|
+
defaultNetwork?: SupportedNetwork;
|
|
18
|
+
/** RPC URL overrides by network */
|
|
19
|
+
rpcUrls?: Partial<Record<SupportedNetwork, string>>;
|
|
20
|
+
/** Enable deferred scheme */
|
|
21
|
+
deferredEnabled?: boolean;
|
|
22
|
+
/** Escrow addresses for deferred scheme by network */
|
|
23
|
+
escrowAddresses?: Partial<Record<SupportedNetwork, Address>>;
|
|
24
|
+
/** Token name overrides for exact scheme (by chainId) */
|
|
25
|
+
tokenNames?: Record<number, string>;
|
|
26
|
+
/** Token version overrides for exact scheme (by chainId) */
|
|
27
|
+
tokenVersions?: Record<number, string>;
|
|
28
|
+
}
|
|
29
|
+
interface SupportedResponse {
|
|
30
|
+
kinds: Array<{
|
|
31
|
+
scheme: "exact" | "deferred";
|
|
32
|
+
network: SupportedNetwork;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
declare class X402Service {
|
|
36
|
+
private config;
|
|
37
|
+
private exactSchemes;
|
|
38
|
+
private deferredSchemes;
|
|
39
|
+
constructor(config?: X402ServiceConfig);
|
|
40
|
+
/**
|
|
41
|
+
* Convert CAIP-2 network format to legacy format
|
|
42
|
+
*/
|
|
43
|
+
caip2ToLegacy(caip2: string): SupportedNetwork | null;
|
|
44
|
+
/**
|
|
45
|
+
* Convert legacy network format to CAIP-2 format
|
|
46
|
+
*/
|
|
47
|
+
legacyToCaip2(network: SupportedNetwork): string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Normalize network format (CAIP-2 or legacy) to legacy format
|
|
50
|
+
*/
|
|
51
|
+
normalizeNetwork(network: string): SupportedNetwork | null;
|
|
52
|
+
/**
|
|
53
|
+
* Verify payment payload
|
|
54
|
+
*/
|
|
55
|
+
verify(request: X402VerifyRequest): Promise<VerifyResponse>;
|
|
56
|
+
/**
|
|
57
|
+
* Verify payment without full request wrapper
|
|
58
|
+
*/
|
|
59
|
+
verifyPayload(payload: PaymentPayload, requirements: PaymentRequirements): Promise<VerifyResponse>;
|
|
60
|
+
/**
|
|
61
|
+
* Get supported schemes and networks
|
|
62
|
+
*/
|
|
63
|
+
getSupported(): SupportedResponse;
|
|
64
|
+
/**
|
|
65
|
+
* Check if a network is supported
|
|
66
|
+
*/
|
|
67
|
+
isNetworkSupported(network: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Check if deferred scheme is enabled for a network
|
|
70
|
+
*/
|
|
71
|
+
isDeferredEnabled(network: string): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Get exact scheme verifier for network
|
|
74
|
+
*/
|
|
75
|
+
getExactScheme(network: SupportedNetwork): ExactSchemeVerifier | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Get deferred scheme verifier for network
|
|
78
|
+
*/
|
|
79
|
+
getDeferredScheme(network: SupportedNetwork): DeferredSchemeVerifier | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Get default network
|
|
82
|
+
*/
|
|
83
|
+
getDefaultNetwork(): SupportedNetwork;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create a new X402 service instance
|
|
87
|
+
*/
|
|
88
|
+
declare function createX402Service(config?: X402ServiceConfig): X402Service;
|
|
89
|
+
/**
|
|
90
|
+
* Create a minimal X402 service for verification only (no deferred scheme)
|
|
91
|
+
*/
|
|
92
|
+
declare function createVerificationService(networks?: SupportedNetwork[], rpcUrls?: Partial<Record<SupportedNetwork, string>>): X402Service;
|
|
93
|
+
|
|
94
|
+
export { type SupportedResponse, X402Service, type X402ServiceConfig, createVerificationService, createX402Service };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DeferredSchemeVerifier: () => import_scheme_deferred2.DeferredSchemeVerifier,
|
|
24
|
+
ExactSchemeVerifier: () => import_scheme_exact2.ExactSchemeVerifier,
|
|
25
|
+
SUPPORTED_NETWORKS: () => import_util_chains2.SUPPORTED_NETWORKS,
|
|
26
|
+
X402Service: () => X402Service,
|
|
27
|
+
createDeferredEIP712Domain: () => import_scheme_deferred2.createEIP712Domain,
|
|
28
|
+
createExactEIP712Domain: () => import_scheme_exact2.createEIP712Domain,
|
|
29
|
+
createVerificationService: () => createVerificationService,
|
|
30
|
+
createVoucherMessage: () => import_scheme_deferred2.createVoucherMessage,
|
|
31
|
+
createVoucherTuple: () => import_scheme_deferred2.createVoucherTuple,
|
|
32
|
+
createX402Service: () => createX402Service,
|
|
33
|
+
generateNonce: () => import_scheme_exact2.generateNonce,
|
|
34
|
+
generateVoucherId: () => import_scheme_deferred2.generateVoucherId,
|
|
35
|
+
getChainIdFromNetwork: () => import_util_chains2.getChainIdFromNetwork,
|
|
36
|
+
parseDeferredSignature: () => import_scheme_deferred2.parseSignature,
|
|
37
|
+
parseExactSignature: () => import_scheme_exact2.parseSignature
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
var import_util_chains = require("@perkos/util-chains");
|
|
41
|
+
var import_scheme_exact = require("@perkos/scheme-exact");
|
|
42
|
+
var import_scheme_deferred = require("@perkos/scheme-deferred");
|
|
43
|
+
var import_util_chains2 = require("@perkos/util-chains");
|
|
44
|
+
var import_scheme_exact2 = require("@perkos/scheme-exact");
|
|
45
|
+
var import_scheme_deferred2 = require("@perkos/scheme-deferred");
|
|
46
|
+
var CAIP2_TO_LEGACY = {
|
|
47
|
+
// Avalanche
|
|
48
|
+
"eip155:43114": "avalanche",
|
|
49
|
+
"eip155:43113": "avalanche-fuji",
|
|
50
|
+
// Celo
|
|
51
|
+
"eip155:42220": "celo",
|
|
52
|
+
"eip155:11142220": "celo-sepolia",
|
|
53
|
+
// Base
|
|
54
|
+
"eip155:8453": "base",
|
|
55
|
+
"eip155:84532": "base-sepolia",
|
|
56
|
+
// Ethereum
|
|
57
|
+
"eip155:1": "ethereum",
|
|
58
|
+
"eip155:11155111": "sepolia",
|
|
59
|
+
// Polygon
|
|
60
|
+
"eip155:137": "polygon",
|
|
61
|
+
"eip155:80002": "polygon-amoy",
|
|
62
|
+
// Monad
|
|
63
|
+
"eip155:10142": "monad",
|
|
64
|
+
"eip155:10143": "monad-testnet",
|
|
65
|
+
// Arbitrum
|
|
66
|
+
"eip155:42161": "arbitrum",
|
|
67
|
+
"eip155:421614": "arbitrum-sepolia",
|
|
68
|
+
// Optimism
|
|
69
|
+
"eip155:10": "optimism",
|
|
70
|
+
"eip155:11155420": "optimism-sepolia"
|
|
71
|
+
};
|
|
72
|
+
var LEGACY_TO_CAIP2 = Object.fromEntries(
|
|
73
|
+
Object.entries(CAIP2_TO_LEGACY).map(([k, v]) => [v, k])
|
|
74
|
+
);
|
|
75
|
+
var X402Service = class {
|
|
76
|
+
constructor(config = {}) {
|
|
77
|
+
this.exactSchemes = /* @__PURE__ */ new Map();
|
|
78
|
+
this.deferredSchemes = /* @__PURE__ */ new Map();
|
|
79
|
+
this.config = {
|
|
80
|
+
defaultNetwork: "base-sepolia",
|
|
81
|
+
deferredEnabled: false,
|
|
82
|
+
...config
|
|
83
|
+
};
|
|
84
|
+
for (const network of import_util_chains.SUPPORTED_NETWORKS) {
|
|
85
|
+
const chainId = (0, import_util_chains.getChainIdFromNetwork)(network);
|
|
86
|
+
if (chainId) {
|
|
87
|
+
this.exactSchemes.set(
|
|
88
|
+
network,
|
|
89
|
+
new import_scheme_exact.ExactSchemeVerifier({
|
|
90
|
+
network,
|
|
91
|
+
rpcUrl: this.config.rpcUrls?.[network],
|
|
92
|
+
tokenName: chainId ? this.config.tokenNames?.[chainId] : void 0,
|
|
93
|
+
tokenVersion: chainId ? this.config.tokenVersions?.[chainId] : void 0
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (this.config.deferredEnabled && this.config.escrowAddresses) {
|
|
99
|
+
for (const [network, escrowAddress] of Object.entries(this.config.escrowAddresses)) {
|
|
100
|
+
if (escrowAddress && import_util_chains.SUPPORTED_NETWORKS.includes(network)) {
|
|
101
|
+
try {
|
|
102
|
+
this.deferredSchemes.set(
|
|
103
|
+
network,
|
|
104
|
+
new import_scheme_deferred.DeferredSchemeVerifier({
|
|
105
|
+
network,
|
|
106
|
+
escrowAddress,
|
|
107
|
+
rpcUrl: this.config.rpcUrls?.[network]
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Convert CAIP-2 network format to legacy format
|
|
118
|
+
*/
|
|
119
|
+
caip2ToLegacy(caip2) {
|
|
120
|
+
return CAIP2_TO_LEGACY[caip2] || null;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Convert legacy network format to CAIP-2 format
|
|
124
|
+
*/
|
|
125
|
+
legacyToCaip2(network) {
|
|
126
|
+
return LEGACY_TO_CAIP2[network] || null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Normalize network format (CAIP-2 or legacy) to legacy format
|
|
130
|
+
*/
|
|
131
|
+
normalizeNetwork(network) {
|
|
132
|
+
if (import_util_chains.SUPPORTED_NETWORKS.includes(network)) {
|
|
133
|
+
return network;
|
|
134
|
+
}
|
|
135
|
+
if (network.includes(":")) {
|
|
136
|
+
return this.caip2ToLegacy(network);
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Verify payment payload
|
|
142
|
+
*/
|
|
143
|
+
async verify(request) {
|
|
144
|
+
const { paymentPayload, paymentRequirements } = request;
|
|
145
|
+
const isV1 = request.x402Version === 1 && paymentPayload.x402Version === 1;
|
|
146
|
+
const isV2 = request.x402Version === 2 && paymentPayload.x402Version === 2;
|
|
147
|
+
if (!isV1 && !isV2) {
|
|
148
|
+
return {
|
|
149
|
+
isValid: false,
|
|
150
|
+
invalidReason: `Unsupported x402 version. Expected 1 or 2, got ${request.x402Version} (payload: ${paymentPayload.x402Version})`,
|
|
151
|
+
payer: null
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const normalizedPayloadNetwork = this.normalizeNetwork(paymentPayload.network);
|
|
155
|
+
const normalizedRequirementsNetwork = this.normalizeNetwork(paymentRequirements.network);
|
|
156
|
+
if (!normalizedPayloadNetwork) {
|
|
157
|
+
return {
|
|
158
|
+
isValid: false,
|
|
159
|
+
invalidReason: `Unsupported network: ${paymentPayload.network}`,
|
|
160
|
+
payer: null
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
if (!normalizedRequirementsNetwork) {
|
|
164
|
+
return {
|
|
165
|
+
isValid: false,
|
|
166
|
+
invalidReason: `Unsupported network: ${paymentRequirements.network}`,
|
|
167
|
+
payer: null
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (normalizedPayloadNetwork !== normalizedRequirementsNetwork) {
|
|
171
|
+
return {
|
|
172
|
+
isValid: false,
|
|
173
|
+
invalidReason: `Network mismatch between payload (${paymentPayload.network}) and requirements (${paymentRequirements.network})`,
|
|
174
|
+
payer: null
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (paymentPayload.scheme !== paymentRequirements.scheme) {
|
|
178
|
+
return {
|
|
179
|
+
isValid: false,
|
|
180
|
+
invalidReason: "Scheme mismatch",
|
|
181
|
+
payer: null
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const network = normalizedPayloadNetwork;
|
|
185
|
+
if (paymentPayload.scheme === "exact") {
|
|
186
|
+
const exactScheme = this.exactSchemes.get(network);
|
|
187
|
+
if (!exactScheme) {
|
|
188
|
+
return {
|
|
189
|
+
isValid: false,
|
|
190
|
+
invalidReason: `Exact scheme not initialized for network: ${network}`,
|
|
191
|
+
payer: null
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return exactScheme.verify(
|
|
195
|
+
paymentPayload.payload,
|
|
196
|
+
paymentRequirements
|
|
197
|
+
);
|
|
198
|
+
} else if (paymentPayload.scheme === "deferred") {
|
|
199
|
+
const deferredScheme = this.deferredSchemes.get(network);
|
|
200
|
+
if (!deferredScheme) {
|
|
201
|
+
return {
|
|
202
|
+
isValid: false,
|
|
203
|
+
invalidReason: `Deferred scheme not enabled for network: ${network}`,
|
|
204
|
+
payer: null
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return deferredScheme.verify(
|
|
208
|
+
paymentPayload.payload,
|
|
209
|
+
paymentRequirements
|
|
210
|
+
);
|
|
211
|
+
} else {
|
|
212
|
+
return {
|
|
213
|
+
isValid: false,
|
|
214
|
+
invalidReason: `Unsupported scheme: ${paymentPayload.scheme}`,
|
|
215
|
+
payer: null
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Verify payment without full request wrapper
|
|
221
|
+
*/
|
|
222
|
+
async verifyPayload(payload, requirements) {
|
|
223
|
+
return this.verify({
|
|
224
|
+
x402Version: payload.x402Version,
|
|
225
|
+
paymentPayload: payload,
|
|
226
|
+
paymentRequirements: requirements
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get supported schemes and networks
|
|
231
|
+
*/
|
|
232
|
+
getSupported() {
|
|
233
|
+
const kinds = [];
|
|
234
|
+
for (const network of this.exactSchemes.keys()) {
|
|
235
|
+
kinds.push({ scheme: "exact", network });
|
|
236
|
+
}
|
|
237
|
+
for (const network of this.deferredSchemes.keys()) {
|
|
238
|
+
kinds.push({ scheme: "deferred", network });
|
|
239
|
+
}
|
|
240
|
+
return { kinds };
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Check if a network is supported
|
|
244
|
+
*/
|
|
245
|
+
isNetworkSupported(network) {
|
|
246
|
+
const normalized = this.normalizeNetwork(network);
|
|
247
|
+
return normalized !== null && this.exactSchemes.has(normalized);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Check if deferred scheme is enabled for a network
|
|
251
|
+
*/
|
|
252
|
+
isDeferredEnabled(network) {
|
|
253
|
+
const normalized = this.normalizeNetwork(network);
|
|
254
|
+
return normalized !== null && this.deferredSchemes.has(normalized);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Get exact scheme verifier for network
|
|
258
|
+
*/
|
|
259
|
+
getExactScheme(network) {
|
|
260
|
+
return this.exactSchemes.get(network);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get deferred scheme verifier for network
|
|
264
|
+
*/
|
|
265
|
+
getDeferredScheme(network) {
|
|
266
|
+
return this.deferredSchemes.get(network);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Get default network
|
|
270
|
+
*/
|
|
271
|
+
getDefaultNetwork() {
|
|
272
|
+
return this.config.defaultNetwork || "base-sepolia";
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
function createX402Service(config) {
|
|
276
|
+
return new X402Service(config);
|
|
277
|
+
}
|
|
278
|
+
function createVerificationService(networks, rpcUrls) {
|
|
279
|
+
return new X402Service({
|
|
280
|
+
deferredEnabled: false,
|
|
281
|
+
rpcUrls
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
285
|
+
0 && (module.exports = {
|
|
286
|
+
DeferredSchemeVerifier,
|
|
287
|
+
ExactSchemeVerifier,
|
|
288
|
+
SUPPORTED_NETWORKS,
|
|
289
|
+
X402Service,
|
|
290
|
+
createDeferredEIP712Domain,
|
|
291
|
+
createExactEIP712Domain,
|
|
292
|
+
createVerificationService,
|
|
293
|
+
createVoucherMessage,
|
|
294
|
+
createVoucherTuple,
|
|
295
|
+
createX402Service,
|
|
296
|
+
generateNonce,
|
|
297
|
+
generateVoucherId,
|
|
298
|
+
getChainIdFromNetwork,
|
|
299
|
+
parseDeferredSignature,
|
|
300
|
+
parseExactSignature
|
|
301
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
SUPPORTED_NETWORKS,
|
|
4
|
+
getChainIdFromNetwork
|
|
5
|
+
} from "@perkos/util-chains";
|
|
6
|
+
import { ExactSchemeVerifier } from "@perkos/scheme-exact";
|
|
7
|
+
import { DeferredSchemeVerifier } from "@perkos/scheme-deferred";
|
|
8
|
+
import {
|
|
9
|
+
SUPPORTED_NETWORKS as SUPPORTED_NETWORKS2,
|
|
10
|
+
getChainIdFromNetwork as getChainIdFromNetwork2
|
|
11
|
+
} from "@perkos/util-chains";
|
|
12
|
+
import {
|
|
13
|
+
ExactSchemeVerifier as ExactSchemeVerifier2,
|
|
14
|
+
parseSignature,
|
|
15
|
+
createEIP712Domain,
|
|
16
|
+
generateNonce
|
|
17
|
+
} from "@perkos/scheme-exact";
|
|
18
|
+
import {
|
|
19
|
+
DeferredSchemeVerifier as DeferredSchemeVerifier2,
|
|
20
|
+
parseSignature as parseSignature2,
|
|
21
|
+
createEIP712Domain as createEIP712Domain2,
|
|
22
|
+
generateVoucherId,
|
|
23
|
+
createVoucherMessage,
|
|
24
|
+
createVoucherTuple
|
|
25
|
+
} from "@perkos/scheme-deferred";
|
|
26
|
+
var CAIP2_TO_LEGACY = {
|
|
27
|
+
// Avalanche
|
|
28
|
+
"eip155:43114": "avalanche",
|
|
29
|
+
"eip155:43113": "avalanche-fuji",
|
|
30
|
+
// Celo
|
|
31
|
+
"eip155:42220": "celo",
|
|
32
|
+
"eip155:11142220": "celo-sepolia",
|
|
33
|
+
// Base
|
|
34
|
+
"eip155:8453": "base",
|
|
35
|
+
"eip155:84532": "base-sepolia",
|
|
36
|
+
// Ethereum
|
|
37
|
+
"eip155:1": "ethereum",
|
|
38
|
+
"eip155:11155111": "sepolia",
|
|
39
|
+
// Polygon
|
|
40
|
+
"eip155:137": "polygon",
|
|
41
|
+
"eip155:80002": "polygon-amoy",
|
|
42
|
+
// Monad
|
|
43
|
+
"eip155:10142": "monad",
|
|
44
|
+
"eip155:10143": "monad-testnet",
|
|
45
|
+
// Arbitrum
|
|
46
|
+
"eip155:42161": "arbitrum",
|
|
47
|
+
"eip155:421614": "arbitrum-sepolia",
|
|
48
|
+
// Optimism
|
|
49
|
+
"eip155:10": "optimism",
|
|
50
|
+
"eip155:11155420": "optimism-sepolia"
|
|
51
|
+
};
|
|
52
|
+
var LEGACY_TO_CAIP2 = Object.fromEntries(
|
|
53
|
+
Object.entries(CAIP2_TO_LEGACY).map(([k, v]) => [v, k])
|
|
54
|
+
);
|
|
55
|
+
var X402Service = class {
|
|
56
|
+
constructor(config = {}) {
|
|
57
|
+
this.exactSchemes = /* @__PURE__ */ new Map();
|
|
58
|
+
this.deferredSchemes = /* @__PURE__ */ new Map();
|
|
59
|
+
this.config = {
|
|
60
|
+
defaultNetwork: "base-sepolia",
|
|
61
|
+
deferredEnabled: false,
|
|
62
|
+
...config
|
|
63
|
+
};
|
|
64
|
+
for (const network of SUPPORTED_NETWORKS) {
|
|
65
|
+
const chainId = getChainIdFromNetwork(network);
|
|
66
|
+
if (chainId) {
|
|
67
|
+
this.exactSchemes.set(
|
|
68
|
+
network,
|
|
69
|
+
new ExactSchemeVerifier({
|
|
70
|
+
network,
|
|
71
|
+
rpcUrl: this.config.rpcUrls?.[network],
|
|
72
|
+
tokenName: chainId ? this.config.tokenNames?.[chainId] : void 0,
|
|
73
|
+
tokenVersion: chainId ? this.config.tokenVersions?.[chainId] : void 0
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (this.config.deferredEnabled && this.config.escrowAddresses) {
|
|
79
|
+
for (const [network, escrowAddress] of Object.entries(this.config.escrowAddresses)) {
|
|
80
|
+
if (escrowAddress && SUPPORTED_NETWORKS.includes(network)) {
|
|
81
|
+
try {
|
|
82
|
+
this.deferredSchemes.set(
|
|
83
|
+
network,
|
|
84
|
+
new DeferredSchemeVerifier({
|
|
85
|
+
network,
|
|
86
|
+
escrowAddress,
|
|
87
|
+
rpcUrl: this.config.rpcUrls?.[network]
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Convert CAIP-2 network format to legacy format
|
|
98
|
+
*/
|
|
99
|
+
caip2ToLegacy(caip2) {
|
|
100
|
+
return CAIP2_TO_LEGACY[caip2] || null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Convert legacy network format to CAIP-2 format
|
|
104
|
+
*/
|
|
105
|
+
legacyToCaip2(network) {
|
|
106
|
+
return LEGACY_TO_CAIP2[network] || null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Normalize network format (CAIP-2 or legacy) to legacy format
|
|
110
|
+
*/
|
|
111
|
+
normalizeNetwork(network) {
|
|
112
|
+
if (SUPPORTED_NETWORKS.includes(network)) {
|
|
113
|
+
return network;
|
|
114
|
+
}
|
|
115
|
+
if (network.includes(":")) {
|
|
116
|
+
return this.caip2ToLegacy(network);
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Verify payment payload
|
|
122
|
+
*/
|
|
123
|
+
async verify(request) {
|
|
124
|
+
const { paymentPayload, paymentRequirements } = request;
|
|
125
|
+
const isV1 = request.x402Version === 1 && paymentPayload.x402Version === 1;
|
|
126
|
+
const isV2 = request.x402Version === 2 && paymentPayload.x402Version === 2;
|
|
127
|
+
if (!isV1 && !isV2) {
|
|
128
|
+
return {
|
|
129
|
+
isValid: false,
|
|
130
|
+
invalidReason: `Unsupported x402 version. Expected 1 or 2, got ${request.x402Version} (payload: ${paymentPayload.x402Version})`,
|
|
131
|
+
payer: null
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const normalizedPayloadNetwork = this.normalizeNetwork(paymentPayload.network);
|
|
135
|
+
const normalizedRequirementsNetwork = this.normalizeNetwork(paymentRequirements.network);
|
|
136
|
+
if (!normalizedPayloadNetwork) {
|
|
137
|
+
return {
|
|
138
|
+
isValid: false,
|
|
139
|
+
invalidReason: `Unsupported network: ${paymentPayload.network}`,
|
|
140
|
+
payer: null
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (!normalizedRequirementsNetwork) {
|
|
144
|
+
return {
|
|
145
|
+
isValid: false,
|
|
146
|
+
invalidReason: `Unsupported network: ${paymentRequirements.network}`,
|
|
147
|
+
payer: null
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
if (normalizedPayloadNetwork !== normalizedRequirementsNetwork) {
|
|
151
|
+
return {
|
|
152
|
+
isValid: false,
|
|
153
|
+
invalidReason: `Network mismatch between payload (${paymentPayload.network}) and requirements (${paymentRequirements.network})`,
|
|
154
|
+
payer: null
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (paymentPayload.scheme !== paymentRequirements.scheme) {
|
|
158
|
+
return {
|
|
159
|
+
isValid: false,
|
|
160
|
+
invalidReason: "Scheme mismatch",
|
|
161
|
+
payer: null
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const network = normalizedPayloadNetwork;
|
|
165
|
+
if (paymentPayload.scheme === "exact") {
|
|
166
|
+
const exactScheme = this.exactSchemes.get(network);
|
|
167
|
+
if (!exactScheme) {
|
|
168
|
+
return {
|
|
169
|
+
isValid: false,
|
|
170
|
+
invalidReason: `Exact scheme not initialized for network: ${network}`,
|
|
171
|
+
payer: null
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return exactScheme.verify(
|
|
175
|
+
paymentPayload.payload,
|
|
176
|
+
paymentRequirements
|
|
177
|
+
);
|
|
178
|
+
} else if (paymentPayload.scheme === "deferred") {
|
|
179
|
+
const deferredScheme = this.deferredSchemes.get(network);
|
|
180
|
+
if (!deferredScheme) {
|
|
181
|
+
return {
|
|
182
|
+
isValid: false,
|
|
183
|
+
invalidReason: `Deferred scheme not enabled for network: ${network}`,
|
|
184
|
+
payer: null
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return deferredScheme.verify(
|
|
188
|
+
paymentPayload.payload,
|
|
189
|
+
paymentRequirements
|
|
190
|
+
);
|
|
191
|
+
} else {
|
|
192
|
+
return {
|
|
193
|
+
isValid: false,
|
|
194
|
+
invalidReason: `Unsupported scheme: ${paymentPayload.scheme}`,
|
|
195
|
+
payer: null
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Verify payment without full request wrapper
|
|
201
|
+
*/
|
|
202
|
+
async verifyPayload(payload, requirements) {
|
|
203
|
+
return this.verify({
|
|
204
|
+
x402Version: payload.x402Version,
|
|
205
|
+
paymentPayload: payload,
|
|
206
|
+
paymentRequirements: requirements
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get supported schemes and networks
|
|
211
|
+
*/
|
|
212
|
+
getSupported() {
|
|
213
|
+
const kinds = [];
|
|
214
|
+
for (const network of this.exactSchemes.keys()) {
|
|
215
|
+
kinds.push({ scheme: "exact", network });
|
|
216
|
+
}
|
|
217
|
+
for (const network of this.deferredSchemes.keys()) {
|
|
218
|
+
kinds.push({ scheme: "deferred", network });
|
|
219
|
+
}
|
|
220
|
+
return { kinds };
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Check if a network is supported
|
|
224
|
+
*/
|
|
225
|
+
isNetworkSupported(network) {
|
|
226
|
+
const normalized = this.normalizeNetwork(network);
|
|
227
|
+
return normalized !== null && this.exactSchemes.has(normalized);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Check if deferred scheme is enabled for a network
|
|
231
|
+
*/
|
|
232
|
+
isDeferredEnabled(network) {
|
|
233
|
+
const normalized = this.normalizeNetwork(network);
|
|
234
|
+
return normalized !== null && this.deferredSchemes.has(normalized);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get exact scheme verifier for network
|
|
238
|
+
*/
|
|
239
|
+
getExactScheme(network) {
|
|
240
|
+
return this.exactSchemes.get(network);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get deferred scheme verifier for network
|
|
244
|
+
*/
|
|
245
|
+
getDeferredScheme(network) {
|
|
246
|
+
return this.deferredSchemes.get(network);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get default network
|
|
250
|
+
*/
|
|
251
|
+
getDefaultNetwork() {
|
|
252
|
+
return this.config.defaultNetwork || "base-sepolia";
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
function createX402Service(config) {
|
|
256
|
+
return new X402Service(config);
|
|
257
|
+
}
|
|
258
|
+
function createVerificationService(networks, rpcUrls) {
|
|
259
|
+
return new X402Service({
|
|
260
|
+
deferredEnabled: false,
|
|
261
|
+
rpcUrls
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
export {
|
|
265
|
+
DeferredSchemeVerifier2 as DeferredSchemeVerifier,
|
|
266
|
+
ExactSchemeVerifier2 as ExactSchemeVerifier,
|
|
267
|
+
SUPPORTED_NETWORKS2 as SUPPORTED_NETWORKS,
|
|
268
|
+
X402Service,
|
|
269
|
+
createEIP712Domain2 as createDeferredEIP712Domain,
|
|
270
|
+
createEIP712Domain as createExactEIP712Domain,
|
|
271
|
+
createVerificationService,
|
|
272
|
+
createVoucherMessage,
|
|
273
|
+
createVoucherTuple,
|
|
274
|
+
createX402Service,
|
|
275
|
+
generateNonce,
|
|
276
|
+
generateVoucherId,
|
|
277
|
+
getChainIdFromNetwork2 as getChainIdFromNetwork,
|
|
278
|
+
parseSignature2 as parseDeferredSignature,
|
|
279
|
+
parseSignature as parseExactSignature
|
|
280
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@perkos/service-x402",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Unified x402 payment protocol verification and settlement service",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"x402",
|
|
24
|
+
"payment",
|
|
25
|
+
"protocol",
|
|
26
|
+
"verification",
|
|
27
|
+
"settlement",
|
|
28
|
+
"ethereum",
|
|
29
|
+
"web3",
|
|
30
|
+
"crypto"
|
|
31
|
+
],
|
|
32
|
+
"author": "PerkOS",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/PerkOS-xyz/pkg-service-x402"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@perkos/types-x402": "^1.0.0",
|
|
40
|
+
"@perkos/util-chains": "^1.0.0",
|
|
41
|
+
"@perkos/scheme-exact": "^1.0.0",
|
|
42
|
+
"@perkos/scheme-deferred": "^1.0.0",
|
|
43
|
+
"viem": "^2.28.2"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"tsup": "^8.5.0",
|
|
47
|
+
"typescript": "^5.8.3"
|
|
48
|
+
}
|
|
49
|
+
}
|