GitHub

Docs

PATH REQUEST

Payment requests and checkout sessions — the only pillar that starts from the receiver, and the one a buyer actually sees.

LIVE partially — requests are implemented; checkout sessions and fee disclosure are DRAFT.

Why this is its own pillar

The other four pillars assume a payer: I want to send to someone, I find them, I check what they take, I send, I keep a receipt.

A payment request runs the other way. A merchant, a friend, a subscription — the receiver states what is owed, and the payer decides afterwards. That asymmetry is why it is not part of addressing:

SaysIs a
Standing intent (ADDRESS)"I accept USDC on Base"policy
Payment request (REQUEST)"You owe 5,000 for order 123, until Friday"claim

Systems that store both in one place end up unable to expire one, revoke the other, or tell a payer which they are looking at.

The object

{
  "kind": "payment_request",
  "reference": "7fk2m9pq3vx8",
  "issuer": "member-slug",
  "amount": "5000",
  "currency": "XOF",
  "accepts": [{ "rail": "mobile_money", "currency": "XOF" }],
  "fees": { "network_fee": "0", "operator_fee": "25", "currency": "XOF" },
  "order_reference": "ord_10482",
  "status": "created",
  "expires_at": "2026-09-10T10:00:00Z",
  "protocol_version": "0.1.0",
  "kid": "op_example_2026_01",
  "signature": "…"
}

amount may be absent. That means the payer chooses — a tip, a donation, an open invoice. It is a first-class case, not a missing field.

References are random, not sequential. A sequential reference leaks volume: read two links a week apart and you know how many payments were requested in between. It also invites walking other merchants' links.

Scope — the line that keeps this small

In the protocol: the reference and URL, status, expiry, amount and currency, accepted rails, the disclosed fee, the signature, the hand-back, and the link to the settlement receipt.

Not in the protocol: the title, description and image; branding and the landing page; promotion codes; the basket, tax, shipping and inventory; the provider integration.

PATH standardises what crosses between the merchant's provider and the payer's wallet. Everything else belongs to whoever built the store — and should, because that is where products differ.

This is the pillar where scope creep is fatal. A payment platform specification would need renegotiating in every jurisdiction and would never ship. If a field is not read by the payer's wallet, it does not belong here.

Reading a request

GET /requests/{reference} is the only public route in the protocol, and it has to be. A wallet that has never heard of the issuer must still be able to read a link before showing a payment screen. Otherwise "any conforming wallet can pay this" is false and the pillar is pointless.

The response has the same shape as a resolver answer with the terms already fixed. A wallet that can read one can read the other with no new code — a payment request is, structurally, a resolver answer whose terms were decided in advance.

Issuer and host are different roles

Fig. 01 — Two roles
The domainWhere to fetch.
The signatureWho is responsible.

A link may be served from the issuer's own domain, from its network under a managed arrangement, or from a custom domain. The kid identifies the responsible party in every case.

Two consequences for implementers:

Verify before displaying. Fetch the issuer's keys from its discovery document — never from whoever served the envelope — and check the signature.

Show the verified name, never the URL. A payer confronted with https://some-provider.xyz/p/abc will close the app. Show the issuer's name from the member registry, the way a card terminal shows a merchant rather than an acquirer's hostname.

Fee disclosure

DRAFT

When present, fees travel with the terms, in the same signed object the payer reads before deciding.

"fees": {
  "network_fee": "0",
  "operator_fee": "25",
  "fx_rate": "655.957",
  "fx_source": "operator_quote",
  "currency": "XOF",
  "total_deducted": "25"
}

A price disclosed after the decision is not a disclosure. Putting it inside the signature means it cannot be quietly different from what was shown.

Lifecycle

Fig. 02 — Lifecycle
created
paid
expired
revoked

created is the only state with anywhere to go. Every transition is recorded — a status overwritten without a trail cannot be contested, and disputes are exactly the case that needs one.

Reading an expired request returns path.request.expired, not not_found. The payer needs to know the difference between too late and never existed.

Checkout sessions

DRAFT

A session is a request plus the two things a merchant flow needs and a bare link does not: where to send the payer back, and where to notify the merchant.

{
  "id": "cs_4b81",
  "request_reference": "7fk2m9pq3vx8",
  "status": "open",
  "success_url": "https://store.example.com/thanks",
  "cancel_url": "https://store.example.com/cart",
  "payer_member": null,
  "attempts": 0
}

payer_member is where this stops being theoretical. Today a checkout completes only inside the wallet its provider chose. Under PATH the wallet that completes it may belong to another member entirely, and the merchant integrates nothing extra.

Attempts are counted, not overwritten. A payer who tries three wallets before one works is a normal story, and a merchant investigating a complaint needs all three.

The hand-back — return URL and webhook — is the only part of checkout PATH specifies, because it is the only part that crosses between two organisations.

Partial payments, multiple concurrent attempts and refunds tied to an order are RESERVED for v0.2. Should the session acquire a lifecycle of its own at that point, the demand becomes a first-class object and this pillar grows; that is a decision to take with evidence, not in advance.

Subscriptions are not payment requests

A subscribe link looks like a payment link and is not one: it creates a standing permission, so it belongs to PATH CONNECT.

Get this wrong and you ship without revocation and without counters, and find out when a customer asks how to stop a recurring charge.


On this page