mirror of
https://github.com/Permissionless-Software-Foundation/psf-bch-api-base.git
synced 2026-09-21 16:52:00 -07:00
feat(x402): Updated to x402 V2
This commit is contained in:
+40
-31
@@ -7,7 +7,9 @@
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
import dotenv from 'dotenv'
|
||||
import { paymentMiddleware as x402PaymentMiddleware } from 'x402-express'
|
||||
import { paymentMiddleware as x402PaymentMiddleware } from '@x402/express'
|
||||
import { HTTPFacilitatorClient, x402ResourceServer } from '@x402/core/server'
|
||||
import { registerExactEvmScheme } from '@x402/evm/exact/server'
|
||||
|
||||
import { fileURLToPath } from 'url'
|
||||
import { dirname, join } from 'path'
|
||||
@@ -74,7 +76,22 @@ class Server {
|
||||
origin: true, // Allow all origins (more reliable than '*')
|
||||
credentials: false, // Set to true if you need to support credentials
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
|
||||
allowedHeaders: [
|
||||
'Content-Type',
|
||||
'Authorization',
|
||||
'X-Requested-With',
|
||||
'PAYMENT-SIGNATURE',
|
||||
'PAYMENT-REQUIRED',
|
||||
'PAYMENT-RESPONSE',
|
||||
'X-PAYMENT',
|
||||
'X-PAYMENT-RESPONSE'
|
||||
],
|
||||
exposedHeaders: [
|
||||
'PAYMENT-REQUIRED',
|
||||
'PAYMENT-RESPONSE',
|
||||
'PAYMENT-SIGNATURE',
|
||||
'X-PAYMENT-RESPONSE'
|
||||
]
|
||||
}))
|
||||
|
||||
// URL normalization middleware - collapse multiple slashes
|
||||
@@ -108,54 +125,46 @@ class Server {
|
||||
if (x402Settings.enabled && basicAuthSettings.enabled) {
|
||||
// X402_ENABLED=true AND USE_BASIC_AUTH=true: Apply x402 conditionally
|
||||
const routes = buildX402Routes(this.config.apiPrefix)
|
||||
let facilitatorOptions = x402Settings.facilitatorUrl
|
||||
|
||||
if (facilitatorOptions) {
|
||||
facilitatorOptions = {
|
||||
const facilitatorOptions = x402Settings.facilitatorUrl
|
||||
? {
|
||||
url: x402Settings.facilitatorUrl,
|
||||
createAuthHeaders: createAuthHeader
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
|
||||
wlogger.info(`x402 middleware enabled with basic auth bypass; enforcing ${x402Settings.priceUSDC} satoshis per request (unless basic auth provided)`)
|
||||
wlogger.info(`x402 v2 middleware enabled with basic auth bypass; enforcing ${x402Settings.priceUSDC} USDC per request (unless basic auth provided)`)
|
||||
|
||||
const facilitatorClient = new HTTPFacilitatorClient(facilitatorOptions)
|
||||
// x402 v2 exports use lowercase class names (x402ResourceServer).
|
||||
// eslint-disable-next-line new-cap
|
||||
const resourceServer = new x402ResourceServer(facilitatorClient)
|
||||
registerExactEvmScheme(resourceServer, {})
|
||||
const x402Mw = x402PaymentMiddleware(routes, resourceServer)
|
||||
|
||||
// Create conditional x402 middleware that bypasses if basic auth is valid
|
||||
const conditionalX402Middleware = (req, res, next) => {
|
||||
// req.headers['accept'] = 'application/json';
|
||||
// If basic auth is valid, bypass x402
|
||||
if (req.locals?.basicAuthValid === true) {
|
||||
return next()
|
||||
}
|
||||
|
||||
// Otherwise, apply x402 middleware
|
||||
return x402PaymentMiddleware(
|
||||
x402Settings.serverAddress,
|
||||
routes,
|
||||
facilitatorOptions
|
||||
)(req, res, next)
|
||||
return x402Mw(req, res, next)
|
||||
}
|
||||
|
||||
app.use(conditionalX402Middleware)
|
||||
} else if (x402Settings.enabled && !basicAuthSettings.enabled) {
|
||||
// X402_ENABLED=true AND USE_BASIC_AUTH=false: Apply x402 unconditionally (no basic auth bypass)
|
||||
const routes = buildX402Routes(this.config.apiPrefix)
|
||||
let facilitatorOptions = x402Settings.facilitatorUrl
|
||||
|
||||
if (facilitatorOptions) {
|
||||
facilitatorOptions = {
|
||||
const facilitatorOptions = x402Settings.facilitatorUrl
|
||||
? {
|
||||
url: x402Settings.facilitatorUrl,
|
||||
createAuthHeaders: createAuthHeader
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
|
||||
wlogger.info(`x402 middleware enabled (basic auth disabled); enforcing ${x402Settings.priceUSDC} satoshis per request`)
|
||||
wlogger.info(`x402 v2 middleware enabled (basic auth disabled); enforcing ${x402Settings.priceUSDC} USDC per request`)
|
||||
|
||||
// Apply x402 middleware unconditionally - no basic auth bypass
|
||||
app.use(x402PaymentMiddleware(
|
||||
x402Settings.serverAddress,
|
||||
routes,
|
||||
facilitatorOptions
|
||||
))
|
||||
const facilitatorClient = new HTTPFacilitatorClient(facilitatorOptions)
|
||||
// eslint-disable-next-line new-cap
|
||||
const resourceServer = new x402ResourceServer(facilitatorClient)
|
||||
registerExactEvmScheme(resourceServer, {})
|
||||
app.use(x402PaymentMiddleware(routes, resourceServer))
|
||||
} else if (basicAuthSettings.enabled && !x402Settings.enabled) {
|
||||
// USE_BASIC_AUTH=true AND X402_ENABLED=false: Require basic auth, reject unauthenticated requests
|
||||
wlogger.info('Basic auth enforcement enabled (x402 disabled)')
|
||||
|
||||
@@ -1,38 +1,58 @@
|
||||
import axios from 'axios'
|
||||
import { withPaymentInterceptor } from 'x402-axios'
|
||||
/**
|
||||
* x402 v2 axios example — USDC on Base (CAIP-2). Run from repo root:
|
||||
* `node examples/01-x402-axios-client.js`
|
||||
* The x402 stack pulls in `siwe`, which expects `ethers` as a peer — it is listed in package.json.
|
||||
*
|
||||
* Uses `toClientEvmSigner(account, publicClient)` so the signer has `readContract` (required by
|
||||
* @x402/evm ClientEvmSigner). Optional: `BASE_RPC_URL` for Base / Base Sepolia HTTP RPC.
|
||||
*/
|
||||
import axios, { AxiosHeaders } from 'axios'
|
||||
|
||||
import { createPublicClient, http } from 'viem'
|
||||
import { base, baseSepolia } from 'viem/chains'
|
||||
import { privateKeyToAccount } from 'viem/accounts'
|
||||
import { base } from 'viem/chains'
|
||||
// import { baseSepolia } from 'viem/chains' // Testnet
|
||||
import { x402Client, wrapAxiosWithPayment } from '@x402/axios'
|
||||
import { registerExactEvmScheme } from '@x402/evm/exact/client'
|
||||
import { toClientEvmSigner } from '@x402/evm'
|
||||
|
||||
import { createWalletClient, http } from 'viem'
|
||||
|
||||
// const baseURL = 'http://localhost:5942' // Local
|
||||
const baseURL = 'https://x402.fullstack.cash' // Production
|
||||
const baseURL = 'https://localhost:5942' // Local
|
||||
// const baseURL = 'https://x402.fullstack.cash' // Production
|
||||
|
||||
const endpointPath = '/v6/full-node/blockchain/getBlockchainInfo'
|
||||
const pKey = process.env.PRIVATE_KEY || ''
|
||||
const pKey = process.env.PRIVATE_KEY || process.env.EVM_PRIVATE_KEY || ''
|
||||
|
||||
const x402Network = 'eip155:8453' // sepolia eip155:84532
|
||||
|
||||
if (!pKey) throw new Error('PRIVATE_KEY or EVM_PRIVATE_KEY env required!')
|
||||
|
||||
// PRIVATE_KEY=0x... node examples/08-x402-axios-client.js
|
||||
if (!pKey) throw new Error('PRIVATE_KEY env required!')
|
||||
console.log('pKey', pKey)
|
||||
// Create a wallet client
|
||||
const account = privateKeyToAccount(pKey)
|
||||
const client = createWalletClient({
|
||||
account,
|
||||
transport: http(),
|
||||
chain: base
|
||||
const chain = x402Network === 'eip155:84532' ? baseSepolia : base
|
||||
const defaultRpc =
|
||||
x402Network === 'eip155:84532'
|
||||
? 'https://sepolia.base.org'
|
||||
: 'https://mainnet.base.org'
|
||||
const publicClient = createPublicClient({
|
||||
chain,
|
||||
transport: http(process.env.BASE_RPC_URL || defaultRpc)
|
||||
})
|
||||
const signer = toClientEvmSigner(account, publicClient)
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
const client = new x402Client()
|
||||
registerExactEvmScheme(client, {
|
||||
signer,
|
||||
networks: [x402Network]
|
||||
})
|
||||
|
||||
const api = withPaymentInterceptor(
|
||||
const api = wrapAxiosWithPayment(
|
||||
axios.create({
|
||||
baseURL,
|
||||
headers: {
|
||||
headers: new AxiosHeaders({
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
}),
|
||||
client
|
||||
)
|
||||
|
||||
const request = async () => {
|
||||
console.log('\n\nStep 1: Making first call, expecting a 402 error returned.')
|
||||
try {
|
||||
@@ -42,6 +62,10 @@ const request = async () => {
|
||||
} catch (err) {
|
||||
console.log(`Status code: ${err?.response?.status}`)
|
||||
console.log(`Error data: ${JSON.stringify(err.response.data, null, 2)}`)
|
||||
console.log(
|
||||
`Error StatusText: ${JSON.stringify(err.response.statusText, null, 2)}`
|
||||
)
|
||||
|
||||
console.log('\n\n')
|
||||
}
|
||||
|
||||
@@ -54,6 +78,9 @@ const request = async () => {
|
||||
} catch (err) {
|
||||
console.log('Step 2 failed. Expected a 200 success status code.')
|
||||
console.log(`Status code: ${err?.response?.status}`)
|
||||
console.log(
|
||||
`Error StatusText: ${JSON.stringify(err.response.statusText, null, 2)}`
|
||||
)
|
||||
console.log(`Error data: ${JSON.stringify(err.response.data, null, 2)}`)
|
||||
|
||||
process.exit(1)
|
||||
|
||||
Generated
+73
-4671
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -19,20 +19,20 @@
|
||||
"dependencies": {
|
||||
"@coinbase/cdp-sdk": "1.45.0",
|
||||
"@psf/bch-js": "7.1.14",
|
||||
"@x402/axios": "2.6.0",
|
||||
"@x402/core": "2.6.0",
|
||||
"@x402/evm": "2.6.0",
|
||||
"@x402/express": "2.6.0",
|
||||
"axios": "1.7.7",
|
||||
"cors": "2.8.5",
|
||||
"dotenv": "16.3.1",
|
||||
"ethers": "6.13.5",
|
||||
"express": "5.1.0",
|
||||
"minimal-slp-wallet": "7.1.5",
|
||||
"psffpp": "1.2.1",
|
||||
"slp-token-media": "1.2.10",
|
||||
"winston": "3.11.0",
|
||||
"winston-daily-rotate-file": "4.7.1",
|
||||
"x402-axios": "1.1.0",
|
||||
"x402-express": "1.1.0",
|
||||
"x402-fetch": "1.1.0"
|
||||
"winston-daily-rotate-file": "4.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"apidoc": "1.2.0",
|
||||
|
||||
Vendored
+23
-8
@@ -26,26 +26,41 @@ const normalizeBoolean = (value, defaultValue) => {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// By default, the price per API call is 200 satoshis.
|
||||
// But the user can override this value by setting the X402_PRICE_SAT environment variable.
|
||||
// Default price per API call in USDC (x402 on Base). Override with X402_PRICE_USDC.
|
||||
const parsedPrice = Number(process.env.X402_PRICE_USDC)
|
||||
const priceUSDC = Number.isFinite(parsedPrice) && parsedPrice > 0 ? parsedPrice : 200
|
||||
|
||||
// Network configuration: CDP requires CAIP-2 format (eip155:chainId)
|
||||
// base-sepolia = eip155:84532, base-mainnet = eip155:8453
|
||||
const x402Network = process.env.x402_NETWORK || 'eip155:8453'
|
||||
// x402 v2 uses CAIP-2 network ids (see https://docs.x402.org/guides/migration-v1-to-v2).
|
||||
// Accept legacy short names from env for convenience. Also accept X402_NETWORK or x402_NETWORK.
|
||||
function toV2Caip2Network (raw) {
|
||||
const s = String(raw ?? '').trim()
|
||||
if (!s) return 'eip155:8453'
|
||||
const lower = s.toLowerCase()
|
||||
if (lower === 'base' || lower === 'eip155:8453') return 'eip155:8453'
|
||||
if (lower === 'base-sepolia' || lower === 'eip155:84532') return 'eip155:84532'
|
||||
if (lower.startsWith('eip155:')) return s
|
||||
return s
|
||||
}
|
||||
|
||||
const x402NetworkRaw =
|
||||
process.env.x402_NETWORK ||
|
||||
process.env.X402_NETWORK ||
|
||||
'eip155:8453'
|
||||
const x402Network = toV2Caip2Network(x402NetworkRaw)
|
||||
|
||||
const x402Defaults = {
|
||||
enabled: normalizeBoolean(process.env.X402_ENABLED, true),
|
||||
// CDP Facilitator: https://api.cdp.coinbase.com/platform/v2/x402
|
||||
// Custom/Local Facilitator: http://localhost:4022
|
||||
facilitatorUrl: process.env.x402_FACILITATOR_URL || 'https://api.cdp.coinbase.com/platform/v2/x402',
|
||||
serverAddress: process.env.SERVER_BASE_ADDRESS || 'bitcoincash:qqsrke9lh257tqen99dkyy2emh4uty0vky9y0z0lsr',
|
||||
// EVM 0x… address for USDC settlement; required for x402 exact scheme (no legacy BCH default).
|
||||
serverAddress: (process.env.SERVER_BASE_ADDRESS || '').trim(),
|
||||
facilitatorKeyId: process.env.FACILITATOR_KEY_ID || '',
|
||||
facilitatorSecretKey: process.env.FACILITATOR_SECRET_KEY || '',
|
||||
// CDP requires CAIP-2 network format: eip155:8453 (base) or eip155:84532 (base-sepolia)
|
||||
network: x402Network,
|
||||
priceUSDC
|
||||
priceUSDC,
|
||||
// Optional: USDC token contract (0x…). If unset, Base / Base Sepolia use built-in USDC addresses.
|
||||
usdcAssetAddress: (process.env.X402_USDC_ASSET || '').trim()
|
||||
}
|
||||
|
||||
const basicAuthDefaults = {
|
||||
|
||||
+131
-34
@@ -5,10 +5,23 @@ const DEFAULT_DESCRIPTION = 'Access to protected psf-bch-api resources'
|
||||
const DEFAULT_TIMEOUT_SECONDS = 120
|
||||
|
||||
/**
|
||||
* Builds a route configuration map for x402-bch middleware.
|
||||
*
|
||||
* @param {string} apiPrefix Express API prefix (e.g., "/v6")
|
||||
* @returns {Object} Routes configuration compatible with x402-bch-express
|
||||
* Resolve USDC contract + EIP-712 domain hints for the configured network.
|
||||
* @param {string} network CAIP-2
|
||||
*/
|
||||
|
||||
/** x402 `exact` on EVM expects a checksummable 0x address for `payTo`. */
|
||||
function assertEvmPayTo (payTo) {
|
||||
if (typeof payTo !== 'string' || !/^0x[a-fA-F0-9]{40}$/.test(payTo)) {
|
||||
throw new Error(
|
||||
'SERVER_BASE_ADDRESS must be a 0x-prefixed 40-hex EVM address (Base USDC settlement).'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds x402 v2 RoutesConfig for @x402/express (CAIP-2 network + accepts[]).
|
||||
* Payment asset is explicitly USDC (ERC-20) via AssetAmount, not generic "money".
|
||||
* @see https://docs.x402.org/guides/migration-v1-to-v2
|
||||
*/
|
||||
export function buildX402Routes (apiPrefix = '/v6') {
|
||||
const normalizedPrefix = apiPrefix.endsWith('/')
|
||||
@@ -18,29 +31,27 @@ export function buildX402Routes (apiPrefix = '/v6') {
|
||||
? normalizedPrefix
|
||||
: `/${normalizedPrefix}`
|
||||
|
||||
const routeKey = `${prefixWithSlash}/*`
|
||||
const routeKey = `* ${prefixWithSlash}/*`
|
||||
const network = config.x402.network
|
||||
if (!network) throw new Error('x402 network is required (set x402_NETWORK / X402_NETWORK).')
|
||||
|
||||
// Get network from config (now supports CAIP-2 format: eip155:8453)
|
||||
const NETWORK = config.x402.network
|
||||
if (!NETWORK) throw new Error('x402_NETWORK env required!')
|
||||
const payTo = config.x402.serverAddress
|
||||
if (!payTo) throw new Error('SERVER_BASE_ADDRESS is required for x402 v2 payTo.')
|
||||
assertEvmPayTo(payTo)
|
||||
|
||||
return {
|
||||
network: NETWORK,
|
||||
[routeKey]: {
|
||||
accepts: [
|
||||
{
|
||||
scheme: 'exact',
|
||||
payTo,
|
||||
price: config.x402.priceUSDC,
|
||||
network: NETWORK,
|
||||
config: {
|
||||
network,
|
||||
maxTimeoutSeconds: DEFAULT_TIMEOUT_SECONDS
|
||||
}
|
||||
],
|
||||
description: `${DEFAULT_DESCRIPTION} (${config.x402.priceUSDC} USDC)`,
|
||||
maxTimeoutSeconds: DEFAULT_TIMEOUT_SECONDS,
|
||||
mimeType: 'application/json',
|
||||
outputSchema: {
|
||||
input: {
|
||||
type: 'http',
|
||||
method: 'GET',
|
||||
discoverable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
mimeType: 'application/json'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,11 +59,82 @@ export function buildX402Routes (apiPrefix = '/v6') {
|
||||
export function getX402Settings () {
|
||||
return {
|
||||
enabled: Boolean(config.x402?.enabled),
|
||||
facilitatorUrl: config.x402?.facilitatorUrl, // https://docs.cdp.coinbase.com/x402/quickstart-for-sellers
|
||||
facilitatorUrl: config.x402?.facilitatorUrl,
|
||||
facilitatorKeyId: config.x402?.facilitatorKeyId,
|
||||
facilitatorSecretKey: config.x402?.facilitatorSecretKey,
|
||||
serverAddress: config.x402?.serverAddress,
|
||||
priceUSDC: config.x402?.priceUSDC
|
||||
priceUSDC: config.x402?.priceUSDC,
|
||||
usdcAssetAddress: config.x402?.usdcAssetAddress,
|
||||
network: config.x402?.network
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON for `GET /.well-known/x402` — matches `buildX402Routes` (Base USDC, exact scheme).
|
||||
* @param {string} [apiPrefix]
|
||||
*/
|
||||
export function getX402WellKnownManifest (apiPrefix = '/v6') {
|
||||
const normalizedPrefix = apiPrefix.endsWith('/')
|
||||
? apiPrefix.slice(0, -1)
|
||||
: apiPrefix
|
||||
const prefixWithSlash = normalizedPrefix.startsWith('/')
|
||||
? normalizedPrefix
|
||||
: `/${normalizedPrefix}`
|
||||
|
||||
const network = config.x402.network
|
||||
if (!network) throw new Error('x402 network is required (set x402_NETWORK / X402_NETWORK).')
|
||||
|
||||
const payTo = config.x402.serverAddress
|
||||
if (!payTo) throw new Error('SERVER_BASE_ADDRESS is required for x402 v2 payTo.')
|
||||
assertEvmPayTo(payTo)
|
||||
|
||||
return {
|
||||
x402Version: 2,
|
||||
network,
|
||||
facilitator: {
|
||||
url: config.x402.facilitatorUrl
|
||||
},
|
||||
resources: [
|
||||
{
|
||||
resource: `${prefixWithSlash}/*`,
|
||||
type: 'http',
|
||||
x402Version: 2,
|
||||
accepts: [
|
||||
{
|
||||
scheme: 'exact',
|
||||
network,
|
||||
price: config.x402.priceUSDC,
|
||||
payTo,
|
||||
maxTimeoutSeconds: DEFAULT_TIMEOUT_SECONDS,
|
||||
description: `${DEFAULT_DESCRIPTION} (${config.x402.priceUSDC} USDC)`,
|
||||
mimeType: 'application/json',
|
||||
extra: { }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Snapshot for `build-documents` agent manifest — Base USDC / exact scheme.
|
||||
* @returns {object|null} null when x402 is off or payTo/network invalid.
|
||||
*/
|
||||
export function getX402AgentAuthPricing () {
|
||||
const x402 = config.x402
|
||||
if (!x402?.enabled || !x402.serverAddress || !x402.network) return null
|
||||
try {
|
||||
assertEvmPayTo(x402.serverAddress)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
scheme: 'exact',
|
||||
x402Version: 2,
|
||||
network: x402.network,
|
||||
payTo: x402.serverAddress,
|
||||
priceUSDC: x402.priceUSDC,
|
||||
facilitatorUrl: x402.facilitatorUrl
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,29 +146,41 @@ export function getBasicAuthSettings () {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Auth headers for interact with mainnet coin base facilitator endpoints.
|
||||
*
|
||||
* CDP JWT auth for facilitator HTTPFacilitatorClient (verify / settle / supported).
|
||||
* @see https://docs.cdp.coinbase.com/get-started/authentication/jwt-authentication#javascript-2
|
||||
*/
|
||||
// https://docs.cdp.coinbase.com/get-started/authentication/jwt-authentication#javascript-2
|
||||
// https://docs.cdp.coinbase.com/api-reference/v2/rest-api/x402-facilitator/verify-a-payment
|
||||
export async function createAuthHeader () {
|
||||
// /verify endpoint jwt
|
||||
const id = config.x402?.facilitatorKeyId
|
||||
const secret = config.x402?.facilitatorSecretKey
|
||||
if (!id || !secret) {
|
||||
return {
|
||||
verify: {},
|
||||
settle: {},
|
||||
supported: {}
|
||||
}
|
||||
}
|
||||
|
||||
const verifyToken = await generateJwt({
|
||||
apiKeyId: config.x402?.facilitatorKeyId,
|
||||
apiKeySecret: config.x402?.facilitatorSecretKey,
|
||||
apiKeyId: id,
|
||||
apiKeySecret: secret,
|
||||
requestMethod: 'POST',
|
||||
requestHost: 'api.cdp.coinbase.com',
|
||||
requestPath: '/platform/v2/x402/verify'
|
||||
})
|
||||
// /settle endpoint jwt
|
||||
const settleToken = await generateJwt({
|
||||
apiKeyId: config.x402?.facilitatorKeyId,
|
||||
apiKeySecret: config.x402?.facilitatorSecretKey,
|
||||
apiKeyId: id,
|
||||
apiKeySecret: secret,
|
||||
requestMethod: 'POST',
|
||||
requestHost: 'api.cdp.coinbase.com',
|
||||
requestPath: '/platform/v2/x402/settle'
|
||||
})
|
||||
const supportedToken = await generateJwt({
|
||||
apiKeyId: id,
|
||||
apiKeySecret: secret,
|
||||
requestMethod: 'GET',
|
||||
requestHost: 'api.cdp.coinbase.com',
|
||||
requestPath: '/platform/v2/x402/supported'
|
||||
})
|
||||
|
||||
return {
|
||||
verify: {
|
||||
@@ -94,6 +188,9 @@ export async function createAuthHeader () {
|
||||
},
|
||||
settle: {
|
||||
Authorization: `Bearer ${settleToken}`
|
||||
},
|
||||
supported: {
|
||||
Authorization: `Bearer ${supportedToken}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user