Slide 1

Slide 1 text

PAY → Paul Conroy / @conroyp 402 → Building with Machine Payments → Agentic Commerce Beyond The Checkout GET 200

Slide 2

Slide 2 text

Paul Conroy 🇮🇪 From Dublin, Ireland 👴 Started playing with the web 30+ years ago (Notepad, table layouts, spacer GIFs, & FTP deploys!) CTO at Square1 🌍 conroyp.com / @conroyp

Slide 3

Slide 3 text

Agentic commerce is having a moment

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

What does it mean? An agent finds a price. It decides to buy. It completes the transaction under some authority: a wallet, a mandate, a token, or a human approval.

Slide 8

Slide 8 text

What does it mean? An agent finds a price. It decides to buy. It completes the transaction under some authority: a wallet, a mandate, a token, or a human approval. A shopping assistant buys you a jacket A research agent unlocks one dataset A coding agent pays for a single API call

Slide 9

Slide 9 text

What does it mean? An agent finds a price. It decides to buy. It completes the transaction under some authority: a wallet, a mandate, a token, or a human approval. A shopping assistant buys you a jacket A research agent unlocks one dataset A coding agent pays for a single API call 🚨 The buyer can no longer be assumed to be a person in a browser 🚨

Slide 10

Slide 10 text

Which layer do you need? COMMERCE PROTOCOLS (UCP) ● Implement agreed endpoints. ● Assistant is the shopfront. ● Platform gatekeepers! PAYMENT PRIMITIVES (MPP, x402) ● A resource, a price, and a way for a machine to pay. ● Agent needs to discover your routes and payment options.

Slide 11

Slide 11 text

Which layer do you need? COMMERCE PROTOCOLS (UCP) ● Implement agreed endpoints. ● Assistant is the shopfront. ● Platform gatekeepers! PAYMENT PRIMITIVES (MPP, x402) ● A resource, a price, and a way for a machine to pay. ● Agent needs to discover your routes and payment options.

Slide 12

Slide 12 text

402 Payment Required

Slide 13

Slide 13 text

402: A Brief History 1997 2020 HTTP/1.1 → L402 over Lightning reserves 402 2025 2026 → Coinbase ships x402 Machine → Payments Protocol

Slide 14

Slide 14 text

The whole protocol, in one exchange THE AGENT OUR API

Slide 15

Slide 15 text

The whole protocol, in one exchange THE AGENT OUR API 1. GET /resource

Slide 16

Slide 16 text

The whole protocol, in one exchange THE AGENT OUR API 1. 2. GET /resource 402 + signed challenge · price · expiry · rail

Slide 17

Slide 17 text

The whole protocol, in one exchange THE AGENT OUR API 1. 2. 3. Obtains a payment artefact: a token, or a signed transfer GET /resource 402 + signed challenge · price · expiry · rail

Slide 18

Slide 18 text

The whole protocol, in one exchange THE AGENT OUR API 1. 2. GET /resource 402 + signed challenge · price · expiry · rail 3. Obtains a payment artefact: a token, or a signed 4. transfer GET /resource + Authorization: Payment

Slide 19

Slide 19 text

The whole protocol, in one exchange THE AGENT OUR API 1. 2. GET /resource 402 + signed challenge · price · expiry · rail 3. Obtains a payment artefact: a token, or a signed 4. transfer GET /resource + Authorization: Payment Verifies settlement on the rail, then serves resource

Slide 20

Slide 20 text

The whole protocol, in one exchange THE AGENT OUR API 1. 2. GET /resource 402 + signed challenge · price · expiry · rail 3. Obtains a payment artefact: a token, or a signed 4. transfer GET /resource + Authorization: Payment 5. 200 + resource + Payment-Receipt Verifies settlement on the rail, then serves resource

Slide 21

Slide 21 text

“... verifies settlement on the rail”

Slide 22

Slide 22 text

“... verifies settlement on the rail” The protocol never says how money moves. A challenge names its rail and the client answers exactly one.

Slide 23

Slide 23 text

“... verifies settlement on the rail” The protocol never says how money moves. A challenge names its rail and the client answers exactly one. Lightning Bitcoin micropayments over payment channels USDC on Base / Solana USDC transfers, verified on-chain. Tempo Stripe pathUSD stablecoin on Tempo blockchain Shared Payment Tokens - card, wallets, Link

Slide 24

Slide 24 text

