# Payments

Rogue has four independently configurable payment engines: **ACP, AP2, MPP,
and x402**. Both the platform switch and every engine start disabled. No provider
credentials, network calls, wallets or charge attempts are needed while disabled.
Several engines can be enabled at once. Agents discover the alternatives at
`GET /api/v1/payments/providers`; people can inspect them at `/pricing`.

The product is **prepaid Pro access**, canonical product `pro`, quantity one. The
legacy `harbor` product remains an accepted alias. All engines use the same package
catalog: **$19/30 days**, **$54/90 days**, **$102/180 days**, or **$180/365 days**.
These are the 1-, 3-, 6-, and 12-month packages; the annual equivalent is $15/month.
[Plans and pricing](https://rogue.camp/pricing) records the discounts, exact durations and quotas.
Prices are USD in integer cents, paid up front. x402 also accepts smaller quotes for proportional access at the selected package rate. No recurring charge is scheduled. Successful
live payments extend `harbor_access` entitlements; concurrent renewals add their
periods. Existing manually granted Harbor tiers are preserved and shown as Pro. Test payments are
recorded but never grant access. Services in the service queue remain free.

There are no automatic renewals or usage overages. Renewals require a new explicit
purchase. Existing checkouts retain their amounts and access days. New checkout
prices come from `src/lib/plans.ts`; legacy `amount_minor` and `access_days` provider
config fields remain accepted but no longer override new Pro prices. New CLI
templates show $19/30 days. Confirm
the engine, live/test mode, price and access period before paying. Quotas do not
activate unconfigured services: file exchange and Git content require their own
bindings. Keep payment switches disabled until the advertised paid offering is ready.

## Choose a package

### Discover payments from a fresh connection

Rogue's `/.well-known/api-catalog` includes the public payment-provider entry point.
Shared HTTP `Link` headers advertise `/api/v1/payments/providers` and this guide;
robots and sitemaps also list them. Read the provider response's `enabled` flag,
available engines, test/live mode and package prices before creating a checkout.
MCP clients can use `payment_options({})` for the same information.

MPP agents also discover the existing payment POST in `/openapi.json` through
`x-payment-info.offers`. The advertised method is `stripe`, intent `charge`, and
currency `usd`; `amount: null` means that the exact amount depends on the checkout
ID. This offer applies to checkouts created with `engine: "mpp"`. Inspect current
provider availability first. The signed HTTP 402 challenge fixes the actual
checkout price and is authoritative over discovery metadata. `get_agent_protocols`
includes the same MPP entry points, credential header and provider-status URL.
The official mppx middleware already performs challenge/credential/receipt
handling; discovery does not turn on a disabled provider or create a payment.

Free public pages and discovery documents return HTTP 200. Rogue uses x402 for
explicit prepaid Pro purchases, including smaller payments for proportional
access. The HTTP 402 challenge belongs to the authenticated checkout's offer/pay
URLs below. Browsing the homepage, reading a price preview or connecting to MCP
does not charge the caller.

An analyzer that probes only `/`, `/api` and `/api/v1` may report that x402 was not
detected. Follow the payment entry in the API catalog to the implemented flow.
Bazaar registration is a separate facilitator feature; Rogue does not advertise
individual account checkout URLs in a public Bazaar directory.

### ACP and AP2 discovery

`GET /.well-known/acp.json` implements the upstream
[ACP discovery proposal](https://github.com/agentic-commerce-protocol/agentic-commerce-protocol/blob/main/rfcs/rfc.discovery.md).
It advertises only Rogue's implemented `2026-01-30` REST checkout service at
`/api/v1/payments/acp`, with links to `/api/v1/plans` and current provider readiness.
It does not claim carts, an orders API or a payment-delegation service. This is
discovery of implemented capability, not an assertion that Stripe is enabled.
Read `/api/v1/payments/providers` before creating a checkout.

The A2A card at `/.well-known/agent-card.json` includes an optional AP2 extension
with role `merchant`, describing Rogue's AP2 0.2 processor adapter and REST/MCP
binding. Send A2A `payments` or `{"skill":"payment_discovery"}` for current
availability. This A2A command creates no checkout and submits no payment; it
rejects mandate/payment-token fields. Actual AP2 purchases follow the existing
REST/MCP flow and require the configured processor described below, an active
account with `payments:write`, and an authorized signed mandate. The card does
not assert the processor is currently enabled or that a payment has settled.

The API/ARD catalogs, shared Link headers, MCP `get_acp_discovery`, and
`get_agent_protocols` (`rog protocols`) link these interfaces and their schemas.

### Preview and create a checkout

Discover package IDs and totals through `GET /api/v1/plans`,
`GET /api/v1/payments/providers`, MCP `get_plans` or `payment_options`.
Preview with `GET /api/v1/payments/quote?engine=acp&package_id=pro-12-months`.
Create a quote through REST or MCP `create_payment`:

```json
{
  "engine": "acp",
  "product": "pro",
  "package_id": "pro-12-months",
  "idempotency_key": "unique-key-for-this-annual-purchase"
}
```

Package IDs are `pro-1-month`, `pro-3-months`, `pro-6-months` and `pro-12-months`.
Omitting `package_id` selects the monthly package. Unknown packages and client-set
prices/durations are rejected, except the bounded x402 `payment_amount_minor` described below. Reuse the key only for the same engine, package and payment amount;
choose a new key when changing either. Historical payments have `package_id: null`
and retain their original terms. Creating or previewing a quote does not charge.

## Install and configure

Apply pending migrations, including `0028_pro_packages` and `0029_proportional_payments`, through the existing
migration runbook during an authorized deployment. Builds and tests do not apply migrations to your configured database.
The existing `ROGUE_SECRETS_KEY` binding encrypts payment credentials using
AES-GCM with the engine identity as authenticated data. Keep that key stable
and backed up; existing checkouts retain encrypted credential snapshots.

Use an active administrator's API key or session bearer token with
`ROGUE_ADMIN_TOKEN` in the selected target's ignored environment file. This
credential stays with the local CLI; it is **not uploaded as a Worker secret**.
Remote API calls require HTTPS. Local HTTP is allowed only on loopback.

```sh
mkdir -p .rogue
./rogue payments template acp > .rogue/acp.json
# Fill in the template from your secret manager; protect the file:
chmod 600 .rogue/acp.json
./rogue payments configure local acp --file .rogue/acp.json --dry-run
./rogue payments configure local acp --file .rogue/acp.json --yes
./rogue payments check local acp
./rogue payments enable local acp --yes
./rogue payments enable local --yes
./rogue payments status local
```

Use `staging` or `production` explicitly to operate those targets. Commands use
the selected target's public URL and admin credential, never the local file as
a remote fallback. Provider JSON is sent to the admin API and encrypted there;
configuration output never contains credentials. Store configuration in a secret
manager or under ignored `.rogue/`, not a tracked JSON file. Reconfiguring a
provider disables that provider and resets its readiness check.

Repeat template/configure/check/enable for `ap2`, `mpp`, and `x402` to offer
alternatives. Each engine keeps its own mode and credentials; prices come from the shared catalog. Enabling an
engine performs a connection check. Enabling the platform does not enable any
engine implicitly. The dashboard distinguishes enabled from globally paused.

```sh
./rogue payments disable production x402 --yes  # Only x402
./rogue payments disable production --yes       # Platform-wide new payment stop
./rogue payments reconcile production PAYMENT_UUID --yes
```

Disabling stops new checkouts, payment offers and new charge attempts. It does
not revoke existing access or block verified settlement of an already-submitted
payment. This lets administrators stop sales without losing late payments.
`--dry-run` has no network calls or credential-file reads; mutation commands
require `--yes` or the existing exact-target interactive confirmation.

## Supported protocol profiles

| Engine | Implemented flow | External configuration |
| --- | --- | --- |
| ACP | 2026-01-30 merchant checkout create/read/update/complete/cancel, digital Pro SKU, Stripe Shared Payment Token | Stripe secret key and webhook signing secret; SPT access enabled on the account |
| AP2 | ES256 merchant-signed checkout, delegated mandate verification and settlement, pinned ES256 processor receipt, merchant-signed checkout receipt | An AP2 processor implementing the adapter contract below, its credential and public receipt key, merchant signing key |
| MPP | Official `mppx` Stripe `charge` intent with HMAC-bound, expiring, checkout-scoped challenge and receipt | Stripe SPT credentials, Stripe account ID, independent random challenge secret, webhook signing secret |
| x402 | v2 `exact` USDC on Solana/Devnet or Base/Sepolia; facilitator `/verify` then `/settle` | Facilitator, RPC, network, mint/token and public recipient; Solana sponsor is discovered during setup |

These are concrete supported profiles, not an assertion that every optional
protocol extension is implemented. MPP streaming, sessions and subscriptions,
ACP physical fulfillment/discounts/3DS UI, non-USD pricing, automated tax
calculation, payout splitting, and automated refunds/disputes are outside this
profile. Set the all-in price accordingly. Card checkouts requiring additional
user action remain pending for operator review; Rogue does not fabricate a
successful charge or collect raw card details.

### ACP and MPP / Stripe

ACP endpoints are rooted at `/api/v1/payments/acp/checkout_sessions` and return
ACP checkout objects directly rather than Rogue's `data` envelope. Use
`API-Version: 2026-01-30` and `Idempotency-Key` on creation. The digital catalog
accepts one package, for example `{ "id": "pro-12-months", "quantity": 1 }`.
The `pro` and legacy `harbor` IDs select the monthly package. Updating to the
same package is a no-op; changing packages requires a new checkout. Buyer details are accepted but not persisted. Unsupported physical
fulfillment and optional extensions are rejected. Token completion uses:

```json
{ "payment_data": { "provider": "stripe", "token": "spt_..." } }
```

Both integrations use Stripe's SPT preview (`2026-07-29.preview`). A passing
account check validates credentials and `charges_enabled`; it does not prove
that Stripe has granted your account access to its SPT product. MPP also checks
that `network_id` matches the Stripe account. Configure at least 50 USD cents.
The maintained `mppx` SDK is pinned in the lockfile; recheck its protocol and
Stripe preview requirements when upgrading.

Create Stripe webhook endpoints at:

- `https://YOUR_HOST/api/v1/payments/webhooks/acp`
- `https://YOUR_HOST/api/v1/payments/webhooks/mpp`

Subscribe to `payment_intent.succeeded`, `payment_intent.processing`,
`payment_intent.payment_failed`, and `payment_intent.canceled`. Put each endpoint's
signing secret in that engine's configuration. Verification uses the raw body,
Stripe signature and a five-minute tolerance, then matches the payment ID,
amount, currency and mode. Repeated and out-of-order failures cannot undo paid
access. Existing checkout snapshots keep their webhook secrets: keep old signing
secrets valid while those checkouts are outstanding, or reconcile them before
rotating the endpoint secret.

For MPP, get `/payments/{id}/offer` or POST `/payments/{id}/pay` without a
credential to receive the native `402` and `WWW-Authenticate` challenge. The
challenge advertises `Payment-Authorization`, preserving `Authorization` for
the Rogue bearer token. Retry the payment POST with the official MPP credential.
Successful responses include `Payment-Receipt`.

### x402: billing, proof and proportional access

Rogue sells a one-time period of Pro access. There is no wallet debit schedule,
subscription invoice, automatic renewal, or automatic top-up. A checkout is a
15-minute price quote. `GET /api/v1/payments/{id}` and MCP `get_payment` return
the payment ledger record: ID, amount in USD cents, package price, purchased
seconds, test/live mode, paid timestamp, status and confirmed transaction receipt.
This is a payment receipt, **not a tax invoice**; Rogue currently does not produce
numbered tax invoices, collect billing addresses, or calculate taxes.

Rogue supports x402 **v2 exact USDC** on Solana (the production default) and Base.
Solana uses a buyer-signed versioned SPL transaction; the facilitator sponsors
network fees. Base uses EIP-3009 authorization. One USD cent is 10,000 atomic units.

| Network | x402 identifier | USDC mint/token |
| --- | --- | --- |
| Solana mainnet | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` |
| Solana Devnet | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` | `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` |
| Base | `eip155:8453` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
| Base Sepolia | `eip155:84532` | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` |

Solana requirements include `extra.feePayer`, the checkout ID in `extra.memo`,
and a fresh `extra.recentBlockhash`. Use that memo to keep concurrent transfers
unique. Buyer's and merchant's USDC associated token accounts must already exist;
fund a newly generated buyer wallet with USDC before using it. The native clients
sign a standard SPL transfer with the sponsor signature left for the facilitator.
An exact transfer is atomic: an insufficient-balance authorization does not
partially transfer funds. Failed or merely signed payments do not buy access.
Do not send an ordinary wallet transfer to the merchant and expect an automatic
upgrade: it would lack the checkout, account and replay bindings.

To pay less, set `payment_amount_minor` when creating an x402 checkout. It must
be an integer from 1 to the selected package's full price. It is not supported
by the other engines. Omit it to buy the full package. Preview it through
`GET /api/v1/payments/quote?engine=x402&package_id=pro-1-month&payment_amount_minor=950`.
The signed exact transfer is then for that smaller amount:

```json
{
  "engine": "x402",
  "product": "pro",
  "package_id": "pro-1-month",
  "payment_amount_minor": 950,
  "idempotency_key": "my-half-month-purchase-001"
}
```

`access_seconds = floor(package_access_days * 86400 * amount_minor / package_amount_minor)`.
$9.50 buys 15 days at the $19 monthly rate. $90 buys 182.5 days at the $180 annual
rate; the selected package's discount also applies to its partial payment.
Sub-second fractions round down. `access_days` may therefore be fractional;
`access_seconds` is the precise entitlement duration. The amount, package price
and duration are fixed in the checkout. Changing any choice needs a new
idempotency key. Cancel an unsubmitted quote before replacing it. Historical
quotes retain their original amounts and durations.

The platform verifies payment as follows:

1. Check that the payload's scheme, network, asset, recipient and amount match
   the saved quote; reserve the EVM authorization nonce or Solana signed message against replay.
2. Call the configured facilitator's `/verify`, then `/settle` exactly once.
   A valid signature alone does not prove payment. The facilitator broadcasts
   the authorized transfer and returns its transaction hash/signature.
3. Save the transaction reference and buyer signature before granting access.
   Independently verify through `rpc_url`. Solana must be **finalized**, match
   the buyer's exact signed transaction, contain one matching SPL `TransferChecked`,
   and increase the merchant's USDC associated token account by the quoted amount.
   Base requires the configured confirmations (default three) and matching USDC
   `Transfer` event. Pending or failed transactions grant no access.
4. Atomically record the unique settlement, receipt and dated entitlement.
   Confirmed **live** payments extend access from `max(now, current_paid_until)`
   by the saved seconds. Test payments never grant live Pro. Concurrent renewals
   and repeated confirmations cannot lose time or grant it twice.

If blocks are still pending, HTTP returns **202**, or MCP returns a payment
record with `status: "pending"`, without a success receipt. Use
`POST /api/v1/payments/{id}/confirm` or MCP `confirm_payment({id})` to check again.
This requires `payments:write`, authorizes only the owning account, never signs or
submits another transfer, and works while new purchases are disabled. Keep
polling modestly (for example every five seconds). Inspect `get_payment` and
`get_me` for the receipt and current account access. An uncertain response must
not trigger a fresh checkout or signature. If no transaction hash was returned,
an administrator must resolve it with the facilitator before charging again.

### REST x402 buyer flow

Use a Rogue bearer credential to bind access to your account. Keep that in
`Authorization` throughout the x402 exchange; the wallet proof has its own header.

1. Discover `GET /api/v1/payments/providers` and create the quote above using
   `POST /api/v1/payments`. Save `data.id`, `pay_url` and `confirm_url`.
2. POST the same-origin `pay_url` without a proof. The server returns HTTP 402,
   `PAYMENT-REQUIRED: <base64 JSON>`, and the x402 v2 requirements in its body.
3. Check the network, USDC mint/token, receiving address, amount and your budget.
   Approve the payment in your wallet and sign its Solana transaction or Base EIP-3009 authorization locally.
4. POST the same URL once with `PAYMENT-SIGNATURE: <base64 PaymentPayload>`.
   On confirmed success, `PAYMENT-RESPONSE` contains the base64 settlement
   response. The body is Rogue's usual `{data: payment, meta: ...}` envelope.
5. For pending/uncertain outcomes, inspect or confirm the existing payment ID.
   A repeated request for a paid ID returns its stored receipt without charging.

### MCP x402 buyer flow

Rogue follows the transport used by the [Cloudflare x402 MCP example](https://github.com/cloudflare/agents/tree/main/examples/x402-mcp)
and [Cloudflare payment guide](https://developers.cloudflare.com/agents/tools/payments/x402/).
It keeps its existing stateless MCP implementation and uses the same wire format.
Call `create_payment`, then `pay_payment({id})`:

```json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"pay_payment","arguments":{"id":"PAYMENT_UUID"}}}
```

Payment requirements are in a **JSON-RPC error with code 402 and `error.data`**,
not an MCP `isError` result or an HTTP 402. The client checks its budget and asks
its wallet to sign, then retries with the same arguments and the full object in
`params._meta["x402/payment"]`. Do not base64-encode MCP metadata and do not put
it in `arguments`. Confirmed results include the settlement object in
`result._meta["x402/payment-response"]`. `structuredContent` contains the payment
record. These tools use their payment ID for idempotency: omit `_idempotency_key`.
`confirm_payment` can finish a pending grant without a second payment.

Use Cloudflare's `withX402Client` with an explicit user confirmation callback,
or the official Rogue SDK helpers and CLI described in the client release's
`docs/X402.md`. Ordinary SDK calls never silently spend money. All language SDKs
have explicit paid-call methods, a caller-owned budget and in-process wallet
callbacks. C/C++ and the CLI include an OpenSSL wallet signer; JavaScript and
Python also include EVM wallet adapters. The stdio `rog mcp` bridge preserves both
payment metadata keys and leaves signing/approval to the calling MCP client.

### Alternative buyers

The [official x402 buyer quickstart](https://docs.x402.org/getting-started/quickstart-for-buyers)
explains these maintained alternatives:

- JavaScript: `@x402/fetch` (or `@x402/axios`) with `@x402/svm` and a Solana signer; `@x402/evm` with viem for Base.
- Python: `x402[httpx]` or `x402[requests]`; add the optional SVM dependencies for Solana, or use `EthAccountSigner` for Base.
- Go: the official x402 Go module with its SVM/EVM signer and HTTP wrapper.
- Cloudflare Agents: `agents/x402` and `withX402Client` for paid MCP calls.

Create the Rogue checkout first. Point the wrapper at its `pay_url`, retain your
Rogue Authorization header, and register only the intended exact Solana or Base network.
Set an explicit maximum and allowlist the correct asset and recipient before
signing. Some current x402 clients default to a $1 maximum; deliberately raise
that ceiling to your chosen amount ($19 monthly or $180 annual), rather than
disabling spend controls. Use the wrapper's approval/policy hook and retain the
same payment ID on interruption. Third-party wallets and wrappers remain optional.

### Receiving payments: where your wallet address goes

Set **`ROGUE_X402_PAY_TO` in `.env.production.local`** to your public Solana
receiving address. No wallet private key is needed. The target file already holds
the database connection and stable `ROGUE_SECRETS_KEY`. Apply it with:

```sh
./rogue payments setup production --dry-run
./rogue payments setup production --yes
```

The guarded setup checks the facilitator and RPC before atomically storing the
configuration encrypted in the database and enabling x402 plus platform payments.
It uses the operator's database credentials, so no `ROGUE_ADMIN_TOKEN` is needed.
It preserves other engines and the site mode. Repeating unchanged setup preserves
open checkout snapshots. Editing the env file alone does not update a deployed
provider; run setup again to apply a new recipient. Deployment preserves these
settings; do not upload a wallet private key as a Worker secret.

Defaults are live USDC on Solana, **finalized** commitment,
`https://api.mainnet-beta.solana.com` and `https://facilitator.payai.network`.
`ROGUE_X402_NETWORK=solana` is optional because it is the default. Set it to `base`
with an EVM receiving address for Base (three confirmations).
Setup discovers and pins the facilitator's public fee payer, verifies the genesis
hash, six-decimal SPL mint, and initialized merchant USDC associated token account.
If the sponsor rotates its fee payer, run setup again. Optional operator variables are
`ROGUE_X402_RPC_URL`, `ROGUE_X402_FACILITATOR_URL`, and
`ROGUE_X402_FACILITATOR_TOKEN` (when the facilitator accepts bearer authentication).
[PayAI's free tier](https://docs.payai.network/x402/facilitators/pricing) currently
includes up to 1,000 lifetime settlements; beyond that it requires merchant credits
and an API key. Shared hosting limits can be reached earlier. Its
[developer reference](https://facilitator.payai.network/developers) describes
supported networks, authentication and settlement errors. A receiver address is
sufficient to start within that allowance; it does not remove provider limits.

For testnet or a custom provider, the existing admin-API workflow also works:

```sh
./rogue payments template x402 > .rogue/x402.json
chmod 600 .rogue/x402.json
# Edit privately; set ROGUE_ADMIN_TOKEN in the selected target env first.
./rogue payments configure production x402 --file .rogue/x402.json --yes
./rogue payments check production x402
./rogue payments enable production x402 --yes
./rogue payments enable production --yes
```

The generic JSON template starts on Base testnet. Solana custom configurations
use the table above, `asset_name: "USDC"`, `asset_version: "2"`, and `fee_payer`
from the matching `/supported` entry's `extra.feePayer`. Solana waits for finality
regardless of the EVM-only `confirmations` setting. For real USDC on Base, set:

- `mode`: `live`; `network`: `eip155:8453`.
- `asset`: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`.
- `asset_name`: `USD Coin`; `asset_version`: `2`.
- `pay_to`: **your public Base/Ethereum receiving address**.
- `rpc_url`: your Base mainnet RPC URL; `confirmations`: at least 1 (default 3).
- `facilitator_url`: a facilitator supporting v2 exact on Base mainnet; supply
  `facilitator_token` only if that provider accepts a bearer token.

The readiness check validates supported scheme/network, RPC chain ID and six
asset decimals. Do not assume the example public test facilitator supports your
production profile or authentication method. Credentials live only in the ignored
JSON/encrypted database. Both switches must be enabled, and the site must be in
**production mode**; maintenance keeps new payments disabled; the production robot landing page allows them. Changing
site mode is a separate deliberate operator action. Configuring a provider resets
its enable flag, so run the check and enable steps after configuration changes.

### AP2 processor adapter contract

AP2 permits merchants to delegate verification to a technology provider. There
is no universal AP2 processor REST endpoint, so Rogue deliberately uses an
explicit private transport contract, **`rogue-ap2-1`**, for that integration.
A processor must implement this contract (directly or through its own bridge)
before the AP2 engine can be enabled. This repository implements the merchant
adapter, not a wallet, credential provider or payment network.

The configured public HTTPS `processor_url` supports:

1. `GET /health`, bearer-authenticated, returns
   `{ "version": "rogue-ap2-1", "ready": true, "mode": "test" }` (or `live`).
2. `POST /payments`, bearer-authenticated, with `Idempotency-Key: PAYMENT_UUID`:

```json
{
  "version": "rogue-ap2-1",
  "payment_id": "UUID",
  "merchant_id": "YOUR_MERCHANT",
  "mode": "test",
  "checkout_jwt": "MERCHANT_ES256_JWT",
  "checkout_hash": "BASE64URL_SHA256_OF_CHECKOUT_JWT",
  "amount_minor": 1900,
  "currency": "usd",
  "checkout_mandate": "COMPLETE_SD_JWT_MANDATE_AND_DISCLOSURES",
  "payment_token": "CREDENTIAL_PROVIDER_SCOPED_TOKEN"
}
```

The processor **must verify** the complete closed/open mandate delegation chain,
trusted issuer/user keys, holder binding, consent, audience, expiry, nonce,
merchant, checkout hash, currency, amount and every constraint. Unknown
constraints fail closed. It must verify the payment token/mandate binding and
settle through its payment network. The client never asserts that it paid.
The processor durably deduplicates by `payment_id` and does not settle twice.

It returns `{ "settlement_receipt": "PROCESSOR_ES256_JWT" }`. The JWT has
`iss = receipt_issuer`, `aud = merchant_id`, `iat`, `exp`, unique `jti`, plus:

```json
{
  "payment_id": "UUID",
  "checkout_hash": "BASE64URL_SHA256_OF_CHECKOUT_JWT",
  "mandate_hash": "BASE64URL_SHA256_OF_SUBMITTED_CHECKOUT_MANDATE_STRING",
  "amount_minor": 1900,
  "currency": "usd",
  "status": "succeeded",
  "reference": "UNIQUE_PROCESSOR_SETTLEMENT_ID",
  "payment_receipt": "NETWORK_OR_PROCESSOR_SIGNED_AP2_PAYMENT_RECEIPT"
}
```

`status` may also be `pending` or `failed`. Rogue verifies the pinned public
key, ES256 algorithm, issuer, audience, freshness (five minutes), expiry and all
checkout bindings before applying the result. It signs the AP2 checkout receipt
with the merchant key and returns it with the processor's payment receipt.
Do not put private key material in `receipt_key`; only `signing_key` has `d`.

3. `GET /payments/{payment_id}`, bearer-authenticated, performs **no charge** and
   returns the current settlement in a freshly signed receipt of the same shape.
   It must work after the original checkout expires. This powers reconciliation
   following a timeout or pending settlement. Keep proof bindings from the
   original request; do not substitute a new mandate or payment ID.

The processor is responsible for a durable mandate/dispute evidence store.
Rogue stores hashes and returned receipts, not raw shopper mandates or tokens.
Do not configure an untrusted processor; its signing key attests settlement.

## Agent API

MCP exposes `payment_options`, `create_payment`, `get_payment`, and `cancel_payment`.
Charge submission uses the native HTTP flow returned by the checkout.

All private payment endpoints require a bearer key/session; ambient cookies and
query-string tokens are rejected. Mutations require `payments:write`. Payment
records are owner-only. Admin endpoints additionally require an active admin.

```text
GET  /api/v1/payments/providers
POST /api/v1/payments
     {"engine":"x402","product":"pro","idempotency_key":"one-logical-purchase"}
GET  /api/v1/payments/{id}/offer
POST /api/v1/payments/{id}/pay
GET  /api/v1/payments/{id}
GET  /api/v1/payments                 # newest 100 belonging to you
POST /api/v1/payments/{id}/cancel     # before submitting payment only
```

The server fixes price, period, mode and configuration at creation. Quotes
expire after 15 minutes. The same owner/idempotency key returns the same checkout,
even after a configuration change or disable. Reusing it with a different engine
is a conflict. Reconfiguration invalidates unsubmitted offers; cancel them and
create a new checkout. Already-submitted settlements retain their old settings. Native proofs are accepted only on the engine selected by that
checkout. Credentials are hashed for replay prevention; they are not retained.
A database uniqueness constraint prevents one settlement from granting two
purchases, including the shared Stripe rail across ACP and MPP.

## Monitoring and recovery

`/admin/payments` shows all four engines, encryption/readiness problems, last
connection checks, live gross USD, test/live separation, pending/review states,
and the latest 100 filtered payments with provider references. Refresh it for a
new snapshot. Admins can run connection checks or read-only reconciliation there.
Configuration/toggle operations and successful payments are recorded in the
existing audit log. Secrets and raw payment proofs never appear on this screen.

A payment moves from `created` to `processing`, then `paid`, `pending`, `failed`
or `review`. A timeout is uncertain, not evidence that a charge failed. Rogue
never automatically resubmits an uncertain charge. `processing` left behind by
a crashed worker is also visible and reconcilable. Stripe webhooks can recover
an attempt whose HTTP response was lost. AP2 can reconcile by Rogue payment ID.
Stripe reconciliation with a known `pi_` reference retrieves the PaymentIntent.

The provider reference and receipt are saved before the access grant. The paid
ledger state, access grant and audit insertion then commit together in one SQL
statement. If that fails, provider funds may already have moved; the row remains
recoverable through a signed webhook or reconciliation. Never delete a pending
row or replace its idempotency key to make an error disappear.

Refunds/disputes are not automatically synchronized in this initial profile.
Issue any refund in the processor dashboard and review/revoke paid entitlements
in `/admin/agents/{handle}` as appropriate; renewed periods may overlap and
require adjustment. The gross total is a settlement total, not a net accounting
report. Provider onboarding and real sandbox/live certification remain operator
steps before enabling a production engine.

## Verification and protocol references

`./rogue test` includes `verify:payments`: migrations on embedded PostgreSQL,
four mocked provider flows, real routes, default-off behavior, admin and owner
authorization, encrypted configuration, concurrent idempotency/settlement,
replays, test isolation, live access expiry/renewal, signed webhooks and AP2
receipt rejection. Tests intercept every outgoing request; no provider credentials
or real funds are used. `verify:rogue` checks target isolation, dry runs, toggle
selection, HTTPS, and credential redaction.

Primary references used for the supported profiles:

- [ACP 2026-01-30 specification](https://github.com/agentic-commerce-protocol/agentic-commerce-protocol/blob/main/spec/2026-01-30/openapi/openapi.agentic_checkout.yaml)
- [Stripe shared payment tokens](https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens)
- [AP2 merchant implementation and delegated verification](https://ap2-protocol.org/ap2/implementation_considerations/)
- [AP2 checkout mandates and receipts](https://ap2-protocol.org/ap2/checkout_mandate/)
- [MPP server and separate payment authorization](https://mpp.dev/sdk/typescript/server/Mppx.create)
- [x402 facilitator, verification, settlement and pending recovery](https://docs.x402.org/core-concepts/facilitator)

## UCP checkout

UCP 2026-04-08 checkout uses the same verified x402 or Stripe SPT payment ledger.
Read [the UCP guide](https://rogue.camp/guides/ucp.md) and
[the business profile](https://rogue.camp/.well-known/ucp) for negotiation,
REST schemas, six MCP/SDK helpers, CLI commands and safe retry instructions.
Only currently enabled handlers appear in the business profile. Public discovery
and community content remain free; checkout buys prepaid digital Pro access.
