Aller au contenu
Ts-Payment

API Ts-Payment

Une seule spec. Tous vos PSP.

Un endpoint unique, un format de payload normalisé, un journal d’événements signés. Peu importe que le paiement passe par PayZen, Stripe, Adyen ou Mollie — votre code ne bouge pas.

Principes

Ce qui ne change pas, quel que soit le PSP

REST + JSON

HTTPS uniquement, JSON UTF-8. Aucune dépendance XML ou formulaire hérité côté consommateur — la traduction vers le format natif du PSP est faite par Ts-Payment.

Idempotence

Chaque POST accepte un header Idempotency-Key. Rejouer la requête renvoie exactement la même réponse pendant 24h — y compris en cas de timeout réseau.

Webhooks signés HMAC-SHA256

Chaque événement est signé avec votre secret de webhook. Deux signatures en rotation simultanée pour rotation à chaud sans downtime.

Versioning par en-tête

L’en-tête Ts-Payment-Version: 2026-04-01 fige le schéma. Nous maintenons chaque version 24 mois après sa dépréciation annoncée.

Erreurs typées

type + code + psp_code + message, toujours présents. Corrélation des erreurs PSP sur un code unifié — votre logique métier n’a jamais à connaître les codes de chaque PSP.

Rate limit généreux

200 req/s en rafale, 100 req/s soutenu par clé API. Quota relevé sur demande pour les gros volumes — contactez-nous.

Authentification

Clés API et idempotence

Chaque environnement dispose de deux clés : une clé tpay_test_* pour la sandbox (route tous les PSP vers leurs sandbox respectives) et une clé tpay_live_* pour la production. Les clés sont rotables à chaud sans interruption.

Toute requête POST accepte un en-tête Idempotency-Key (UUIDv4 recommandé). Deux appels avec la même clé en moins de 24 h renvoient la même réponse — y compris si le premier appel s’est soldé par un timeout. C’est votre filet de sécurité en cas de panne réseau entre vos serveurs et Ts-Payment.

Requêtehttp
POST /v1/payments HTTP/1.1
Host: api.ts-payment.org
Authorization: Bearer tpay_live_a1B2c3D4e5F6...
Ts-Payment-Version: 2026-04-01
Idempotency-Key: 8d4e6f2a-9c1b-4a7d-b3e2-5f90a1c2d3e4
Content-Type: application/json

Quickstart

Flux d’un paiement de bout en bout

Votre serveur crée le paiement, le client est redirigé vers le formulaire du PSP, Ts-Payment vous notifie l’issue par webhook. La capture est toujours une seconde étape explicite.

PCI DSS — frontière stricte

L’API Ts-Payment n’accepte jamais un PAN, une date d’expiration ni un CVC en clair. Le formulaire de saisie carte est hébergé par le PSP sélectionné (PayZen, Stripe, Adyen…). La gestion 3DS est du ressort du PSP — Ts-Payment n’arbitre rien sur ce sujet et se contente de vous restituer le résultat d’authentification. Vous restez en SAQ A.

Montants en unité mineure (centimes) : 4 990 € = 499000. Convention alignée sur Stripe, Adyen, PayZen, Mollie — pas d’ambiguïté sur les devises à 3 décimales.

1. Créer le paiement

Le backend marchand déclare l’intention de paiement. La réponse contient une checkout_url vers laquelle rediriger le client.