“... verifies settlement on the rail” The protocol never says how money moves. A challenge names its rail and the client answers exactly one. Lightning Bitcoin micropayments over payment channels USDC on Base / Solana USDC transfers, verified on-chain. Tempo Stripe pathUSD stablecoin on Tempo blockchain Shared Payment Tokens - card, wallets, Link

Slide 25

Slide 25 text

Anatomy of a 402 challenge $ curl -si https://www.payforgoals.com/api/v1/scores/match/1 HTTP/2 402 Payment Required WWW-Authenticate: Payment id="zN3AD07Vo2…", realm="www.payforgoals.com", method="stripe", request="eyJhbW91bnQi…", expires="2026-09-04T09:19:10Z", opaque="eyJub25jZSI6…" WWW-Authenticate: Payment id="Tz…", method="tempo", … One challenge per rail The client can pick one A short expiry Prices only valid for a short window Request terms How much? How to pay? Payment ID An HMAC over every field.

Slide 26

Slide 26 text

Anatomy of a 402 challenge $ curl -si https://www.payforgoals.com/api/v1/scores/match/1 HTTP/2 402 Payment Required WWW-Authenticate: Payment id="zN3AD07Vo2…", realm="www.payforgoals.com", method="stripe", request="eyJhbW91bnQi…", expires="2026-09-04T09:19:10Z", opaque="eyJub25jZSI6…" WWW-Authenticate: Payment id="Tz…", method="tempo", … One challenge per rail The client can pick one A short expiry Prices only valid for a short window Request terms How much? How to pay? Payment ID An HMAC over every field.

Slide 27

Slide 27 text

Anatomy of a 402 challenge $ curl -si https://www.payforgoals.com/api/v1/scores/match/1 HTTP/2 402 Payment Required WWW-Authenticate: Payment id="zN3AD07Vo2…", realm="www.payforgoals.com", method="stripe", request="eyJhbW91bnQi…", expires="2026-09-04T09:19:10Z", opaque="eyJub25jZSI6…" WWW-Authenticate: Payment id="Tz…", method="tempo", … One challenge per rail The client can pick one A short expiry Prices only valid for a short window Request terms How much? How to pay? Payment ID An HMAC over every field.

Slide 28

Slide 28 text

Anatomy of a 402 challenge $ curl -si https://www.payforgoals.com/api/v1/scores/match/1 HTTP/2 402 Payment Required WWW-Authenticate: Payment id="zN3AD07Vo2…", realm="www.payforgoals.com", method="stripe", request="eyJhbW91bnQi…", expires="2026-09-04T09:19:10Z", opaque="eyJub25jZSI6…" WWW-Authenticate: Payment id="Tz…", method="tempo", … One challenge per rail The client can pick one A short expiry Prices only valid for a short window Request terms How much? How to pay? Payment ID An HMAC over every field.

Slide 29

Slide 29 text

Anatomy of a 402 challenge $ curl -si https://www.payforgoals.com/api/v1/scores/match/1 HTTP/2 402 Payment Required WWW-Authenticate: Payment id="zN3AD07Vo2…", realm="www.payforgoals.com", method="stripe", request="eyJhbW91bnQi…", expires="2026-09-04T09:19:10Z", opaque="eyJub25jZSI6…" WWW-Authenticate: Payment id="Tz…", method="tempo", … One challenge per rail The client can pick one A short expiry Prices only valid for a short window Request terms How much? How to pay? Payment ID An HMAC over every field.

Slide 30

Slide 30 text

Anatomy of a 402 challenge $ curl -si https://www.payforgoals.com/api/v1/scores/match/1 HTTP/2 402 Payment Required WWW-Authenticate: Payment id="zN3AD07Vo2…", realm="www.payforgoals.com", method="stripe", request="eyJhbW91bnQi…", expires="2026-09-04T09:19:10Z", opaque="eyJub25jZSI6…" WWW-Authenticate: Payment id="Tz…", method="tempo", … One challenge per rail The client can pick one A short expiry Prices only valid for a short window Request terms How much? How to pay? Payment ID An HMAC over every field.

Slide 31

Slide 31 text

Inside the request data method="stripe", request="eyJhbW91bnQi…", { "amount": "100", "currency": "usd", "methodDetails": { "networkId": "profile_61Ut…", "paymentMethodTypes": ["card"] } }

Slide 32

Slide 32 text

