RPP402-006: Receipt
| RFC | RPP402-006 |
| Title | Receipt |
| Status | Draft |
| Version | 1.0.0 |
| Requires | RPP402-005 |
Overview
A Receipt is the cryptographically verifiable, durable proof that a Commerce Session settled -itemized per leg, independently checkable without trusting the issuing Service. It is the terminal primitive: once issued, the Session moves to receipt_issued and the purchase's lifecycle is complete.
Motivation
A payment protocol whose only record of "this happened" is a database row on the seller's server isn't durable -it disappears the moment that server does, and it can't be checked by anyone who doesn't trust the seller. RPP402-006 borrows the "durable by design" principle MPP states explicitly for exactly this reason (RPP402-000 §Relationship to Prior Art) and applies it to a Receipt that has to cover a multi-leg, potentially multi-asset Session rather than one line item.
Problem
A Receipt has to be verifiable two ways: cryptographically (a signature anyone can check against a known key) and onchain (a reference back to the Settlement transaction anyone can look up directly on Robinhood Chain). Losing either check would mean trusting the issuer's word for at least part of the proof.
Terminology
| Term | Definition |
|---|---|
| Receipt | The rcpt_ object defined below. |
| Line item | One leg's contribution to the Receipt, mirroring the Session's legs. |
| Verification | The process of independently confirming a Receipt's signature and onchain reference -see Protocol. |
Protocol
Issued automatically once a Settlement reaches confirmed; retrievable via GET {endpoints.receipts}/{id} (from Discovery, RPP402-001) or GET {endpoints.receipts}?session_id={sess_id}.
| Field | Type | Description |
|---|---|---|
rpp402_version | string | "1.0". |
id | string | rcpt_ prefixed. |
session_id | string | |
settlement_id | string | |
payer_wallet | string | |
line_items | array<LineItem> | One per Session leg. |
total | Money | Sum of line_items, in the Settlement's asset. |
settlement_ref | SettlementRef | Onchain pointer -see below. |
signature | ReceiptSignature | See below. |
issued_at | string (RFC 3339) |
LineItem:
{ "service_id": "svc_marketdata01", "capability": "market-data.quote", "quote_id": "quote_8f2e9a1c", "amount": { "asset": { "...": "AssetRef" }, "amount": "0.002" } }SettlementRef -how anyone, not just the issuing Service, verifies the Receipt against the chain directly:
{ "chain_id": "robinhood-1", "tx_hash": "0x7e2f...c9a1", "block_number": 4821093 }ReceiptSignature -how anyone verifies the Receipt's content wasn't altered after issuance, independent of the chain lookup:
{ "algorithm": "eip712", "signer": "0x1111...aaaa", "signature": "0x..." }signer is the issuing Service's wallet (from its Discovery Document, RPP402-001), signing the canonical EIP-712 payload of every field above except signature itself. A verifier recomputes that payload and checks the signature against signer -no call to the issuing Service is required to verify a Receipt already in hand.
Verification procedure
- Recompute the EIP-712 digest over the Receipt's fields (excluding
signature). - Recover the signer address from
signature.signatureand confirm it equalssignature.signer. - Confirm
signature.signermatches thewallet.addressin the relevant Service's Discovery Document (RPP402-001) at the time of issuance. - Independently look up
settlement_ref.tx_hashon Robinhood Chain and confirm its transfers matchline_items/total.
A Receipt that passes steps 1–3 but not step 4 (or vice versa) should be treated as invalid -both checks exist because they fail independently (step 1–3 catches a tampered document; step 4 catches a signed document describing a settlement that never actually happened onchain).
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://rpp402.com/schemas/v1/receipt.json",
"title": "RPP402 Receipt",
"type": "object",
"required": ["rpp402_version", "id", "session_id", "settlement_id", "payer_wallet", "line_items", "total", "settlement_ref", "signature", "issued_at"],
"properties": {
"rpp402_version": { "type": "string", "const": "1.0" },
"id": { "type": "string", "pattern": "^rcpt_[a-zA-Z0-9]{8,}$" },
"session_id": { "type": "string", "pattern": "^sess_[a-zA-Z0-9]{8,}$" },
"settlement_id": { "type": "string", "pattern": "^settle_[a-zA-Z0-9]{8,}$" },
"payer_wallet": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
"line_items": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["service_id", "capability", "quote_id", "amount"],
"properties": {
"service_id": { "type": "string" },
"capability": { "type": "string" },
"quote_id": { "type": "string" },
"amount": {
"type": "object",
"required": ["asset", "amount"],
"properties": {
"asset": { "$ref": "https://rpp402.com/schemas/v1/asset-ref.json" },
"amount": { "type": "string" }
}
}
}
}
},
"total": {
"type": "object",
"required": ["asset", "amount"],
"properties": {
"asset": { "$ref": "https://rpp402.com/schemas/v1/asset-ref.json" },
"amount": { "type": "string" }
}
},
"settlement_ref": {
"type": "object",
"required": ["chain_id", "tx_hash", "block_number"],
"properties": {
"chain_id": { "type": "string" },
"tx_hash": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$" },
"block_number": { "type": "integer" }
}
},
"signature": {
"type": "object",
"required": ["algorithm", "signer", "signature"],
"properties": {
"algorithm": { "const": "eip712" },
"signer": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
"signature": { "type": "string", "pattern": "^0x[a-fA-F0-9]+$" }
}
},
"issued_at": { "type": "string", "format": "date-time" }
}
}Examples
{
"rpp402_version": "1.0",
"id": "rcpt_5b8e10",
"session_id": "sess_3fd8c2",
"settlement_id": "settle_c91a4f",
"payer_wallet": "0xaaaa...1234",
"line_items": [
{
"service_id": "svc_marketdata01",
"capability": "market-data.quote",
"quote_id": "quote_8f2e9a1c",
"amount": { "asset": { "type": "stablecoin", "symbol": "USDG", "chain_id": "robinhood-1", "contract": "0x1111...aaaa" }, "amount": "0.002" }
}
],
"total": { "asset": { "type": "stablecoin", "symbol": "USDG", "chain_id": "robinhood-1", "contract": "0x1111...aaaa" }, "amount": "0.002" },
"settlement_ref": { "chain_id": "robinhood-1", "tx_hash": "0x7e2f...c9a1", "block_number": 4821093 },
"signature": { "algorithm": "eip712", "signer": "0x1111...aaaa", "signature": "0x3c8f..." },
"issued_at": "2026-07-10T18:24:04Z"
}Security
- A Receipt's
signature.signerMUST be checked against the issuing Service's Discovery Document wallet at time of issuance (step 3 above) -a Receipt signed by an unrelated key proves nothing. - Both verification legs (signature and onchain) matter independently; see Verification procedure above. Do not accept a Receipt on the strength of only one.
- Receipts are immutable once issued -there is no update endpoint. A correction (e.g. a partial refund, out of scope for v1) requires a new, separate onchain transaction and is not representable by mutating an existing Receipt.
Errors
type | status | Meaning |
|---|---|---|
https://rpp402.com/errors/receipt-not-found | 404 | |
https://rpp402.com/errors/settlement-not-confirmed | 409 | Requested before the underlying Settlement reached confirmed. |
Best Practices
- Store the full Receipt object, not just its
id-the whole point of Receipt durability is that it's checkable without a further call to the issuing Service, which may not exist by the time you need to check it. - Automate the four-step verification procedure in tooling (
rpp402 receipts verify, Phase 10) rather than trustingGETresponses at face value in production code paths. - When reconciling notional-mode tokenized-security purchases (RPP402-004/RPP402-005), always cross-reference a line item's
amountagainst the Settlement'sreference_price, not just the Intent's requested notional -the Receipt records what actually settled, which is the authoritative figure.
Reference Implementation
Schema: -packages/protocolreceipt.schema.json (Phase 4, this phase). Client: session.receipt() in @rpp402/sdk (Phase 5). Verification CLI: rpp402 receipts verify (Phase 10).
Previous: RPP402-005 -Settlement · Next: Roadmap