POST /v1/paymentsjson
{
  "amount": 499000,
  "currency": "EUR",
  "reference": "ORDER-2026-0042",
  "payment_method_types": ["card", "ideal", "bancontact", "sepa_debit"],
  "customer": {
    "email": "marie.dupont@example.com",
    "name": "Marie Dupont",
    "locale": "fr-FR"
  },
  "billing_address": {
    "line1": "10 rue de Duclus",
    "postal_code": "74300",
    "city": "Cluses",
    "country": "FR"
  },
  "return_url": "https://merchant.example.com/checkout/return",
  "routing": {
    "strategy": "cost_optimized",
    "preferred_psp": "payzen",
    "fallback_psp": ["stripe", "adyen"]
  },
  "metadata": {
    "order_id": "42",
    "cart_hash": "a3f2e91c"
  }
}
201 Created — paiement créé, en attente du clientjson
{
  "id": "pay_01HYX7K3Z4N8Q2PVM6R5J9B2AE",
  "object": "payment",
  "status": "requires_payment",
  "amount": 499000,
  "currency": "EUR",
  "reference": "ORDER-2026-0042",
  "created_at": "2026-04-19T14:32:01.412Z",
  "expires_at": "2026-04-19T14:47:01.412Z",
  "checkout_url": "https://checkout.ts-payment.org/c/pay_01HYX7K3Z4N8Q2PVM6R5J9B2AE",
  "psp": {
    "name": "payzen",
    "transaction_id": "709821342"
  },
  "routing": {
    "strategy_used": "cost_optimized",
    "selected_psp": "payzen",
    "fallback_psp": ["stripe", "adyen"]
  },
  "links": {
    "self": "/v1/payments/pay_01HYX7K3Z4N8Q2PVM6R5J9B2AE",
    "cancel": "/v1/payments/pay_01HYX7K3Z4N8Q2PVM6R5J9B2AE/cancel"
  },
  "metadata": { "order_id": "42", "cart_hash": "a3f2e91c" }
}

2. Webhook payment.authorized

Le client a saisi sa carte chez le PSP, l’authentification (3DS, SCA) a été gérée par le PSP, l’autorisation bancaire est obtenue. Le webhook porte les infos de la carte masquées et le résultat 3DS.

POST https://merchant.example.com/webhooks/tspaymentjson
{
  "id": "evt_01HYX7K9R2T8VW4JX6F3M2PB5Q",
  "type": "payment.authorized",
  "api_version": "2026-04-01",
  "created_at": "2026-04-19T14:32:48.120Z",
  "livemode": true,
  "data": {
    "object": {
      "id": "pay_01HYX7K3Z4N8Q2PVM6R5J9B2AE",
      "object": "payment",
      "status": "authorized",
      "amount": 499000,
      "amount_authorized": 499000,
      "amount_captured": 0,
      "currency": "EUR",
      "reference": "ORDER-2026-0042",
      "authorized_at": "2026-04-19T14:32:48.102Z",
      "psp": {
        "name": "payzen",
        "acquirer": "credit-agricole",
        "transaction_id": "709821342",
        "authorization_code": "A87C2D",
        "response_code": "00"
      },
      "payment_method": {
        "type": "card",
        "card": {
          "brand": "visa",
          "number_masked": "4000 XXXX XXXX 3155",
          "bin": "400000",
          "last4": "3155",
          "exp_month": 12,
          "exp_year": 2028,
          "country": "FR",
          "funding": "credit",
          "holder_name": "Marie D."
        }
      },
      "three_d_secure": {
        "version": "2.2.0",
        "authenticated": true,
        "eci": "05",
        "liability_shift": true
      },
      "metadata": { "order_id": "42" }
    }
  }
}

3. Capturer le paiement

La capture est toujours explicite et découplée de l’autorisation. On capture généralement à l’expédition ou à la prestation effective. Montant optionnel pour une capture partielle.

POST /v1/payments/:id/capturejson
{
  "amount": 499000
}
200 OK — paiement capturéjson
{
  "id": "pay_01HYX7K3Z4N8Q2PVM6R5J9B2AE",
  "status": "captured",
  "amount": 499000,
  "amount_captured": 499000,
  "amount_refunded": 0,
  "authorized_at": "2026-04-19T14:32:48.102Z",
  "captured_at": "2026-04-19T14:38:12.304Z",
  "psp": {
    "name": "payzen",
    "transaction_id": "709821342"
  }
}

Un webhook payment.captured est émis en miroir vers votre endpoint, avec le même format que l’étape 2.

Variante : paiement par token (one-click, abonnement, MIT)

Si vous avez déjà un payment_method_token (issu d’un précédent paiement avec save_payment_method, d’une session SCA ou d’un abonnement), vous appelez directement /v1/payments sans passer parcheckout_url.

Pas de fallback possible.