Inside the request data method="stripe", request="eyJhbW91bnQi…", method="tempo", request="Txsd78abojs79…", { { "amount": "1000000", "currency": "0x20c000…0000", "methodDetails": { "chainId": 42431, "memo": "0xd02e…", "supportedModes": ["pull"] }, "recipient": "0x0dcd39…ABF70" "amount": "100", "currency": "usd", "methodDetails": { "networkId": "profile_61Ut…", "paymentMethodTypes": ["card"] } } }

Slide 33

Slide 33 text

The paid retry $ curl -si https://www.payforgoals.com/api/v1/scores/match/1 \ -H "Authorization: Payment eyJjaGFsbGVuZ2UiOnsiaWQiOi…" Decoded: { "challenge": { …every header param, echoed back untouched… }, "payload": { "spt": "spt_1U1M…" } } • Same challenge returned • Add rail-specific payment details

Slide 34

Slide 34 text

Paid response, with receipt HTTP/2 200 OK Payment-Receipt: eyJzdGF0dXMiOiJzdWNjZXNzIi… { "tier": "pay-per-view", "report": { "quarter": "2026-Q3", "pages": 42, … } }

Slide 35

Slide 35 text

Paid response, with receipt HTTP/2 200 OK Payment-Receipt: eyJzdGF0dXMiOiJzdWNjZXNzIi… { "tier": "pay-per-view", "report": { "quarter": "2026-Q3", "pages": 42, … } } // eyJzdGF0dXMiOiJzdWNjZXNzIi decoded: { "status": "success", "method": "stripe", "reference": "pi_3Qx8…", "timestamp": "2026-09-18T15:01:12Z" }

Slide 36

Slide 36 text

Paid response, with receipt HTTP/2 200 OK Payment-Receipt: eyJzdGF0dXMiOiJzdWNjZXNzIi… { "tier": "pay-per-view", "report": { "quarter": "2026-Q3", "pages": 42, … } } // eyJzdGF0dXMiOiJzdWNjZXNzIi decoded: { "status": "success", "method": "stripe", "reference": "pi_3Qx8…", "timestamp": "2026-09-18T15:01:12Z" } reference is the settlement ref Stripe: PaymentIntent id Tempo: on-chain tx hash Receipt is base64url JSON, auditable on both sides.

Slide 37

Slide 37 text

Ask for one rail $ curl -si https://www.payforgoals.com/api/v1/scores/match/1 \ -H 'Accept-Payment: stripe/charge' HTTP/2 402 Payment Required WWW-Authenticate: Payment id="…", method="stripe", … // Rank instead of filter: Accept-Payment: stripe/charge, tempo/charge;q=0.3

Slide 38

Slide 38 text

402 Pay up

Slide 39

Slide 39 text

200 Paid!

Slide 40

Slide 40 text

200 Paid! …but how?

Slide 41

Slide 41 text

Backend application flow 🧑💻 Verify the request • HMAC altered? • Payment for different route? • Offer expired? • Signature checks out?

Slide 42

Slide 42 text

Backend application flow 🧑💻 Verify the request • HMAC altered? • Payment for different route? • Offer expired? • Signature checks out?

Slide 43

Slide 43 text

Backend application flow 🧑💻 Verify the request • HMAC altered? • Payment for different route? • Offer expired? • Signature checks out?

Slide 44

Slide 44 text

Backend application flow 🧑💻 Verify the request • HMAC altered? • Payment for different route? • Offer expired? • Signature checks out? Create a settlement lock Don’t process the same request twice

Slide 45

Slide 45 text

Backend application flow 🧑💻 Verify the request • HMAC altered? • Payment for different route? • Offer expired? • Signature checks out? Create a settlement lock Don’t process the same request twice Settle on the rail

Slide 46

Slide 46 text

Backend application flow 🧑💻 Verify the request • HMAC altered? • Payment for different route? • Offer expired? • Signature checks out? Create a settlement lock Don’t process the same request twice Settle on the rail

Slide 47

Slide 47 text

Backend application flow 🧑💻 Verify the request • HMAC altered? • Payment for different route? • Offer expired? • Signature checks out? Create a settlement lock Don’t process the same request twice Settle on the rail

Slide 48

Slide 48 text

Backend application flow 🧑💻 Verify the request • HMAC altered? • Payment for different route? • Offer expired? • Signature checks out? Serve response & receipt Create a settlement lock Don’t process the same request twice Settle on the rail

Slide 49

Slide 49 text

Wait - where is the card stored in all of this?

Slide 50

Slide 50 text

Wait - where is the card stored in all of this?

Slide 51

Slide 51 text

