{
  "language": "fr",
  "alternates": {
    "fr": "https://test.each.app/surface/payments/v0.json",
    "en": "https://test.each.app/en/surface/payments/v0.json",
    "markdown_fr": "https://test.each.app/surface/payments/v0.md",
    "markdown_en": "https://test.each.app/en/surface/payments/v0.md"
  },
  "core_enforced": {
    "currencies": [
      "CAD"
    ],
    "max_per_transaction_cents": 50000,
    "max_per_person_day_cents": 100000,
    "max_per_brand_day_cents": 500000,
    "min_cents": 50,
    "test_mode_only": true
  },
  "module": "payments",
  "version": "v0",
  "status": "draft",
  "updated": "2026-10-30",
  "name": "module Paiements",
  "summary": "Le module Paiements permet à une personne de payer une marque sur Each: paiement unique, abonnement à une marque ou frais d'usage. Chaque transaction produit une preuve signée, vérifiable par tout le monde. Each: mode test seulement, aucun paiement réel.",
  "kinds": [
    {
      "id": "one_time",
      "text": "Paiement unique: un montant, une fois (ex.: une commande, un billet)."
    },
    {
      "id": "subscription",
      "text": "Abonnement de marque: un montant récurrent (mensuel ou annuel) qu'une personne ou un commerce paie à une marque."
    },
    {
      "id": "usage",
      "text": "Frais d'usage: un montant calculé selon l'usage d'un outil (ex.: par commande passée par le réseau)."
    }
  ],
  "core": {
    "fr": "Le noyau Each n'est pas modifiable par un module. Un module peut l'étendre, jamais le contourner. Le noyau garde: les clés d'accès (qui confirme), le consentement (rien n'est payé sans confirmation par clé d'accès), les plafonds en dollars et la signature des preuves.",
    "en": "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.",
    "caps": {
      "currency": "CAD",
      "per_transaction": "500 $",
      "per_person_per_day": "1 000 $",
      "per_brand_per_day": "5 000 $",
      "minimum": "0,50 $",
      "test_mode_only": true
    },
    "taxes": "Taxes: à venir",
    "caps_note": "Plafonds, appliqués par le noyau avant tout appel au fournisseur. Un module peut demander des plafonds plus bas, jamais plus hauts."
  },
  "scopes": [
    {
      "id": "payments:read",
      "text": "Lire tes paiements et leurs preuves."
    },
    {
      "id": "payments:checkout",
      "text": "Préparer un paiement; il n'est jamais fait sans ta clé d'accès."
    },
    {
      "id": "payments:subscribe",
      "text": "Préparer un abonnement de marque; chaque abonnement est confirmé par ta clé d'accès."
    },
    {
      "id": "payments:usage",
      "text": "Une marque déclare des frais d'usage, dans les plafonds du noyau."
    },
    {
      "id": "payments:refund",
      "text": "Une marque rembourse un paiement (à définir, voir questions ouvertes)."
    }
  ],
  "flow": [
    "La personne choisit un paiement (type, marque, montant).",
    "Le noyau vérifie la session, la devise et les plafonds.",
    "La personne confirme avec sa clé d'accès (consentement).",
    "Le module crée le paiement chez le fournisseur (ou le simule en Each).",
    "Le fournisseur confirme par webhook signé; Each vérifie la signature et ignore les doublons.",
    "Each émet une preuve signée de la transaction."
  ],
  "webhooks": {
    "endpoint": "POST /v1/modules/payments/webhook",
    "signature": "En-tête de signature: t={horodatage},v1={HMAC-SHA256 hexadécimal de \"{t}.{corps brut}\"} avec le secret de webhook. Refusé si la signature ne correspond pas ou si t a plus de 5 minutes.",
    "idempotency": "Chaque événement a un id unique. Un id déjà reçu répond 200 sans rien refaire. Les appels sortants portent une clé d'idempotence (id du paiement Each).",
    "not_configured": "Sans secret de webhook, le point d'entrée répond 503 module_not_configured."
  },
  "proof": {
    "type": "each.payment",
    "fr": "Chaque transaction produit une preuve signée (ES256, même clé publique que les preuves de geste: /.well-known/each-keys), en ajout seulement. Vérifiable sur /verifier/{id}.",
    "en": "Every transaction produces a signed proof (ES256, same public key as gesture proofs: /.well-known/each-keys), append-only. Verifiable at /verifier/{id}.",
    "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": "La clé du fournisseur est un secret serveur ajouté par l'équipe Each. Elle n'est jamais saisie dans un navigateur. Sans clé: état \"module non configuré\". Clé de production refusée en Each.",
  "versioning": "Version sémantique du contrat: v0 = brouillon. Toute modification incompatible crée v1 à une nouvelle adresse; v0 reste lisible. Pour proposer un changement: copie src/modules/payments/, modifie contract.json, ouvre une revue.",
  "open_questions": [
    {
      "text": "Qui encaisse l'argent: Each, la marque, ou des comptes connectés (chaque marque a son compte, Each prend une part)?"
    },
    {
      "text": "Quelle part pour Each, et sur quels types (unique, abonnement, usage)?"
    },
    {
      "text": "Remboursements: qui décide, dans quel délai, et la preuve d'origine reste-t-elle valide (une preuve de remboursement s'ajoute)?"
    },
    {
      "text": "Devises: CAD seulement au départ, puis lesquelles pour l'international?"
    }
  ]
}