Using Payday

Proof of Payment

A PDF receipt proves nothing on its own. A Proof of Payment lets anyone, with no access to Payday, recompute that this exact document, signed by this wallet, could only have been paid at this address, and that this wallet paid it.

Once a deposit request settles, GET /v1/deposit-requests/{id}/proof returns a JSON document. It is available to you and shared at your discretion with a payer, an auditor, or a counterparty. It is not a public link.

What it proves#

The sentence a proof supports is precise:

Most of that is recomputable by anyone from the proof alone. What remains Payday's word is the identity facts: that the mailbox code was exchanged, or that your server's secret was redeemed, and that the wallet's nonce was issued only after the policy passed. Those facts are listed in the proof and signed by Payday together with the document hash, the chain, the address, the wallet, and the nonce, so the statement belongs to this request and this payer alone and cannot be transplanted onto another.

Issued documentparties, amount, deadline…canonicalizeAttribution hashRFC 8785 + keccakPayer's walletthe one that will payEIP-712 signsAttestation digesthash + nonce, signedSaltkeccak(hash ‖ digest)CREATE3One-time addressexists before a contractTerms the address commits totoken · amount · payout · deadline · recovery walletAnyone holding the proof can recompute every step offline.Nothing in the chain of derivation can be changed after the payer signs.
What a verifier recomputes: the hash from the document, the signature's validity and digest, the salt from both, and the address from the salt and the terms. Then it checks the transfers on-chain.

What is inside#

FieldMeaning
canonical_issuance_snapshotThe exact document that was hashed at issuance, in canonical form: parties, amount in base units, heading, reference, notes, deadline, policy, the attachment's length and SHA-256, chain, token, your payout address, and the factory.
attribution_hashThe hash of that document, after RFC 8785 canonicalization.
payer_walletThe wallet address, the exact EIP-712 typed data it signed, the digest, and the signature. A verifier recovers the signer and checks it is the wallet.
saltDerived from the attribution hash and the attestation digest.
payment_addressThe one-time address, recomputable from the factory, the salt, and the terms.
recovery_addressAlways the attested wallet.
transfersEvery credited USDC transfer into the address: transaction, log index, sender, amount, block. All must come from the attested wallet and sum to at least the amount.
settlement_transaction_hashThe transaction that executed the contract and moved the amount to you. The same hash the request reports as settlement_tx_hash.
verificationPayday's signed statement of the verification facts, bound to this request, address, wallet, and nonce.
A complete proof, abbreviated
JSON
{
  "version": "payday.proof.v2",
  "payment_id": "dr_0198f80c-8d2f-7dc1-a369-90556a64f700",
  "canonical_issuance_snapshot": {
    "schema": "payday.invoice",
    "canonicalization": "RFC8785",
    "issuer": { "name": "Acme LLC", "email": "billing@acme.example" },
    "bill_to": { "name": "Customer Inc" },
    "amount_base_units": "25000000",
    "heading": "March retainer",
    "reference": "INV-1042",
    "notes": null,
    "expiration_timestamp": "1757160000",
    "payer_policy": { "mode": "verified_email", "expected_email": "alice@customer.example" },
    "attachment": { "id": "0198f80c-…", "byte_length": "48211", "sha256": "0x9f…" },
    "chain_id": "143",
    "token_address": "0x754704Bc059F8C67012fEd69BC8A327a5aafb603",
    "receiver_address": "0x1111111111111111111111111111111111111111",
    "factory_address": "0x…"
  },
  "canonicalization": "RFC8785",
  "attribution_hash": "0x…",
  "payer_wallet": {
    "address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    "typed_data": { "domain": { "name": "Payday", "version": "1", "…": "…" }, "…": "…" },
    "digest": "0x…",
    "signature": "0x…",
    "method": "ecdsa"
  },
  "salt": "0x…",
  "chain_id": "143",
  "factory_address": "0x…",
  "payment_address": "0x2222222222222222222222222222222222222222",
  "token_address": "0x754704Bc059F8C67012fEd69BC8A327a5aafb603",
  "recovery_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
  "settlement_transaction_hash": "0x…",
  "transfers": [
    {
      "transaction_hash": "0x…",
      "log_index": "12",
      "sender": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
      "recipient": "0x2222222222222222222222222222222222222222",
      "amount_base_units": "25000000",
      "block_number": "98765432"
    }
  ],
  "verification": {
    "payload": {
      "version": "1",
      "payment_id": "dr_0198f80c-…",
      "attribution_hash": "0x…",
      "chain_id": "143",
      "payment_address": "0x2222…",
      "payer_wallet": "0x5aAe…",
      "wallet_nonce": "0x…",
      "payer_policy_mode": "verified_email",
      "result": "approved",
      "verified_at": "2026-09-01T11:58:00Z",
      "wallet_bound_at": "2026-09-01T11:58:30Z",
      "facts": [
        { "kind": "mailbox", "provider": "auth0", "at": "2026-09-01T11:58:00Z" },
        { "kind": "wallet", "provider": "payday", "at": "2026-09-01T11:58:30Z" }
      ]
    },
    "signer": "0x…",
    "signature": "0x…"
  }
}

Verifying one#

The checks are published as verify_proof in Payday's open-source core library, which is the reference. In words:

  1. Recompute the hash

    Canonicalize canonical_issuance_snapshot with RFC 8785 and hash it. It must equal attribution_hash. If a PDF is attached, hash the file you were given and compare it with the snapshot's attachment.sha256.

  2. Check the wallet signature

    Confirm the typed data names this attribution hash and this wallet, compute its EIP-712 digest, and recover the signer from the signature. It must be payer_wallet.address.

  3. Recompute the salt and the address

    Derive the salt from the hash and the digest, then the address from the factory, the salt, and the terms in the snapshot with the wallet as the recovery term. It must equal payment_address.

  4. Check the transfers

    Every listed transfer must be from the attested wallet to the address, and their sum must be at least the amount. Whether they and the settlement transaction really executed is provable only against the chain: look them up by hash on any node or explorer.

  5. Check Payday's attestation

    Verify the signature over the verification.payload against Payday's published signer, and that the payload's hash, chain, address, wallet, and nonce match the rest of the proof.

When there is no proof#

  • Before settlement: 409 deposit_request_not_settled.
  • When any credited transfer came from a wallet other than the attested one: 409 deposit_sender_mismatch. The funds still settled, but no proof can claim the attested wallet paid them, so Payday does not issue one it cannot stand behind. The request's likely_unsolicited_at and its transfers say what happened.