algovoi-x402-receipt-adapter
Turn an x402 payment into an offline verifiable receipt.
x402 proves a payment happened with a single txHash in the PAYMENT-RESPONSE
header. The amount and currency live in the upstream PAYMENT-REQUIRED header,
nothing binds the two together, and verifying the payment means hitting the
chain. There is no receipt.
This adapter takes both headers and produces a settlement_attestation_v1: a JSON object canonicalised under RFC 8785 (JCS) and content addressed with SHA-256. Any party recomputes the reference and confirms the bytes with a JSON parser, JCS, and SHA-256 alone. No chain call, no issuer service, no account.
What it produces
PAYMENT-REQUIRED -> maxAmountRequired, asset, payTo, network
PAYMENT-RESPONSE -> txHash, networkId, payer, success
|
x402_payment_ref = sha256(JCS({txHash, networkId, payer,
amount, currency, settled_at_ms}))
|
settlement_attestation_v1 (settlement_ref content addresses it)
x402_payment_refbinds the responsetxHashto the requested amount and currency plus an injected timestamp, so atxHashcan no longer be re-bound to a different amount.settlement_refissha256:+ SHA-256(JCS(attestation)), the content address of the whole receipt.
Install
pip install algovoi-x402-receipt-adapter
Use
from algovoi_x402_receipt_adapter import build_x402_settlement_attestation
payment_required = {
"scheme": "exact",
"network": "base",
"maxAmountRequired": "1000000", # atomic minor units (USDC, 6dp)
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x7D01d268636c835d9E56164A24A9587D82B8B186",
}
payment_response = {
"success": True,
"txHash": "0x7ae7b2750c6de4507af6a06305e403b82a31d1090d6330f57d83707800ac6acb",
"networkId": "base",
"payer": "0x1234...",
}
result = build_x402_settlement_attestation(
payment_required,
payment_response,
settled_at_ms=1751472000000,
provider_did="did:web:your-service.example",
jurisdiction_flags=["UK", "EU"],
)
result["settlement_ref"] # "sha256:..."
result["x402_payment_ref"] # "sha256:..."
result["attestation"] # the 8-field settlement_attestation_v1
Either header may be passed as a decoded dict or as the raw base64(JSON)
header value. The settlement fields follow the x402 Foundation SettleResponse
model (transaction, network, payer); the legacy names txHash and
networkId are accepted as aliases, so the adapter reads a PAYMENT-RESPONSE
header regardless of which naming a gateway emits.
Verify offline
from algovoi_x402_receipt_adapter import verify_settlement_ref
verify_settlement_ref(result["attestation"], result["settlement_ref"]) # True
The check is pure and deterministic: sha256: + SHA-256(JCS(attestation)).
Recompute it in any language with an RFC 8785 canonicaliser and a SHA-256, and
the reference matches byte for byte.
Determinism
The adapter injects no clock and asserts no identity of its own:
settled_at_ms and provider_did are explicit arguments. The same headers
with the same arguments always produce the same references. A frozen
conformance vector ships with the source tests.
Fields
settlement_attestation_v1 carries exactly eight fields, sorted
lexicographically by JCS:
| field | meaning |
|---|---|
canon_version |
jcs-rfc8785-v1 |
jurisdiction_flags |
ordered jurisdiction codes |
settled_payment_ref |
the x402_payment_ref |
settlement_amount |
{amount_minor, asset_id} |
settlement_chain |
e.g. ethereum:8453 |
settlement_provider_did |
the attesting party (you) |
settlement_result |
SETTLED / PENDING_FINALITY / REVERSED |
settlement_timestamp_ms |
integer epoch milliseconds |
Three ways to capture
| Option | Function | Use when |
|---|---|---|
| A. Facilitator side | receipt_from_settle |
you are the x402 facilitator (you ran /settle), or a resource server points at yours |
| B. Origin side | capture_x402_settlement |
behind a managed gateway (Cloudflare); re-verify on chain |
| C. Direct | build_x402_settlement_attestation |
you hold both headers and trust them |
All three yield the same settlement_attestation_v1, and to_keystone_binding
binds any of them into the Keystone. All settlement fields accept the x402
Foundation SettleResponse names (transaction, network, payer), with the
legacy txHash / networkId accepted as aliases.
A. Facilitator side
from algovoi_x402_receipt_adapter import receipt_from_settle
result = receipt_from_settle(
payment_requirements, # x402 PaymentRequirements (maxAmountRequired, asset, payTo)
settle_response, # your facilitator's SettleResponse (success, transaction, network, payer, amount)
settled_at_ms=1751472000000,
provider_did="did:web:your-facilitator.example",
keystone_context={"action_ref": "…", "transition_hash": "…", "retention_chain_ref": "sha256:…"},
)
result["settlement_ref"] # same value option B or C would produce
result["confirmed_amount"] # actual settled amount (SettleResponse.amount when present)
As the facilitator you settled the payment, so the result is trusted by default;
pass reverify for an optional on-chain check.
B. Secure capture, origin side (recommended for managed gateways)
A settlement gateway (for example Cloudflare, which returns the result inline in
a PAYMENT-RESPONSE header and exposes no settlement API or webhook) hands you
the two headers on the paid request. Do not trust them at face value: a forged
or replayed PAYMENT-RESPONSE would otherwise mint a false receipt.
capture_x402_settlement closes that hole by re-verifying the transaction on
chain before it builds or binds anything:
from algovoi_x402_receipt_adapter import capture_x402_settlement
def verify(network_id, tx_id, expected_receiver, expected_amount_minor, expected_asset):
# wire to the AlgoVoi facilitator POST /verify, or an in-process verifier.
# MUST return {"verified": bool, "confirmed_amount": int}.
...
result = capture_x402_settlement(
payment_required, payment_response,
verify=verify,
settled_at_ms=1751472000000,
provider_did="did:web:your-service.example",
keystone_context={"action_ref": "…", "transition_hash": "…", "retention_chain_ref": "sha256:…"},
already_bound=lambda tx: store.get(tx), # idempotency on txHash
)
result["binding_ref"] # the payment, verified and bound into the Keystone
result["confirmed_amount"] # the actual on-chain amount recorded
Security properties: fail closed (no on-chain confirmation, no binding),
unforgeable (binding requires a real payment to your payTo), replay safe
(idempotent on txHash). The attestation records the confirmed on-chain amount,
not the quoted maximum. Run this at your origin, ideally behind authenticated
origin pull so a spoofed request cannot reach it in the first place.
Bind it into the Keystone
The settlement this adapter produces can be bound into the AlgoVoi Keystone
accountability chain, so a payment is tied to the verified agent action it
settled. This uses the published algovoi-substrate binding primitive and
introduces no new hashing:
from algovoi_x402_receipt_adapter import (
build_x402_settlement_attestation, to_keystone_binding,
)
result = build_x402_settlement_attestation(
payment_required, payment_response,
settled_at_ms=1751472000000, provider_did="did:web:your-service.example",
)
binding_ref = to_keystone_binding(
result,
action_ref="…", # the verified agent action (bare 64-char hex)
transition_hash="…", # the COMMITTED lifecycle transition (bare hex)
retention_chain_ref="sha256:…", # tamper-evident chain position
)
binding_ref = sha256(JCS({ action_ref, transition_hash,
settlement_ref, retention_chain_ref }))
binding_ref proves which action the payment settled: changing the settlement,
the action, the transition, or the chain position changes it. Recompute it
offline with a JCS canonicaliser and SHA-256, the same as settlement_ref.
Making the payment a Keystone execution step (recipe)
To place the payment as the execution step of a full Keystone chain
(passport -> mandate -> policy -> decision -> execution -> trust_query), map
the settlement into an execution_ref record with the
algovoi-execution-ref
primitive and carry the settlement attestation as that step's evidence:
from algovoi_execution_ref import execution_ref # optional extra dependency
exec_ref = execution_ref(
decision_ref=decision_ref, # the ALLOW that authorised the spend
action_type="payment.settle",
scope=f"{result['attestation']['settlement_chain']}:{payment_response['txHash']}",
outcome="COMMITTED",
executed_at_ms=1751472000000,
)
This adapter does not depend on algovoi-execution-ref; the recipe is opt-in so
the package stays lean.
Networks
networkId maps to the AlgoVoi settlement_chain form (for example base maps
to ethereum:8453, base-sepolia to ethereum:84532, solana-devnet to
solana:devnet). Any eip155:<chainId> is mapped automatically. Pass
network_map={...} or settlement_chain=... to override, or strict=False to
pass an unknown id through unchanged.
Licence
Apache License 2.0. See LICENSE and NOTICE.
Metadata
Release files for algovoi-x402-receipt-adapter 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| algovoi_x402_receipt_adapter-0.1.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl | CPython 3.13 | CPython 3.13 | Linux glibc 2.5+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| algovoi_x402_receipt_adapter-0.1.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl | CPython 3.12 | CPython 3.12 | Linux glibc 2.28+ x86-64, Linux glibc 2.5+ x86-64 | Details |
| algovoi_x402_receipt_adapter-0.1.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl | CPython 3.11 | CPython 3.11 | Linux glibc 2.5+ x86-64, Linux glibc 2.28+ x86-64 | Details |
Total release size: 1.1 MB
Release files / algovoi_x402_receipt_adapter-0.1.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
| Download URL | algovoi_x402_receipt_adapter-0.1.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl |
|---|---|
| Size | 372.7 kB |
| Tags | CPython 3.13 Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 |
|
SHA-256 checksum How to use checksums |
f863cb5862eb9c725eb4285552bbe0e4491beb7800ad0019c3d1dea8f14261a7
|
|
BLAKE2b-256 checksum How to use checksums |
81ab3ef32ac2c4b28ed7590f2b1eda873dae9ea839ed14395d4c12bd4b74d328
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / algovoi_x402_receipt_adapter-0.1.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
| Download URL | algovoi_x402_receipt_adapter-0.1.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl |
|---|---|
| Size | 358.4 kB |
| Tags | CPython 3.12 Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 |
|
SHA-256 checksum How to use checksums |
e44c03a6bf694bf2b8532b733a01162f8374b25571b53437320bb475711344a6
|
|
BLAKE2b-256 checksum How to use checksums |
1644dfb406220f1410b25611d179650f7031707fcfad0c80f3b0bd539028ee6f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / algovoi_x402_receipt_adapter-0.1.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
| Download URL | algovoi_x402_receipt_adapter-0.1.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl |
|---|---|
| Size | 371.8 kB |
| Tags | CPython 3.11 Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 |
|
SHA-256 checksum How to use checksums |
2b82e91d520109ef926e5f9ffaa5dc3b40e83f6cd73b65f77f36b16849e2899a
|
|
BLAKE2b-256 checksum How to use checksums |
40cf1e83f7d7a04560f412ed5b9f9ac9d0d4c22a1178e7e9feccc7998007887b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Security for algovoi-x402-receipt-adapter 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.