The Shared Payment Token { "id": "spt_1RxT…", "object": "shared_payment .granted_token", "usage_limits": { "currency": "usd", "max_amount": 100, "expires_at": 1757442300 }, "payment_method": "pm_…", "seller_details": { "network_business_profile": "pr…" } ● Single-purpose credential ● Minted from a saved card ● Revocable! ● The token travels, but the card never does. } https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens

Slide 52

Slide 52 text

The Shared Payment Token { "id": "spt_1RxT…", "object": "shared_payment .granted_token", "usage_limits": { "currency": "usd", "max_amount": 100, "expires_at": 1757442300 }, "payment_method": "pm_…", "seller_details": { "network_business_profile": "pr…" } Limitations on: ● Specific business ● Spending cap ● Expiry date } https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens

Slide 53

Slide 53 text

Where the SPT comes from 🧑💻

Slide 54

Slide 54 text

Where the SPT comes from 🧑💻 💳

Slide 55

Slide 55 text

Where the SPT comes from 🧑💻 💳

Slide 56

Slide 56 text

Where the SPT comes from 🧑💻 💳

Slide 57

Slide 57 text

Where the SPT comes from 🧑💻 🤖 💳

Slide 58

Slide 58 text

Where the SPT comes from 🧑💻 🤖 💳

Slide 59

Slide 59 text

Where the SPT comes from 🧑💻 🤖 💳 Shared Payment Token 🎟

Slide 60

Slide 60 text

Where the SPT comes from 🧑💻 🤖 💳 Shared Payment Token 🎟 🎟

Slide 61

Slide 61 text

Where the SPT comes from 🧑💻 🤖 💳 🎟 Shared Payment Token 🎟 🎟

Slide 62

Slide 62 text

Where the SPT comes from 🧑💻 🤖 💳 🎟 Shared Payment Token 🎟 🎟

Slide 63

Slide 63 text

Where the SPT comes from 🧑💻 🤖 💳 💵 Shared Payment Token 🎟 🎟 🎟

Slide 64

Slide 64 text

Where the SPT comes from 🧑💻 🤖 💳 💵 Shared Payment Token 🎟 The merchant never sees the card! 🎟 🎟

Slide 65

Slide 65 text

Stripe: You Make The Charge The buyer's wallet grants a scoped token (SPT) ↓ The agent retries your route with the SPT ↓ Your server creates and confirms a PaymentIntent ↓ Stripe reports success ↓ Serve the resource. Receipt ref = pi_…

Slide 66

Slide 66 text

Stripe: You Make The Charge The buyer's wallet grants a scoped token (SPT) ↓ The agent retries your route with the SPT ↓ Your server creates and confirms a PaymentIntent ↓ Stripe reports success ↓ Serve the resource. Receipt ref = pi_… The token is not the payment. It is permission to charge, inside limits. You still create the charge, and you still verify the challenge. Card economics apply. Price in dollars, not microcents!

Slide 67

Slide 67 text

Tempo: You Validate It The agent signs a pathUSD transfer for this challenge ↓ Agent retries your route with the signed transaction ↓ Your server validates it, then broadcasts it ↓ The chain confirms ↓ Serve the resource. Receipt ref = transaction hash The payment arrives signed. The agent commits the money. Our job is to check the transfer matches the challenge, then confirm it settles on-chain. No card minimums. Onecent prices work.

Slide 68

Slide 68 text

PayForGoals.com

Slide 69

Slide 69 text

PayForGoals.com HTTP/2 200 OK Payment-Receipt: eyJzdGF0dXMiOiJzdWNjZXNzIi… { "tier": "pay-per-view", "scoreline": { "id": 1, "away_score": 7, "home_score": 1, "year": 2014, "teams": null } } MPP playground Supports Stripe & Tempo Test cards & testnet!

Slide 70

Slide 70 text

PayForGoals.com HTTP/2 200 OK Payment-Receipt: eyJzdGF0dXMiOiJzdWNjZXNzIi… { "tier": "pay-per-view", "scoreline": { "id": 1, "away_score": 7, "home_score": 1, "year": 2014, "teams": null } } MPP playground Supports Stripe & Tempo Test cards & testnet! Team names in v2 (mvp…)

Slide 71

Slide 71 text

PayForGoals.com HTTP/2 200 OK Payment-Receipt: eyJzdGF0dXMiOiJzdWNjZXNzIi… { "tier": "pay-per-view", "scoreline": { "id": 1, "away_score": 7, "home_score": 1, "year": 2014, "teams": null } } MPP playground Supports Stripe & Tempo Test cards & testnet! Team names in v2 (mvp…)

