Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.prudra.dev/llms.txt

Use this file to discover all available pages before exploring further.

Test MPP payments

MPP testing requires either stub mode (no real blockchain) or a funded Tempo testnet wallet. Start with stub mode, then graduate to testnet once your integration is working. Set PAYMENT_STUB_MODE=true on your server. In stub mode, the server accepts any payment credential without calling the Tempo RPC.
PAYMENT_STUB_MODE=true PRUDRA_API_KEY=prv_test_sk_... npx ts-node server.ts
Test with the stub payment header:
curl -X POST http://localhost:4001/analyse \
  -H "Content-Type: application/json" \
  -H "X-PAYMENT: stub_payment_accepted" \
  -d '{"text": "analyse this"}'

Testnet setup

For full end-to-end testing with real Tempo transactions:
  1. Get a Tempo testnet RPC URL (from your Tempo provider)
  2. Fund a test wallet with testnet USDC.e from the faucet
  3. Set PAYMENT_STUB_MODE=false and configure your Tempo RPC URL
PAYMENT_STUB_MODE=false
TEMPO_RPC_URL=https://tempo-testnet.example.com/v2/your-key
TEMPO_CHAIN_ID=<testnet-chain-id>
USDC_E_CONTRACT=<testnet-usdc-e-address>

The MPP agent test script

This script demonstrates the full MPP agent flow from example-07-mpp-agent.ts:
import { ethers, JsonRpcProvider } from 'ethers';
import { parseMPPChallenge, buildMPPCredential } from '@prudra/payments';

const SERVER_URL = 'http://localhost:4001';
const TEMPO_RPC   = process.env.TEMPO_RPC_URL!;

// ERC-20 ABI — compatible with Tempo's TIP-20 tokens
const ERC20_ABI = [
  'function transfer(address to, uint256 amount) returns (bool)',
  'function decimals() view returns (uint8)',
];

async function runMPPAgent() {
  const provider    = new JsonRpcProvider(TEMPO_RPC);
  const agentWallet = new ethers.Wallet(process.env.AGENT_PRIVATE_KEY!, provider);
  console.log('Agent wallet:', agentWallet.address);

  // Step 1: Request without payment — get 402 with WWW-Authenticate
  console.log('\nStep 1: Get MPP challenge');
  const r1 = await fetch(`${SERVER_URL}/summarise`, {
    method:  'POST',
    headers: { 'Content-Type': 'application/json' },
    body:    JSON.stringify({ text: 'Summarise this' }),
  });
  console.log('Status:', r1.status); // 402

  const wwwAuth = r1.headers.get('www-authenticate');
  if (!wwwAuth) throw new Error('No WWW-Authenticate header');

  // Step 2: Parse the MPP challenge
  console.log('\nStep 2: Parse MPP challenge');
  const challenge = parseMPPChallenge(wwwAuth);
  console.log('Challenge ID:', challenge.id);
  console.log('Realm:', challenge.realm);
  console.log('Intent:', challenge.intent);

  // Decode the request field to get payment details
  const paymentRequest = JSON.parse(
    Buffer.from(challenge.request, 'base64url').toString()
  );
  console.log('Amount:', paymentRequest.amount, 'base units');
  console.log('Recipient:', paymentRequest.recipient);

  // Step 3: Send on-chain transaction on Tempo
  console.log('\nStep 3: Send USDC.e transaction on Tempo');
  const usdce = new ethers.Contract(
    process.env.USDC_E_CONTRACT!,
    ERC20_ABI,
    agentWallet
  );
  const decimals = await usdce.decimals();
  const amount   = BigInt(paymentRequest.amount); // already in base units

  const tx = await usdce.transfer(paymentRequest.recipient, amount);
  console.log('Transaction sent:', tx.hash);

  const receipt = await tx.wait(1); // wait for 1 Tempo confirmation
  console.log('Confirmed in block:', receipt.blockNumber);

  // Step 4: Build credential and resubmit
  console.log('\nStep 4: Resubmit with Authorization: Payment header');
  const credential = buildMPPCredential({
    id:      challenge.id,
    txHash:  tx.hash,
    from:    agentWallet.address,
    chainId: parseInt(process.env.TEMPO_CHAIN_ID!),
  });

  const r2 = await fetch(`${SERVER_URL}/summarise`, {
    method:  'POST',
    headers: {
      'Content-Type':  'application/json',
      'Authorization': `Payment ${credential}`,
    },
    body: JSON.stringify({ text: 'Summarise this' }),
  });
  console.log('Status:', r2.status); // 200

  const result = await r2.json();
  console.log('\nResult:', JSON.stringify(result, null, 2));
  console.log('Vault ID:', result.vaultId);
  console.log('Protocol:', result.payment?.protocol); // 'mpp'
}

runMPPAgent().catch(err => {
  console.error('Fatal:', err.message);
  process.exit(1);
});

Run the test

AGENT_PRIVATE_KEY=0x... \
TEMPO_RPC_URL=https://tempo-testnet... \
TEMPO_CHAIN_ID=<testnet-id> \
USDC_E_CONTRACT=0x... \
  npx ts-node example-07-mpp-agent.ts