File size: 3,642 Bytes
0d53e7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import "dotenv/config";
import { Transaction, TransactionBuilder } from "@stellar/stellar-sdk";
import { x402Client, x402HTTPClient } from "@x402/fetch";
import { createEd25519Signer, getNetworkPassphrase } from "@x402/stellar";
import { ExactStellarScheme } from "@x402/stellar/exact/client";

const STELLAR_PRIVATE_KEY = process.env.STELLAR_PRIVATE_KEY;
const SERVER_URL = process.env.SERVER_URL || "http://localhost:3001";
const NETWORK = "stellar:testnet";
const STELLAR_RPC_URL = "https://soroban-testnet.stellar.org";

if (!STELLAR_PRIVATE_KEY) {
  console.error("ERROR: STELLAR_PRIVATE_KEY not set in .env");
  process.exit(1);
}

const targetUrl = process.argv[2] || "https://x.com/stellarorg";

async function main() {
  const renderEndpoint = `${SERVER_URL}/render?url=${encodeURIComponent(targetUrl)}`;

  // Setup x402 client
  const signer = createEd25519Signer(STELLAR_PRIVATE_KEY, NETWORK);
  const rpcConfig = { url: STELLAR_RPC_URL };
  const client = new x402Client().register(
    "stellar:*",
    new ExactStellarScheme(signer, rpcConfig),
  );
  const httpClient = new x402HTTPClient(client);

  console.log(`Target URL: ${targetUrl}`);
  console.log(`Render endpoint: ${renderEndpoint}`);
  console.log(`Paying from: ${signer.address}`);

  // Step 1: Request without payment — expect 402
  console.log("\n--- Step 1: Request without payment ---");
  const firstTry = await fetch(renderEndpoint);
  console.log(`Response: ${firstTry.status} ${firstTry.statusText}`);

  if (firstTry.status !== 402) {
    console.log("No payment required! Response:", await firstTry.text());
    return;
  }

  // Step 2: Extract payment requirements from 402 response
  console.log("\n--- Step 2: Create payment ---");
  const paymentRequired = httpClient.getPaymentRequiredResponse((name) =>
    firstTry.headers.get(name),
  );
  console.log("Payment required:", JSON.stringify(paymentRequired, null, 2));

  // Step 3: Create and configure payment payload
  let paymentPayload = await client.createPaymentPayload(paymentRequired);
  const networkPassphrase = getNetworkPassphrase(NETWORK);
  const tx = new Transaction(
    paymentPayload.payload.transaction,
    networkPassphrase,
  );
  const sorobanData = tx.toEnvelope().v1()?.tx()?.ext()?.sorobanData();
  if (sorobanData) {
    paymentPayload = {
      ...paymentPayload,
      payload: {
        ...paymentPayload.payload,
        transaction: TransactionBuilder.cloneFrom(tx, {
          fee: "1",
          sorobanData,
          networkPassphrase,
        })
          .build()
          .toXDR(),
      },
    };
  }

  // Step 4: Send paid request
  console.log("\n--- Step 3: Send paid request ---");
  const paymentHeaders =
    httpClient.encodePaymentSignatureHeader(paymentPayload);
  const start = Date.now();
  const paidResponse = await fetch(renderEndpoint, {
    method: "GET",
    headers: paymentHeaders,
  });
  const elapsed = Date.now() - start;

  // Step 5: Show results
  console.log(`\n--- Result (${elapsed}ms) ---`);
  console.log(`Status: ${paidResponse.status}`);

  const paymentResponse = httpClient.getPaymentSettleResponse((name) =>
    paidResponse.headers.get(name),
  );
  if (paymentResponse) {
    console.log("Settlement:", JSON.stringify(paymentResponse, null, 2));
  }

  const data = await paidResponse.json();
  console.log(`\nTitle: ${data.title}`);
  console.log(`Render time: ${data.renderTimeMs}ms`);
  console.log(`Content length: ${data.content?.length} chars`);
  console.log(`\nContent preview:\n${data.content?.substring(0, 600)}`);
}

main().catch((err) => {
  console.error("Client error:", err);
  process.exit(1);
});