@dexterai/x402 1.9.1 → 1.9.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.
- package/README.md +12 -0
- package/dist/server/index.cjs +125 -109
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +7 -6
- package/dist/server/index.d.ts +7 -6
- package/dist/server/index.js +125 -109
- package/dist/server/index.js.map +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -77,10 +77,22 @@ The simplest way to make x402 payments from scripts:
|
|
|
77
77
|
```typescript
|
|
78
78
|
import { wrapFetch } from '@dexterai/x402/client';
|
|
79
79
|
|
|
80
|
+
// Solana
|
|
80
81
|
const x402Fetch = wrapFetch(fetch, {
|
|
81
82
|
walletPrivateKey: process.env.SOLANA_PRIVATE_KEY,
|
|
82
83
|
});
|
|
83
84
|
|
|
85
|
+
// EVM (Base, Polygon, Arbitrum, Optimism, Avalanche, SKALE)
|
|
86
|
+
const x402Fetch = wrapFetch(fetch, {
|
|
87
|
+
evmPrivateKey: process.env.EVM_PRIVATE_KEY, // requires: npm install viem
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Both — SDK picks the chain with balance
|
|
91
|
+
const x402Fetch = wrapFetch(fetch, {
|
|
92
|
+
walletPrivateKey: process.env.SOLANA_PRIVATE_KEY,
|
|
93
|
+
evmPrivateKey: process.env.EVM_PRIVATE_KEY,
|
|
94
|
+
});
|
|
95
|
+
|
|
84
96
|
// That's it. 402 responses are handled automatically.
|
|
85
97
|
const response = await x402Fetch('https://api.example.com/protected');
|
|
86
98
|
```
|
package/dist/server/index.cjs
CHANGED
|
@@ -431,9 +431,104 @@ function createX402Server(config) {
|
|
|
431
431
|
};
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
// src/server/stripe-payto.ts
|
|
435
|
+
var stripeProviderNetworks = /* @__PURE__ */ new WeakMap();
|
|
436
|
+
function getStripeProviderNetwork(provider) {
|
|
437
|
+
return stripeProviderNetworks.get(provider);
|
|
438
|
+
}
|
|
439
|
+
var STRIPE_NETWORK_KEYS = {
|
|
440
|
+
"base": "base",
|
|
441
|
+
"base-sepolia": "base_sepolia"
|
|
442
|
+
};
|
|
443
|
+
var CAIP2_NETWORKS = {
|
|
444
|
+
"base": "eip155:8453",
|
|
445
|
+
"base-sepolia": "eip155:84532"
|
|
446
|
+
};
|
|
447
|
+
var USDC_DECIMALS = 6;
|
|
448
|
+
function stripePayTo(secretKeyOrConfig) {
|
|
449
|
+
const config = typeof secretKeyOrConfig === "string" ? { secretKey: secretKeyOrConfig } : secretKeyOrConfig;
|
|
450
|
+
const networkName = config.network ?? "base";
|
|
451
|
+
const stripeNetworkKey = STRIPE_NETWORK_KEYS[networkName] ?? "base";
|
|
452
|
+
const caip2Network = CAIP2_NETWORKS[networkName] ?? "eip155:8453";
|
|
453
|
+
const apiVersion = config.apiVersion ?? "2026-01-28.clover";
|
|
454
|
+
let stripeClient = null;
|
|
455
|
+
async function getStripe() {
|
|
456
|
+
if (stripeClient) return stripeClient;
|
|
457
|
+
try {
|
|
458
|
+
const { default: Stripe } = await import("stripe");
|
|
459
|
+
stripeClient = new Stripe(config.secretKey, {
|
|
460
|
+
apiVersion,
|
|
461
|
+
appInfo: {
|
|
462
|
+
name: "@dexterai/x402",
|
|
463
|
+
url: "https://dexter.cash/sdk"
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
return stripeClient;
|
|
467
|
+
} catch {
|
|
468
|
+
throw new Error(
|
|
469
|
+
'The "stripe" package is required for stripePayTo(). Install it with: npm install stripe'
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
const provider = async (context) => {
|
|
474
|
+
if (context.paymentHeader) {
|
|
475
|
+
try {
|
|
476
|
+
const decoded = JSON.parse(
|
|
477
|
+
Buffer.from(context.paymentHeader, "base64").toString()
|
|
478
|
+
);
|
|
479
|
+
const toAddress = decoded.payload?.authorization?.to;
|
|
480
|
+
if (toAddress && typeof toAddress === "string") {
|
|
481
|
+
return toAddress;
|
|
482
|
+
}
|
|
483
|
+
const acceptedPayTo = decoded.accepted?.payTo;
|
|
484
|
+
if (acceptedPayTo && typeof acceptedPayTo === "string") {
|
|
485
|
+
return acceptedPayTo;
|
|
486
|
+
}
|
|
487
|
+
} catch {
|
|
488
|
+
}
|
|
489
|
+
throw new Error(
|
|
490
|
+
"Could not extract deposit address from payment header. Ensure the client is sending a valid x402 PAYMENT-SIGNATURE."
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
const stripe = await getStripe();
|
|
494
|
+
const amountAtomic = context.amountAtomic ? parseInt(context.amountAtomic, 10) : 1e4;
|
|
495
|
+
const amountInCents = Math.max(1, Math.round(amountAtomic / Math.pow(10, USDC_DECIMALS - 2)));
|
|
496
|
+
const paymentIntent = await stripe.paymentIntents.create({
|
|
497
|
+
amount: amountInCents,
|
|
498
|
+
currency: "usd",
|
|
499
|
+
payment_method_types: ["crypto"],
|
|
500
|
+
payment_method_data: { type: "crypto" },
|
|
501
|
+
payment_method_options: {
|
|
502
|
+
crypto: { mode: "custom" }
|
|
503
|
+
},
|
|
504
|
+
confirm: true
|
|
505
|
+
});
|
|
506
|
+
const nextAction = paymentIntent.next_action;
|
|
507
|
+
if (!nextAction?.crypto_collect_deposit_details) {
|
|
508
|
+
throw new Error(
|
|
509
|
+
"Stripe PaymentIntent did not return crypto deposit details. Ensure your Stripe account has crypto payins enabled: https://support.stripe.com/questions/get-started-with-pay-with-crypto"
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
const depositDetails = nextAction.crypto_collect_deposit_details;
|
|
513
|
+
const payToAddress = depositDetails.deposit_addresses?.[stripeNetworkKey]?.address;
|
|
514
|
+
if (!payToAddress) {
|
|
515
|
+
throw new Error(
|
|
516
|
+
`No deposit address found for network "${stripeNetworkKey}". Available networks: ${Object.keys(depositDetails.deposit_addresses || {}).join(", ")}`
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
return payToAddress;
|
|
520
|
+
};
|
|
521
|
+
provider._x402Defaults = {
|
|
522
|
+
network: caip2Network,
|
|
523
|
+
facilitatorUrl: "https://x402.dexter.cash"
|
|
524
|
+
};
|
|
525
|
+
stripeProviderNetworks.set(provider, caip2Network);
|
|
526
|
+
return provider;
|
|
527
|
+
}
|
|
528
|
+
|
|
434
529
|
// src/server/middleware.ts
|
|
435
530
|
var DEFAULT_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
436
|
-
var
|
|
531
|
+
var USDC_DECIMALS2 = 6;
|
|
437
532
|
function resolvePayToForNetwork(payTo, network) {
|
|
438
533
|
if (typeof payTo === "string" || typeof payTo === "function") return payTo;
|
|
439
534
|
if (network in payTo) return payTo[network];
|
|
@@ -471,9 +566,9 @@ function x402Middleware(config) {
|
|
|
471
566
|
const servers = /* @__PURE__ */ new Map();
|
|
472
567
|
for (const net of configuredNetworks) {
|
|
473
568
|
const netPayTo = resolvePayToForNetwork(payTo, net);
|
|
474
|
-
if (typeof netPayTo === "function"
|
|
475
|
-
const stripeNet = netPayTo
|
|
476
|
-
if (net !== stripeNet) {
|
|
569
|
+
if (typeof netPayTo === "function") {
|
|
570
|
+
const stripeNet = getStripeProviderNetwork(netPayTo);
|
|
571
|
+
if (stripeNet && net !== stripeNet) {
|
|
477
572
|
throw new Error(
|
|
478
573
|
`stripePayTo is configured for "${stripeNet}" but middleware includes network "${net}". Stripe only supports Base deposit addresses. Use a static payTo for other chains.`
|
|
479
574
|
);
|
|
@@ -496,7 +591,7 @@ function x402Middleware(config) {
|
|
|
496
591
|
const resourceUrl = getResourceUrl?.(req) ?? staticResourceUrl ?? `${req.protocol}://${req.get("host")}${req.originalUrl}`;
|
|
497
592
|
const requestAmount = getAmount?.(req) ?? amount;
|
|
498
593
|
const requestDescription = getDescription?.(req) ?? description;
|
|
499
|
-
const decimals = asset?.decimals ??
|
|
594
|
+
const decimals = asset?.decimals ?? USDC_DECIMALS2;
|
|
500
595
|
const amountAtomic = toAtomicUnits(parseFloat(requestAmount), decimals);
|
|
501
596
|
const requirementsOptions = {
|
|
502
597
|
amountAtomic,
|
|
@@ -1017,7 +1112,7 @@ function verifyJwt(token, secret) {
|
|
|
1017
1112
|
}
|
|
1018
1113
|
}
|
|
1019
1114
|
var DEFAULT_NETWORK2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
1020
|
-
var
|
|
1115
|
+
var USDC_DECIMALS3 = 6;
|
|
1021
1116
|
function x402AccessPass(config) {
|
|
1022
1117
|
const {
|
|
1023
1118
|
payTo,
|
|
@@ -1040,7 +1135,7 @@ function x402AccessPass(config) {
|
|
|
1040
1135
|
}
|
|
1041
1136
|
const log = verbose ? console.log.bind(console, "[x402:access-pass]") : () => {
|
|
1042
1137
|
};
|
|
1043
|
-
const decimals = asset?.decimals ??
|
|
1138
|
+
const decimals = asset?.decimals ?? USDC_DECIMALS3;
|
|
1044
1139
|
const builtTiers = [];
|
|
1045
1140
|
if (tierPrices) {
|
|
1046
1141
|
for (const [id, price] of Object.entries(tierPrices)) {
|
|
@@ -1304,7 +1399,6 @@ function formatPricing(config) {
|
|
|
1304
1399
|
|
|
1305
1400
|
// src/server/token-pricing.ts
|
|
1306
1401
|
var import_crypto3 = require("crypto");
|
|
1307
|
-
var import_tiktoken = require("tiktoken");
|
|
1308
1402
|
|
|
1309
1403
|
// src/server/model-registry.ts
|
|
1310
1404
|
var STANDARD_PARAMS = {
|
|
@@ -1881,17 +1975,30 @@ var MODEL_PRICING_MAP = Object.fromEntries(
|
|
|
1881
1975
|
);
|
|
1882
1976
|
|
|
1883
1977
|
// src/server/token-pricing.ts
|
|
1978
|
+
var _tiktoken = null;
|
|
1979
|
+
async function loadTiktoken() {
|
|
1980
|
+
if (_tiktoken) return _tiktoken;
|
|
1981
|
+
try {
|
|
1982
|
+
_tiktoken = await import("tiktoken");
|
|
1983
|
+
return _tiktoken;
|
|
1984
|
+
} catch {
|
|
1985
|
+
throw new Error(
|
|
1986
|
+
'Token pricing requires the "tiktoken" package. Install with: npm install tiktoken'
|
|
1987
|
+
);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1884
1990
|
var MODEL_PRICING = MODEL_PRICING_MAP;
|
|
1885
1991
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
1886
|
-
function getEncodingForModel(model) {
|
|
1992
|
+
async function getEncodingForModel(model) {
|
|
1993
|
+
const tiktoken = await loadTiktoken();
|
|
1887
1994
|
try {
|
|
1888
|
-
return
|
|
1995
|
+
return tiktoken.encoding_for_model(model);
|
|
1889
1996
|
} catch {
|
|
1890
|
-
return
|
|
1997
|
+
return tiktoken.get_encoding("cl100k_base");
|
|
1891
1998
|
}
|
|
1892
1999
|
}
|
|
1893
|
-
function countTokens(text, model = DEFAULT_MODEL) {
|
|
1894
|
-
const encoding = getEncodingForModel(model);
|
|
2000
|
+
async function countTokens(text, model = DEFAULT_MODEL) {
|
|
2001
|
+
const encoding = await getEncodingForModel(model);
|
|
1895
2002
|
try {
|
|
1896
2003
|
const tokens = encoding.encode(text);
|
|
1897
2004
|
return tokens.length;
|
|
@@ -1925,17 +2032,17 @@ function createTokenPricing(config = {}) {
|
|
|
1925
2032
|
decimals: config.decimals ?? 6
|
|
1926
2033
|
};
|
|
1927
2034
|
const { minUsd, maxUsd, decimals } = fullConfig;
|
|
1928
|
-
function countTokensInternal(input) {
|
|
2035
|
+
async function countTokensInternal(input) {
|
|
1929
2036
|
if (customTokenizer) {
|
|
1930
2037
|
return customTokenizer(input);
|
|
1931
2038
|
}
|
|
1932
2039
|
return countTokens(input, model);
|
|
1933
2040
|
}
|
|
1934
|
-
function calculate(input, systemPrompt) {
|
|
2041
|
+
async function calculate(input, systemPrompt) {
|
|
1935
2042
|
const fullInput = systemPrompt ? `${systemPrompt}
|
|
1936
2043
|
|
|
1937
2044
|
${input}` : input;
|
|
1938
|
-
const inputTokens = countTokensInternal(fullInput);
|
|
2045
|
+
const inputTokens = await countTokensInternal(fullInput);
|
|
1939
2046
|
let usdAmount = inputTokens / 1e6 * modelInfo.input;
|
|
1940
2047
|
usdAmount = Math.max(usdAmount, minUsd);
|
|
1941
2048
|
usdAmount = Math.min(usdAmount, maxUsd);
|
|
@@ -1954,9 +2061,9 @@ ${input}` : input;
|
|
|
1954
2061
|
quoteHash
|
|
1955
2062
|
};
|
|
1956
2063
|
}
|
|
1957
|
-
function validateQuote(input, quoteHash) {
|
|
2064
|
+
async function validateQuote(input, quoteHash) {
|
|
1958
2065
|
if (!quoteHash) return false;
|
|
1959
|
-
const inputTokens = countTokensInternal(input);
|
|
2066
|
+
const inputTokens = await countTokensInternal(input);
|
|
1960
2067
|
const expectedHash = generateQuoteHash(input, model, modelInfo.input, inputTokens);
|
|
1961
2068
|
return expectedHash === quoteHash;
|
|
1962
2069
|
}
|
|
@@ -1991,97 +2098,6 @@ function formatTokenPricing(model = DEFAULT_MODEL) {
|
|
|
1991
2098
|
return `$${pricing.input.toFixed(2)} per 1M tokens (${actualModel})`;
|
|
1992
2099
|
}
|
|
1993
2100
|
|
|
1994
|
-
// src/server/stripe-payto.ts
|
|
1995
|
-
var STRIPE_NETWORK_KEYS = {
|
|
1996
|
-
"base": "base",
|
|
1997
|
-
"base-sepolia": "base_sepolia"
|
|
1998
|
-
};
|
|
1999
|
-
var CAIP2_NETWORKS = {
|
|
2000
|
-
"base": "eip155:8453",
|
|
2001
|
-
"base-sepolia": "eip155:84532"
|
|
2002
|
-
};
|
|
2003
|
-
var USDC_DECIMALS3 = 6;
|
|
2004
|
-
function stripePayTo(secretKeyOrConfig) {
|
|
2005
|
-
const config = typeof secretKeyOrConfig === "string" ? { secretKey: secretKeyOrConfig } : secretKeyOrConfig;
|
|
2006
|
-
const networkName = config.network ?? "base";
|
|
2007
|
-
const stripeNetworkKey = STRIPE_NETWORK_KEYS[networkName] ?? "base";
|
|
2008
|
-
const caip2Network = CAIP2_NETWORKS[networkName] ?? "eip155:8453";
|
|
2009
|
-
const apiVersion = config.apiVersion ?? "2026-01-28.clover";
|
|
2010
|
-
let stripeClient = null;
|
|
2011
|
-
async function getStripe() {
|
|
2012
|
-
if (stripeClient) return stripeClient;
|
|
2013
|
-
try {
|
|
2014
|
-
const { default: Stripe } = await import("stripe");
|
|
2015
|
-
stripeClient = new Stripe(config.secretKey, {
|
|
2016
|
-
apiVersion,
|
|
2017
|
-
appInfo: {
|
|
2018
|
-
name: "@dexterai/x402",
|
|
2019
|
-
url: "https://dexter.cash/sdk"
|
|
2020
|
-
}
|
|
2021
|
-
});
|
|
2022
|
-
return stripeClient;
|
|
2023
|
-
} catch {
|
|
2024
|
-
throw new Error(
|
|
2025
|
-
'The "stripe" package is required for stripePayTo(). Install it with: npm install stripe'
|
|
2026
|
-
);
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
const provider = async (context) => {
|
|
2030
|
-
if (context.paymentHeader) {
|
|
2031
|
-
try {
|
|
2032
|
-
const decoded = JSON.parse(
|
|
2033
|
-
Buffer.from(context.paymentHeader, "base64").toString()
|
|
2034
|
-
);
|
|
2035
|
-
const toAddress = decoded.payload?.authorization?.to;
|
|
2036
|
-
if (toAddress && typeof toAddress === "string") {
|
|
2037
|
-
return toAddress;
|
|
2038
|
-
}
|
|
2039
|
-
const acceptedPayTo = decoded.accepted?.payTo;
|
|
2040
|
-
if (acceptedPayTo && typeof acceptedPayTo === "string") {
|
|
2041
|
-
return acceptedPayTo;
|
|
2042
|
-
}
|
|
2043
|
-
} catch {
|
|
2044
|
-
}
|
|
2045
|
-
throw new Error(
|
|
2046
|
-
"Could not extract deposit address from payment header. Ensure the client is sending a valid x402 PAYMENT-SIGNATURE."
|
|
2047
|
-
);
|
|
2048
|
-
}
|
|
2049
|
-
const stripe = await getStripe();
|
|
2050
|
-
const amountAtomic = context.amountAtomic ? parseInt(context.amountAtomic, 10) : 1e4;
|
|
2051
|
-
const amountInCents = Math.max(1, Math.round(amountAtomic / Math.pow(10, USDC_DECIMALS3 - 2)));
|
|
2052
|
-
const paymentIntent = await stripe.paymentIntents.create({
|
|
2053
|
-
amount: amountInCents,
|
|
2054
|
-
currency: "usd",
|
|
2055
|
-
payment_method_types: ["crypto"],
|
|
2056
|
-
payment_method_data: { type: "crypto" },
|
|
2057
|
-
payment_method_options: {
|
|
2058
|
-
crypto: { mode: "custom" }
|
|
2059
|
-
},
|
|
2060
|
-
confirm: true
|
|
2061
|
-
});
|
|
2062
|
-
const nextAction = paymentIntent.next_action;
|
|
2063
|
-
if (!nextAction?.crypto_collect_deposit_details) {
|
|
2064
|
-
throw new Error(
|
|
2065
|
-
"Stripe PaymentIntent did not return crypto deposit details. Ensure your Stripe account has crypto payins enabled: https://support.stripe.com/questions/get-started-with-pay-with-crypto"
|
|
2066
|
-
);
|
|
2067
|
-
}
|
|
2068
|
-
const depositDetails = nextAction.crypto_collect_deposit_details;
|
|
2069
|
-
const payToAddress = depositDetails.deposit_addresses?.[stripeNetworkKey]?.address;
|
|
2070
|
-
if (!payToAddress) {
|
|
2071
|
-
throw new Error(
|
|
2072
|
-
`No deposit address found for network "${stripeNetworkKey}". Available networks: ${Object.keys(depositDetails.deposit_addresses || {}).join(", ")}`
|
|
2073
|
-
);
|
|
2074
|
-
}
|
|
2075
|
-
return payToAddress;
|
|
2076
|
-
};
|
|
2077
|
-
provider._x402Defaults = {
|
|
2078
|
-
network: caip2Network,
|
|
2079
|
-
facilitatorUrl: "https://x402.dexter.cash"
|
|
2080
|
-
};
|
|
2081
|
-
provider._stripeNetwork = caip2Network;
|
|
2082
|
-
return provider;
|
|
2083
|
-
}
|
|
2084
|
-
|
|
2085
2101
|
// src/server/index.ts
|
|
2086
2102
|
var import_x402_ads_types = require("@dexterai/x402-ads-types");
|
|
2087
2103
|
// Annotate the CommonJS export names for ESM import in node:
|