Synthetic fixtures; deterministic local model. No production system is contacted.
Experiment inputs
Complete-run results
Decision trace
What this experiment demonstrates
Jenya's shopping agent buys three books from Belgavi's Book Store: ₹499, ₹350 and ₹650, with ₹49 shipping and ₹8.82 tax on shipping, ₹1,556.82 in all. The merchant signs the cart and the payment mandate references the SHA-256 of the cart's canonical JSON (keys sorted, no whitespace, amounts in integer paise). Then one change happens before payment: a price update, shipping recomputed with 18% tax on it, a tax recompute, an out-of-stock substitution, or rounding to whole rupees.
The hash is computed with a real synchronous SHA-256 that the lab's tests check against Node's crypto module, so the hex values in the trace are the actual digests of the two carts. The flow around it is a deterministic model: no signature is created, nothing is charged, and in human-not-present mode the model assumes Jenya approves when she is asked.
Run controlled comparisons
Start with the defaults: shipping rises by ₹40, the total becomes ₹1,604.02, the merchant charges it under the old mandate and a verifier that compares amounts with a ₹50 tolerance accepts it. One stale-cart payment and one overcharge without consent, ₹47.20 above the approved ₹1,556.82. The trace shows 2b5f0a41... against 5faf94a0....
Now change one thing at a time. Turn on the hash check: the payment is rejected as a stale cart. Set the tolerance to 40 instead: also rejected, but only because ₹47.20 exceeds ₹40, and the hash was never checked. Set the merchant to re-sign with human present: Jenya re-confirms and ₹1,604.02 is charged with consent. Switch to human not present: with the amount_range max at ₹1,600 the merchant returns unresolved_constraint and Jenya is asked; raise the max to ₹1,650 and the agent approves the new cart itself. Choose a substitution with amount 0 and tolerance 0: the total stays ₹1,556.82, the hash changes to 41ff9eb2..., and the amount-only check accepts a stale cart with a book Jenya did not choose. Choose rounding with tolerance 1: 18 paise are added and accepted. Choose reject: nothing is charged and the agent must fetch a newly signed cart. Choose price with amount 1 and the trace shows b6390214..., the same digest the article prints from Python.
Inspect the trace and keep evidence
The table compares the signed cart with the cart being charged line by line, with both full SHA-256 values in the last row. The decision trace shows who signed what and why the payment was accepted or rejected. Start trace, Next step, Show all and the slider control how much of the trace is visible; the metrics always describe the whole run. Reset restores the defaults and Export JSON downloads the inputs, metrics and trace.
Limits and production translation
The model hashes canonical cart JSON, as the AP2 v0.1 cart_hash did; AP2 v0.2 hashes the merchant-signed checkout JWT, and because that signature is non-deterministic even an identical re-sign gives a new hash. There are no real keys, SD-JWTs, receipts or payment networks, and one change happens per run. What carries over: any change to a signed cart changes its hash, a payment should be accepted only for the cart its mandate references, a re-signed cart needs new consent, and the only legitimate tolerance in human-not-present mode is the one the user signed in the open mandate.