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.
What is inside#
| Field | Meaning |
|---|---|
| canonical_issuance_snapshot | The 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_hash | The hash of that document, after RFC 8785 canonicalization. |
| payer_wallet | The 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. |
| salt | Derived from the attribution hash and the attestation digest. |
| payment_address | The one-time address, recomputable from the factory, the salt, and the terms. |
| recovery_address | Always the attested wallet. |
| transfers | Every 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_hash | The transaction that executed the contract and moved the amount to you. The same hash the request reports as settlement_tx_hash. |
| verification | Payday's signed statement of the verification facts, bound to this request, address, wallet, and nonce. |
A complete proof, abbreviated
{
"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:
Recompute the hash
Canonicalize
canonical_issuance_snapshotwith RFC 8785 and hash it. It must equalattribution_hash. If a PDF is attached, hash the file you were given and compare it with the snapshot'sattachment.sha256.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.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.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.
Check Payday's attestation
Verify the signature over the
verification.payloadagainst 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'slikely_unsolicited_atand its transfers say what happened.