Slide 72

Slide 72 text

Live Demo 402 PAY 200

Slide 73

Slide 73 text

Live Demo 402 PAY 200

Slide 74

Slide 74 text

What just happened? 402 The server minted a signed challenge Price + rail + expiry, bound to that resource PAY The agent produced a payment artefact A Shared Payment Token, a signed transfer (no checkout page!) 200 The server verified settlement, then served Receipt ref → PaymentIntent id / transaction hash

Slide 75

Slide 75 text

Charging for a 404? $ curl -si https://www.payforgoals.com/api/v1/scores/match/9999999 \ -H "Authorization: Payment eyJjaGFsbGVuZ2UiOnsiaWQiOi…" HTTP/2 404 Resource Not Found

Slide 76

Slide 76 text

Charging for a 404? $ curl -si https://www.payforgoals.com/api/v1/scores/match/9999999 \ -H "Authorization: Payment eyJjaGFsbGVuZ2UiOnsiaWQiOi…" HTTP/2 404 Resource Not Found 402 → PAY → 404 The customer paid to learn the thing does not exist!

Slide 77

Slide 77 text

Charging for a 404? $ curl -si https://www.payforgoals.com/api/v1/scores/match/9999999 \ -H "Authorization: Payment eyJjaGFsbGVuZ2UiOnsiaWQiOi…" HTTP/2 404 Resource Not Found 402 → PAY → 404 The customer paid to learn the thing does not exist! ● Run a pre-qualifying function before returning 402. ● Only suitable if the lookup itself isn’t costly! (inference costs etc)

Slide 78

Slide 78 text

Can you make that any easier for me? square1-io/laravel-mpp

Slide 79

Slide 79 text

Pay to skip the queue HTTP/2 429 Too Many Requests Retry-After: 3600

Slide 80

Slide 80 text

Pay to skip the queue HTTP/2 429 Too Many Requests Retry-After: 3600 WWW-Authenticate: Payment id="zN3AD07Vo2…", realm="www.payforgoals.com", method="stripe", request="eyJhbW91bnQi…", expires="2026-09-04T09:19:10Z", opaque="eyJub25jZSI6…"

Slide 81

Slide 81 text

Pay to skip the queue HTTP/2 42902 Too Many Requests - but maybe.. Retry-After: 3600 WWW-Authenticate: Payment id="zN3AD07Vo2…", realm="www.payforgoals.com", method="stripe", request="eyJhbW91bnQi…", expires="2026-09-04T09:19:10Z", opaque="eyJub25jZSI6…"

Slide 82

Slide 82 text

Pay to skip the queue HTTP/2 42902 Too Many Requests - but maybe.. Retry-After: 3600 WWW-Authenticate: Payment id="zN3AD07Vo2…", realm="www.payforgoals.com", method="stripe", request="eyJhbW91bnQi…", expires="2026-09-04T09:19:10Z", opaque="eyJub25jZSI6…" { "detail": "Free tier exhausted. Retry in 3600s or skip the queue for $3.50." }

Slide 83

Slide 83 text

How do agents find you? /openapi.json List priced routes Accepted payment rails listed Automatically update on change

Slide 84

Slide 84 text

How do agents find you? OpenAPI.json file on your domain Publish x-payment-info per route: method, intent, amount, currency. Agents read the menu when asked to investigate your site. Directories mpp.dev lists MPP-enabled services. Connect to the MCP server - agents search, find a price, pay, and call. A human tells them! “I want to buy ABC from example.com. Find out how much it costs and come back for approval.”

Slide 85

Slide 85 text

The EMEA Reality Check

Slide 86

Slide 86 text

The EMEA Reality Check Live SPT buyer flows are US-only today The Link-wallet buyer side has not opened up here yet Stripe seller flows are now enabled in EMEA and test mode is available globally, covering the buyer side! Tempo mainnet is live, from anywhere No geo-gate. For teams outside the US: seller rails are (mostly) open, buyer rails half-there. Build the server side once, and both rails are ready.

Slide 87

Slide 87 text

402 Payment Required

Slide 88

Slide 88 text

200 OK

Slide 89

Slide 89 text

💶 💵 💰 200 💸 OK

Slide 90

Slide 90 text

Thank you! payforgoals.com github.com/square1-io/laravel-mpp mpp.dev @conroyp · conroyp.com