From 68c27444b4b5ba066e853f5f632f5f9940d2156f Mon Sep 17 00:00:00 2001 From: Anthonyushie Date: Wed, 21 Jan 2026 19:29:52 +0100 Subject: [PATCH] final x402 changes --- package-lock.json | 160 ++++++++++++++++++++++++++++++++++++++++- package.json | 1 + scripts/derive-key.ts | 36 ++++++++++ scripts/verify-x402.ts | 78 ++++++++++++++++++++ src/lib/x402.ts | 11 +++ 5 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 scripts/derive-key.ts create mode 100644 scripts/verify-x402.ts diff --git a/package-lock.json b/package-lock.json index 5df01c0..94c5091 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@emailjs/browser": "^4.4.1", "@stacks/connect": "^8.2.4", + "@stacks/wallet-sdk": "^7.2.0", "@types/qrcode": "^1.5.6", "axios": "^1.13.2", "clsx": "2.1.1", @@ -2854,6 +2855,21 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@stacks/auth": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@stacks/auth/-/auth-7.3.1.tgz", + "integrity": "sha512-8zjQrnthhymJruSWYuP17IRx+c0k8LrOZYH9DRyaTzjEZ+pbvsRUM9v7hH1aGr2LG94BI9249qXpCtGWorVI+g==", + "license": "MIT", + "dependencies": { + "@noble/secp256k1": "1.7.1", + "@stacks/common": "^7.3.1", + "@stacks/encryption": "^7.3.1", + "@stacks/network": "^7.3.1", + "@stacks/profile": "^7.3.1", + "cross-fetch": "^3.1.5", + "jsontokens": "^4.0.1" + } + }, "node_modules/@stacks/common": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/@stacks/common/-/common-7.3.1.tgz", @@ -2891,6 +2907,62 @@ "@stencil/core": "^4.29.3" } }, + "node_modules/@stacks/encryption": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@stacks/encryption/-/encryption-7.3.1.tgz", + "integrity": "sha512-hCY61gd4PVr5LUZKOuzWfPLmuPrIGEapd1LkMintToJ+F3R/x0T+iIJVnJf2Y1l0cJsc4Xxq/TWCBeEAfybScg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.1.5", + "@noble/secp256k1": "1.7.1", + "@scure/bip39": "1.1.0", + "@stacks/common": "^7.3.1", + "base64-js": "^1.5.1", + "bs58": "^5.0.0", + "ripemd160-min": "^0.0.6", + "varuint-bitcoin": "^1.1.2" + } + }, + "node_modules/@stacks/encryption/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@stacks/encryption/node_modules/@scure/bip39": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", + "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.1.1", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@stacks/encryption/node_modules/base-x": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.1.tgz", + "integrity": "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==", + "license": "MIT" + }, + "node_modules/@stacks/encryption/node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "license": "MIT", + "dependencies": { + "base-x": "^4.0.0" + } + }, "node_modules/@stacks/network": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/@stacks/network/-/network-7.3.1.tgz", @@ -2951,6 +3023,20 @@ "zone-file": "^2.0.0-beta.3" } }, + "node_modules/@stacks/storage": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@stacks/storage/-/storage-7.3.1.tgz", + "integrity": "sha512-AvbZJkc97LZ0icpIWeZpUgtSQS3Nsd3M16GYAJcHDqsBdsCtlwoSzsEwrcwrxZ1CFsWMnz3Te9xCNqUjoQ+CcA==", + "license": "MIT", + "dependencies": { + "@stacks/auth": "^7.3.1", + "@stacks/common": "^7.3.1", + "@stacks/encryption": "^7.3.1", + "@stacks/network": "^7.3.1", + "base64-js": "^1.5.1", + "jsontokens": "^4.0.1" + } + }, "node_modules/@stacks/transactions": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-7.3.1.tgz", @@ -3015,6 +3101,68 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, + "node_modules/@stacks/wallet-sdk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@stacks/wallet-sdk/-/wallet-sdk-7.2.0.tgz", + "integrity": "sha512-w4UmIaulB03ki0eosWA2ju4vXtF1N+n+nX+/GuV8ZW3rbZ7xeRCv16IzZZL6TspMcaUKyZKTVB2uximqBNbqPQ==", + "license": "MIT", + "dependencies": { + "@scure/bip32": "1.1.3", + "@scure/bip39": "1.1.0", + "@stacks/auth": "^7.2.0", + "@stacks/common": "^7.0.2", + "@stacks/encryption": "^7.2.0", + "@stacks/network": "^7.2.0", + "@stacks/profile": "^7.2.0", + "@stacks/storage": "^7.2.0", + "@stacks/transactions": "^7.2.0", + "c32check": "^2.0.0", + "jsontokens": "^4.0.1", + "zone-file": "^2.0.0-beta.3" + } + }, + "node_modules/@stacks/wallet-sdk/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@stacks/wallet-sdk/node_modules/@scure/bip32": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.3.tgz", + "integrity": "sha512-dSH3+LCWONlSNQuF34xZrG6Xas7tp2jSSqHb/pMfXWM0vKE4JZOtK3uJfoWouUVW5IGlls75HkXmYLldZ8ySgQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.1.3", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@stacks/wallet-sdk/node_modules/@scure/bip39": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", + "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.1.1", + "@scure/base": "~1.1.0" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -10257,6 +10405,14 @@ "node": ">= 0.8" } }, + "node_modules/ripemd160-min": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz", + "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==", + "engines": { + "node": ">=8" + } + }, "node_modules/rollup": { "version": "4.55.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", @@ -10476,8 +10632,7 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/safe-push-apply": { "version": "1.0.0", @@ -11835,7 +11990,6 @@ "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==", "license": "MIT", - "optional": true, "dependencies": { "safe-buffer": "^5.1.1" } diff --git a/package.json b/package.json index a1786da..01c3de5 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "dependencies": { "@emailjs/browser": "^4.4.1", "@stacks/connect": "^8.2.4", + "@stacks/wallet-sdk": "^7.2.0", "@types/qrcode": "^1.5.6", "axios": "^1.13.2", "clsx": "2.1.1", diff --git a/scripts/derive-key.ts b/scripts/derive-key.ts new file mode 100644 index 0000000..f3c238b --- /dev/null +++ b/scripts/derive-key.ts @@ -0,0 +1,36 @@ +/** + * Helper to derive Stacks Private Key from Mnemonic + * + * Usage: npx tsx scripts/derive-key.ts "your mnemonic phrase here" + */ +import { generateWallet } from "@stacks/wallet-sdk"; + +const mnemonic = process.argv[2]; + +if (!mnemonic) { + console.error("Please provide your mnemonic phrase as an argument."); + console.error( + 'Usage: npx tsx scripts/derive-key.ts "word1 word2 ... word12"', + ); + process.exit(1); +} + +async function main() { + try { + const wallet = await generateWallet({ + secretKey: mnemonic, + password: "", // Default password for most wallets + }); + + const account1 = wallet.accounts[0]; + const privateKey = account1.stxPrivateKey; // This gets the raw private key + + console.log("\nāœ… Derived Private Key:"); + console.log(privateKey); + console.log("\n(Add this to your .env.local as STACKS_PRIVATE_KEY)"); + } catch (error) { + console.error("Failed to derive key:", error); + } +} + +main(); diff --git a/scripts/verify-x402.ts b/scripts/verify-x402.ts new file mode 100644 index 0000000..e95cecf --- /dev/null +++ b/scripts/verify-x402.ts @@ -0,0 +1,78 @@ +/** + * x402 Verification Script + * + * This script performs a real end-to-end test of the x402 payment flow. + * It connects to the local API, triggers a payment request, signs the transaction, + * and verifies the successful invoice creation. + * + * Usage: npx tsx scripts/verify-x402.ts + */ + +import { loadEnvConfig } from "@next/env"; +import { createInflowClient, createInvoiceExample } from "@/lib/x402-client"; +import axios from "axios"; + +// Load environment variables from .env.local, .env, etc. +const projectDir = process.cwd(); +loadEnvConfig(projectDir); + +const PRIVATE_KEY = process.env.STACKS_PRIVATE_KEY?.trim(); +const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"; + +async function main() { + console.log("šŸ” x402 Verification Script"); + console.log("==========================="); + + if (!PRIVATE_KEY) { + console.error( + "āŒ Error: STACKS_PRIVATE_KEY is missing in environment variables.", + ); + console.error(" Please add it to .env.local to run this verification."); + process.exit(1); + } + + console.log(`šŸŒ Target API: ${BASE_URL}`); + + if (PRIVATE_KEY) { + console.log(`šŸ”‘ Key Length: ${PRIVATE_KEY.length}`); + console.log(` Is Hex: ${/^[0-9a-fA-F]+$/.test(PRIVATE_KEY)}`); + } + + console.log(`šŸ‘¤ Wallet identity loaded`); + + try { + const client = createInflowClient(PRIVATE_KEY, BASE_URL); + + console.log("\n1ļøāƒ£ Initiating Invoice Creation Request..."); + // We send a request. The client interceptor will handle the 402 loop automatically. + const result = await createInvoiceExample(client, { + recipient: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", // Example testnet address + amount: "5.00", + token: "USDC", + memo: "x402-verification-" + Date.now(), + network: "stacks", + }); + + console.log("\nāœ… Success! Invoice Created."); + console.log(` ID: ${result.invoice.id}`); + console.log(` URL: ${result.invoice.paymentUrl}`); + + if (result.payment) { + console.log(` Payment TX: ${result.payment.txId}`); + console.log(` Amount Paid: ${result.payment.amountPaid}`); + } + } catch (error) { + console.error("\nāŒ Verification Failed"); + if (axios.isAxiosError(error) && error.response) { + console.error(` Status: ${error.response.status}`); + console.error(` Data:`, error.response.data); + } else if (error instanceof Error) { + console.error(` Error: ${error.message}`); + } else { + console.error(error); + } + process.exit(1); + } +} + +main().catch(console.error); diff --git a/src/lib/x402.ts b/src/lib/x402.ts index 7cc2c66..991f1cb 100644 --- a/src/lib/x402.ts +++ b/src/lib/x402.ts @@ -86,6 +86,17 @@ export async function verifyAndSettlePayment( return { isValid: false, error: "Missing X-PAYMENT header" }; } + // Bypass verification for local testing if configured + if (process.env.X402_BYPASS_VERIFICATION === "true") { + console.log( + "āš ļø x402 Verification BYPASSED (X402_BYPASS_VERIFICATION=true)", + ); + return { + isValid: true, + txId: "0xbypassed_transaction_" + Date.now().toString(16), + }; + } + try { const verifier = new X402PaymentVerifier( X402_FACILITATOR_URL,