Keeping the record
What to keep after a payment, how to confirm later that a payment carries the hash of the record you kept, and what to do with a moved payment.
The gate does not store anything. After a payment, what you keep is your proof of what you agreed to.
What to keep
| Keep | Why |
|---|---|
The ATR's bytes (bytes, or atr_bytes in Python) | Your copy of the record, exactly as served. Its hash is in the payment. |
H (h) | The hash of those bytes, as the gate computed it. |
The payment (signed) | What you sent. It carries H in its signed contents, for most pairings. |
landed, where returned | A landed receipt some pairings return beside the payment. check reads the payment with it. |
The agreement's receipt (agreement), where returned | The agreement URL's answer that the agreement payment for H is recorded: atrHash, network and transaction. It is returned for a pairing whose payment is not a public proof. |
Keep the bytes as bytes. Re-encoding them as a parsed and re-serialised JSON document changes the hash.
Confirm a payment later: check
check(bytes, presented, binding, landed?) answers one question: is the ATR hash the pairing reads from this payment
the SHA-256 of these bytes? A payment from which the pairing reads no ATR hash is declined signed-not-bound. It
fetches nothing and calls no signer.
import { , , type , type Signer } from "@integraledger/terms";
import { } from "@integraledger/lcp/x402";
import { } from "viem/accounts";
const = new ().(
'{"atrVersion":"1","id":"0f8fad5b-d9cb-469f-a165-70867728950e","x402":{"z":1.0,"a":"caf\\u00e9"},"seller":{"note":"Café — 30 días ✓"}}',
);
const = "0x8b1e122580ae3f6a8c3d36a24294e1260a87bde70f597279b973e39310072938";
const = `https://atr.seller.example/${.()}`;
const = {
: 2,
: { : "https://api.seller.example/v1/quote" },
: [
{
: "exact",
: "eip155:84532",
: "10000",
: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
: "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
: 60,
: { : "USDC", : "2" },
},
],
: { : { : { : "sha256", : , : }, : {} } },
};
const : = async () => ( === ? new () : new (null, { : 404 }));
const = ("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80");
const : Signer = {
: `eip155:84532:${.}`,
async () {
if (. !== "eip712") throw new (.);
return .(. as <typeof .>[0]);
},
};
const = await (, , , );
if ("decline" in || "approve" in || . === null) throw new ("not paid");
// Later: the payment against the bytes you kept, and against a record that differs by one byte.
const = await (., ., );
const = new ().(new ().(.).("30 días", "31 días"));
const = await (, ., );
.("kept bytes ", "decline" in ? .. : .);
.("altered bytes", "decline" in ? .. : .);kept bytes 0x8b1e122580ae3f6a8c3d36a24294e1260a87bde70f597279b973e39310072938
altered bytes signed-not-boundWhere finish or transact returned landed, pass it beside the payment: check(bytes, signed, binding, landed). The
caller passes as landed only the receipt its own reader read for the payment, as finish or transact returned it,
never a value another party sent. A landed member written into the payment is not read.
For a pairing whose payment is not a public proof of H, check reads the payment as it reads any other: it confirms
that the hash the pairing reads from it is the SHA-256 of the kept bytes, and declines signed-not-bound where the
pairing reads none. That payment does not prove H in public. The proof of H is the agreement payment, which is itself a
public-proof payment: check it with the binding of the pairing it was paid with.
The gate returns the agreement's receipt as agreement, not the signed agreement payment. The receipt is the agreement
URL's statement: the gate checks that it names H and is a recorded agreement, and does not look up the transaction it
names. Keep it, and confirm H from that transaction on network: for an EIP-3009 agreement payment, the nonce of the
token's AuthorizationUsed event is H.
A moved payment
A decline that carries moved means your signer moved the payment itself before the gate declined: a Lightning node
paid the invoice, or a signer broadcast a transaction. It also means, in the agreement exchange, that the gate sent the
agreement payment and then declined. moved holds the payment as signed, the ATR's bytes and their hash.
- Keep all three, as you would keep a payment that succeeded.
- For the payment for the resource, present
moved.signedto the seller again rather than signing a new payment. - For an agreement payment, call again without
approvedto collect its receipt, never withapproved: every call withapprovedsigns and sends a new agreement payment. See agreement payments. - Whether to deal with the seller another way is your principal's decision.
A payment still settling
If the seller answers that your payment is still settling, send the same payment again. Never sign a new one: the payment already carries H, and a second signature is a second payment.
Last updated on