RPP402-006Draft

RPP402-006: Receipt

RFCRPP402-006
TitleReceipt
StatusDraft
Version1.0.0
RequiresRPP402-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

TermDefinition
ReceiptThe rcpt_ object defined below.
Line itemOne leg's contribution to the Receipt, mirroring the Session's legs.
VerificationThe 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}.

FieldTypeDescription
rpp402_versionstring"1.0".
idstringrcpt_ prefixed.
session_idstring
settlement_idstring
payer_walletstring
line_itemsarray<LineItem>One per Session leg.
totalMoneySum of line_items, in the Settlement's asset.
settlement_refSettlementRefOnchain pointer -see below.
signatureReceiptSignatureSee below.
issued_atstring (RFC 3339)

LineItem:

json
{ "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:

json
{ "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:

json
{ "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

  1. Recompute the EIP-712 digest over the Receipt's fields (excluding signature).
  2. Recover the signer address from signature.signature and confirm it equals signature.signer.
  3. Confirm signature.signer matches the wallet.address in the relevant Service's Discovery Document (RPP402-001) at the time of issuance.
  4. Independently look up settlement_ref.tx_hash on Robinhood Chain and confirm its transfers match line_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

json
{
  "$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

json
{
  "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.signer MUST 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

typestatusMeaning
https://rpp402.com/errors/receipt-not-found404
https://rpp402.com/errors/settlement-not-confirmed409Requested 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) rather than trusting GET responses 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 amount against the Settlement's reference_price, not just the Intent's requested notional -the Receipt records what actually settled, which is the authoritative figure.

Reference Implementation

Schema: @rpp402/protocol - receipt.schema.json. Client: session.receipt() in @rpp402/sdk. Verification CLI: rpp402 receipts verify in @rpp402/cli.


Previous: RPP402-005 -Settlement · Next: Roadmap