# Payments module · v0

> The Payments module lets a person pay a brand on Each: one-time payment, brand subscription or usage fee. Every transaction produces a signed proof anyone can verify. Each: test mode only, no real payments.

Status: draft · updated 2026-10-30 · JSON: https://test.each.app/en/surface/payments/v0.json

## What it does
- **one_time**: One-time payment: one amount, once (e.g. an order, a ticket).
- **subscription**: Brand subscription: a recurring amount (monthly or yearly) a person or a shop pays a brand.
- **usage**: Usage fee: an amount based on how a tool is used (e.g. per order placed through the network).

## The core (non-modifiable)
The Each core cannot be modified by a module. A module may extend it, never bypass it. The core owns: passkeys (who confirms), consent (nothing is paid without passkey confirmation), dollar caps and proof signing.

| Cap | Value |
|---|---|
| Currency | CAD |
| Per transaction | $500 |
| Per person per day | $1,000 |
| Per brand per day | $5,000 |
| Minimum | $0.50 |
| Test mode only | yes |

Each caps, enforced by the core before any provider call. A module may ask for lower caps, never higher ones.

## Scopes
- `payments:read`: Read your payments and their proofs.
- `payments:checkout`: Prepare a payment; it never happens without your passkey.
- `payments:subscribe`: Prepare a brand subscription; each one is confirmed by your passkey.
- `payments:usage`: A brand reports usage fees, within the core caps.
- `payments:refund`: A brand refunds a payment (to be defined, see open questions).

## Flow
1. The person picks a payment (kind, brand, amount).
2. The core checks the session, currency and caps.
3. The person confirms with their passkey (consent).
4. The module creates the payment with the provider (or simulates it in Each).
5. The provider confirms through a signed webhook; Each checks the signature and ignores duplicates.
6. Each issues a signed proof of the transaction.

## Webhooks
- POST /v1/modules/payments/webhook
- Signature: Signature header: t={timestamp},v1={hex HMAC-SHA256 of "{t}.{raw body}"} with the webhook secret. Rejected if the signature does not match or t is older than 5 minutes.
- Idempotency: Every event has a unique id. An id already received answers 200 and does nothing again. Outgoing calls carry an idempotency key (the Each payment id).
- Without a webhook secret, the endpoint answers 503 module_not_configured.

## Signed proof for every transaction
Every transaction produces a signed proof (ES256, same public key as gesture proofs: /.well-known/each-keys), append-only. Verifiable at /verifier/{id}.

Type: `each.payment` · fields: `v`, `type`, `id`, `issuer`, `environment`, `issued_at`, `test`, `person`, `brand`, `payment.kind`, `payment.amount_cents`, `payment.currency`, `payment.interval`, `payment.provider_mode`, `statement`, `confirmation (passkey)`, `core.version`, `module.version`

## API
- `GET /v1/modules/payments/status`
- `POST /v1/modules/payments/simulate/options`
- `POST /v1/modules/payments/simulate`
- `POST /v1/modules/payments/checkout`
- `POST /v1/modules/payments/webhook`
- `GET /v1/proofs/{id}`

## Configuration
The provider key is a server secret added by the Each team. It is never entered in a browser. Without a key: "module not configured" state. Live keys are refused in Each.

## Versions
Semantic contract version: v0 = draft. Any breaking change creates v1 at a new address; v0 stays readable. To propose a change: copy src/modules/payments/, edit contract.json, open a review.

Taxes: coming soon

## Open questions
- Who collects the money: Each, the brand, or connected accounts (each brand has its own account, Each takes a share)?
- What share for Each, and on which kinds (one-time, subscription, usage)?
- Refunds: who decides, within what delay, and does the original proof stay valid (a refund proof is added)?
- Currencies: CAD only at first, then which ones internationally?