Un token est lié au PSP qui l’a émis (PayZen, Stripe, Adyen…). Ts-Payment ne peut pas le rejouer sur un autre PSP. Si le PSP émetteur est indisponible, le paiement échoue — le fallback_psp est ignoré pour ce type d’appel.

POST /v1/payments (token)json
{
  "amount": 499000,
  "currency": "EUR",
  "reference": "SUB-2026-0007-APR",
  "payment_method": {
    "type": "card",
    "token": "pmtok_01HYX7K1P4M9Q2PVM6R5J9B2AD"
  },
  "customer_id": "cus_01HXZ4R9M6T3K2B8N1P7VX5A0C",
  "merchant_initiated": true,
  "metadata": {
    "subscription_id": "sub_42",
    "period": "2026-04"
  }
}
200 OK — autorisé directement (pas de redirect)json
{
  "id": "pay_01HYX7M2P5Q9R3S4T5U6V7W8X9Y",
  "status": "authorized",
  "amount": 499000,
  "amount_authorized": 499000,
  "currency": "EUR",
  "authorized_at": "2026-04-19T14:32:48.102Z",
  "psp": {
    "name": "payzen",
    "transaction_id": "709821555",
    "authorization_code": "B42F3A"
  },
  "routing": {
    "strategy_used": "token_bound",
    "selected_psp": "payzen",
    "fallback_available": false
  },
  "payment_method": {
    "type": "card",
    "card": {
      "brand": "visa",
      "number_masked": "4000 XXXX XXXX 3155",
      "bin": "400000",
      "last4": "3155",
      "exp_month": 12,
      "exp_year": 2028,
      "holder_name": "Marie D."
    }
  }
}

Endpoints

Surface de l’API

Douze endpoints couvrent 100 % des cas e-commerce et SaaS courants.

MéthodeCheminDescription
POST/v1/paymentsCréer un paiement (authorize ou authorize+capture)
GET/v1/payments/:idRécupérer un paiement
GET/v1/paymentsLister / filtrer (pagination cursor)
POST/v1/payments/:id/captureCapturer une autorisation (total ou partiel)
POST/v1/payments/:id/cancelAnnuler une autorisation non capturée
POST/v1/payments/:id/refundsRembourser (total ou partiel)
GET/v1/payments/:id/refundsLister les remboursements d’un paiement
POST/v1/customersCréer un client (réutilisable pour abonnements / tokens)
POST/v1/payment_methodsTokeniser un moyen de paiement (token cross-PSP)
POST/v1/subscriptionsCréer un abonnement récurrent
GET/v1/eventsJournal d’événements (rejeu, audit trail)
POST/v1/webhooksEnregistrer un endpoint de webhook

Webhooks

Un format unique, signé HMAC

Chaque événement est envoyé en POST JSON sur votre endpoint, avec un en-tête de signature et un nonce anti-rejeu. Réessai exponentiel sur 72h puis dead-letter queue consultable via l’API.

POST https://merchant.example.com/webhooks/tspaymenthttp
Content-Type: application/json
Ts-Signature: t=1744989122,v1=5e8c4a...f2,v1=9d3b7e...c1
Ts-Event-Id: evt_01HYX7K9R2T8VW4JX6F3M2PB5Q
Ts-Delivery-Attempt: 1

{
  "id": "evt_01HYX7K9R2T8VW4JX6F3M2PB5Q",
  "type": "payment.captured",
  "api_version": "2026-04-01",
  "created_at": "2026-04-19T14:32:02.105Z",
  "livemode": true,
  "data": {
    "object": {
      "id": "pay_01HYX7K3Z4N8Q2PVM6R5J9B2AE",
      "object": "payment",
      "status": "captured",
      "amount": 499000,
      "amount_captured": 499000,
      "currency": "EUR",
      "psp": { "name": "payzen", "transaction_id": "709821342" },
      "reference": "ORDER-2026-0042",
      "metadata": { "order_id": "42" }
    },
    "previous_attributes": {
      "status": "authorized",
      "amount_captured": 0
    }
  }
}

Événements disponibles

  • payment.requires_action
  • payment.authorized
  • payment.captured
  • payment.partially_captured
  • payment.failed
  • payment.canceled
  • payment.refunded
  • payment.partially_refunded
  • payment.dispute.opened
  • payment.dispute.closed
  • refund.succeeded
  • refund.failed
  • subscription.activated
  • subscription.canceled
  • subscription.renewed
  • payout.paid
  • webhook.test

Chaque webhook est livré au moins une fois. Utilisez Ts-Event-Id pour dédupliquer côté consommateur. Le journal /v1/events permet de rejouer les 90 derniers jours.

Erreurs

Typées, corrélées, actionnables

Chaque erreur PSP est traduite dans une taxonomie unifiée. Votre code applicatif ne connaît que les codes Ts-Payment — le code PSP natif reste exposé pour le debug.

402 Payment Requiredjson
{
  "error": {
    "type": "card_error",
    "code": "card_declined",
    "decline_code": "insufficient_funds",
    "message": "Votre carte a été refusée : provision insuffisante.",
    "psp_code": "51",
    "psp_message": "Insufficient funds",
    "psp": "payzen",
    "doc_url": "https://docs.ts-payment.org/errors#insufficient_funds",
    "request_id": "req_01HYX7KC8N3P9R2VM4J5B6A7Q",
    "payment_id": "pay_01HYX7L0M4R9Q2PVM6R5J9B2BG"
  }
}
CodeDescription
authentication_errorClé API manquante ou invalide.
invalid_request_errorPayload mal formé ou champ obligatoire manquant.
card_errorCarte refusée (insufficient_funds, do_not_honor, expired_card…).
three_d_secure_errorAuthentification 3DS échouée ou abandonnée.
psp_errorLe PSP sous-jacent a retourné une erreur non gérée — voir psp_code / psp_message.
rate_limit_errorQuota dépassé — consulter l’en-tête Retry-After.
routing_errorAucun PSP disponible pour cette combinaison pays / méthode / montant.
api_errorErreur interne Ts-Payment (5xx) — corrigée automatiquement, réessayer.

Smart routing

Les règles de routage en clair

Définissez vos règles une fois, elles s’appliquent à chaque requête. Exemples de policies fréquentes, déclinables dans votre dashboard ou via l’API.

routing.rules — exemplejson
[
  {
    "when": { "country": "FR", "method": "card", "amount_max": 5000 },
    "route": { "primary": "payzen", "fallback": ["stripe"] }
  },
  {
    "when": { "country": "FR", "method": "card", "amount_min": 5000 },
    "route": { "primary": "adyen", "fallback": ["payzen"] }
  },
  {
    "when": { "method": "ideal" },
    "route": { "primary": "mollie" }
  },
  {
    "when": { "method": "bnpl" },
    "route": { "primary": "alma", "fallback": ["klarna"] }
  },
  {
    "when": { "currency": "USD" },
    "route": { "primary": "stripe", "fallback": ["checkoutcom"] }
  }
]

Stratégies supportées : primary_only, cost_optimized, auth_rate_optimized, round_robin, weighted (A/B test), custom (règles DSL).

Un circuit breaker coupe automatiquement un PSP dont le taux d’autorisation tombe sous votre seuil ou dont la latence p95 dépasse 3 secondes, et réouvre par sondes toutes les 30 secondes.

Chaque tentative de routage est horodatée dans le champ routing.attempts[] de la réponse — vous gardez la traçabilité complète, y compris pour les audits DAF.

SDK officiels

Drop-in pour tous les stacks

Nos SDK sont générés depuis la spec OpenAPI 3.1. Ils restent synchronisés avec chaque version de l’API.

TypeScript / Node

npm i @ts-payment/node

PHP

composer require ts-payment/php

Python

pip install ts-payment

Java

implementation("org.tspayment:client")

Plugins e-commerce : WooCommerce, Prestashop, Magento 2. Shopify sur la roadmap 2026. Tous open-source, accès sur demande.

Envie de tester l’API sur votre stack ?

Parlez-nous de votre intégration. Accès sandbox sous 24h ouvrées, avec vos clés test PayZen / Stripe / Mollie pré-connectées.