Examples
Runnable integrations against @rpp402/sdk, read directly from the @rpp402/examples package at build time - this page can't show stale code, because it's the exact file @rpp402/examples' own test suite actually executes.
Basic purchase
runs against the real starterThe canonical single-leg lifecycle -Discovery → Quote → Commerce Session → Payment Intent → Settlement → Receipt -against a real, freshly-spawned copy of the minimal-service starter that rpp402 init produces.
pnpm --filter @rpp402/examples basic-purchaseimport { pathToFileURL } from "node:url";
import { createClient } from "@rpp402/sdk";
import { startDemoService } from "./lib/start-demo-service";
const DEMO_WALLET = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" as const;
/**
* The canonical single-leg lifecycle: Discovery → Quote → Commerce
* Session → Payment Intent → Settlement → Receipt, against a real,
* freshly-spawned copy of packages/private/templates/minimal-service -the exact
* starter `rpp402 init` produces, not a stand-in for it.
*/
export async function runBasicPurchase() {
const service = await startDemoService("examplebasicpurchase", 4041);
try {
const rpp402 = createClient({ agentWallet: DEMO_WALLET });
const discovered = await rpp402.discover(`${service.baseUrl}/.well-known/rpp.json`);
const session = await rpp402.session.create(discovered);
const [asset] = discovered.supported_assets;
if (!asset) throw new Error(`${discovered.service.name} published no supported_assets`);
const quote = await session.quote(discovered, {
capability: "example.echo",
params: { hello: "world" },
settlementAsset: asset,
});
const intent = await session.intent({
asset: quote.price.asset,
authorization: {
type: "agentic_account_delegation",
account_id: "aa_example01",
delegation_ref: "del_example1",
},
});
const settlement = await session.settle();
const receipt = await session.receipt();
return { discovered, session: session.data, quote, intent, settlement, receipt };
} finally {
await service.stop();
}
}
// pathToFileURL, not string concatenation -process.argv[1] is a raw OS
// path (backslashes on Windows), import.meta.url is a proper file:// URL.
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const result = await runBasicPurchase();
console.log(`Discovered ${result.discovered.service.name} (${result.discovered.service.id})`);
console.log(`Quote: ${result.quote.price.amount} ${result.quote.price.asset.symbol}`);
console.log(`Settlement: ${result.settlement.status} -${result.settlement.tx_hash}`);
console.log(`Receipt: ${result.receipt.id}, signed by ${result.receipt.signature.signer}`);
}
Multi-leg session
simplified, disclosed belowOne Commerce Session holding two legs, settled and receipted atomically -the structural claim at the center of RPP402-000's motivation. Both legs are against the same fixture service, not two independent companies -attaching a leg whose Quote was issued by a different service needs the session host to verify a Quote it never issued itself, which the reference implementation doesn't do yet (see RPP402-003 §Session host). Disclosed, not hidden -see the @rpp402/examplespackage's own docs.
pnpm --filter @rpp402/examples multi-leg-sessionimport { fileURLToPath, pathToFileURL } from "node:url";
import { createClient } from "@rpp402/sdk";
import { startFixtureService } from "./lib/start-demo-service";
const DEMO_WALLET = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" as const;
const FIXTURE = fileURLToPath(new URL("./fixtures/two-capability-service.js", import.meta.url));
/**
* One Commerce Session, two legs, one Settlement, one Receipt with two
* line items -the structural claim at the center of RPP402-000's
* motivation. Both legs are against the same fixture service (see
* src/fixtures/two-capability-service.js for why: cross-service leg
* attachment needs the session host to verify a Quote it never issued
* itself, which the reference implementation doesn't do yet). What this
* *does* prove for real: the Session/Intent/Settlement/Receipt state
* machine correctly holds and settles more than one leg atomically.
*/
export async function runMultiLegSession() {
const service = await startFixtureService(FIXTURE, 4043);
try {
const rpp402 = createClient({ agentWallet: DEMO_WALLET });
const discovered = await rpp402.discover(`${service.baseUrl}/.well-known/rpp.json`);
const session = await rpp402.session.create(discovered);
const [asset] = discovered.supported_assets;
if (!asset) throw new Error(`${discovered.service.name} published no supported_assets`);
const quoteA = await session.quote(discovered, {
capability: "example.echo",
params: { leg: "A" },
settlementAsset: asset,
});
const quoteB = await session.quote(discovered, {
capability: "example.ping",
params: { leg: "B" },
settlementAsset: asset,
});
const intent = await session.intent({
asset,
authorization: {
type: "agentic_account_delegation",
account_id: "aa_example02",
delegation_ref: "del_example2",
},
});
const settlement = await session.settle();
const receipt = await session.receipt();
return { discovered, session: session.data, quoteA, quoteB, intent, settlement, receipt };
} finally {
await service.stop();
}
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const result = await runMultiLegSession();
console.log(`Session ${result.session.id} -${result.session.legs.length} legs`);
console.log(
`Intent amount: ${result.intent.amount} ${result.intent.asset.symbol} (sum of both legs, exact decimal-string addition)`,
);
console.log(
`Receipt ${result.receipt.id}: ${result.receipt.line_items.length} line items, total ${result.receipt.total.amount} ${result.receipt.total.asset.symbol}`,
);
}
