Skip to content
Integra Agentic Terms

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

KeepWhy
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 returnedA landed receipt some pairings return beside the payment. check reads the payment with it.
The agreement's receipt (agreement), where returnedThe 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-bound

Where 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.signed to the seller again rather than signing a new payment.
  • For an agreement payment, call again without approved to collect its receipt, never with approved: every call with approved signs 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.

Edit on GitHub

Last updated on

On this page