Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 157 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
36 changes: 36 additions & 0 deletions scripts/derive-key.ts
Original file line number Diff line number Diff line change
@@ -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();
78 changes: 78 additions & 0 deletions scripts/verify-x402.ts
Original file line number Diff line number Diff line change
@@ -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);
11 changes: 11 additions & 0 deletions src/lib/x402.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading