API

Enregistrer une carte et la débiter plus tard

Encaissez un paiement par carte sur votre propre checkout, conservez la carte avec le consentement du client, puis débitez-la à nouveau plus tard en l'absence du client : frais de non-présentation, suppléments ajoutés après le départ, acompte complété. Cette page détaille tout le flux, de la création du premier checkout au débit de la carte enregistrée, avec les requêtes et réponses de chaque étape.

Activée par Yavin. L'enregistrement d'une carte et le débit d'une carte enregistrée nécessitent tous deux que la fonctionnalité de cartes enregistrées soit activée sur votre compte. Demandez son activation à votre contact Yavin ; d'ici là, les requêtes ci-dessous sont refusées.


Fonctionnement

Deux moments distincts, reliés par un seul identifiant. Le premier paiement enregistre la carte et vous donne stored_card.id. Plus tard, à tout moment, votre serveur débite cet identifiant sur un nouveau checkout. Le numéro de carte n'atteint jamais vos serveurs, ni dans un cas ni dans l'autre.


Avant de commencer

PrérequisPourquoi
La fonctionnalité activée sur votre compteUn seul interrupteur couvre l'enregistrement d'une carte via le widget carte et le débit d'une carte enregistrée via l'API. Demandez à Yavin de l'activer.
Le widget carteLe consentement du titulaire de la carte est transmis avec le paiement : la carte est donc enregistrée depuis votre propre page via le widget (submit({ enrolConsent: true })). La page de paiement hébergée par Yavin ne le propose pas. Voir Intégration Drop-in du paiement en ligne.
Votre propre mention de consentementYavin ne recueille pas le consentement à votre place. Vous affichez votre propre mention d'information et ne transmettez enrolConsent: true que pour un client qui l'a acceptée.
A webhook_urlL'identifiant de la carte enregistrée vous parvient dans le webhook payment_received. Conservez-le pour chaque checkout du parcours.
Un plafond de dépenses adaptéLes débits sur carte enregistrée sont plafonnés par client, sur toute la durée de vie du client, à un montant défini sur votre compte (300 € par défaut). Un débit qui ferait dépasser ce plafond au client est refusé avec amount_over_limit. Demandez à Yavin de le modifier.

URL de base : production https://api.yavin.com/api/v5/ecommerce, sandbox https://api.sandbox.yavin.com/api/v5/ecommerce. Authentification : votre clé API dans l'en-tête Yavin-Secret.


Étape 1 - Enregistrer la carte

Étape 1 - Créer le checkout qui enregistre la carte

Créez le checkout côté serveur, exactement comme dans l'intégration Drop-in, avec deux ajouts : features.enrol_card et un objet customer.

  • features.enrol_card est un booléen et vaut false par défaut. Chaque valeur de features doit être un véritable booléen
  • Avec enrol_card: true, l'objet customer est obligatoire : first_name, last_name et au moins l'un des champs telephone (50 caractères maximum) ou email. Yavin conserve ce contact avec la carte enregistrée, et votre marchand le voit dans MyYavin
  • Une requête qui enfreint l'une de ces règles, ou qui provient d'un compte sans cette fonctionnalité, est rejetée avant la création de tout checkout : 422 sur /checkouts/, avec une erreur désignant features ou customer
  • enrol_card n'est pris en compte que par le widget de carte. Un checkout ouvert sur la page de paiement hébergée de Yavin n'envoie aucun consentement : rien n'y est donc enregistré
cURL
POST https://api.yavin.com/api/v5/ecommerce/checkouts/
Yavin-Secret: <your yavin_secret_key>
Content-Type: application/json

{ "checkout_external_id": "booking_88",
  "amount": 2900,
  "customer": { "first_name": "Ada", "last_name": "Lovelace",
                "telephone": "+33612345678", "email": "ada@example.com" },
  "features": { "enrol_card": true },
  "return_url_success": "https://shop.example.com/return",
  "return_url_cancelled": "https://shop.example.com/cancel",
  "webhook_url": "https://shop.example.com/hooks/yavin" }
JSON
// 201 Created
{ "checkout_token": "Ab3f...",
  "payment_link": "https://..." }

Affichez le widget avec checkout_token, présentez votre mention de consentement, puis transmettez le consentement lors du paiement :

  • enrolConsent est facultatif et vaut false par défaut. Il doit être un véritable booléen : "true", 1 ou null font que submit() rejette la valeur et que rien n'est envoyé
  • Le consentement ne s'applique qu'à cette tentative. Une nouvelle tentative qui ne le transmet pas n'enregistre rien, même si une tentative précédente l'avait fait
  • Après une authentification renforcée 3-D Secure, c'est le consentement donné au début du paiement qui est pris en compte
  • L'enregistrement de la carte ne fait jamais échouer le paiement : si la carte ne peut pas être enregistrée, le paiement aboutit quand même et aucune carte n'est renvoyée
Node
const widget = window.YavinPaymentsSDK.mount(
  document.getElementById('card'),
  { checkoutToken: 'Ab3f...' }
)

// the customer ticked your "keep my card for later payments" box
const result = await widget.submit({ enrolConsent: customerAgreed })

Étape 3 - Lire la carte enregistrée

Une fois le paiement autorisé, le webhook payment_received arrive avec le checkout. Chaque transaction contient stored_card :

ChampSignification
stored_cardnull lorsqu'aucune carte enregistrée n'est liée à la transaction, ce qui est le cas dès que rien n'a été enregistré.
stored_card.idL'identifiant de la carte (un UUID). Conservez-le avec votre client ou votre réservation : c'est la valeur customer_card_id que vous envoyez pour débiter la carte.
stored_card.expiration_dateYYYY-MM, le mois d'expiration de la carte, tel qu'imprimé sur la carte. Une carte est valide jusqu'à la fin de ce mois. null lorsque l'expiration est inconnue.

Vérifiez-la avant de vous y fier. Si le webhook indique stored_card: null, le client a payé mais aucune carte n'est enregistrée, par exemple parce que le consentement n'a pas été envoyé. Gérez ce cas dans votre parcours : demandez un autre moyen de paiement plutôt que de supposer qu'une carte existe.

La carte est enregistrée lorsque le paiement est autorisé, avant toute capture, donc stored_card figure déjà dans le webhook payment_received. Seuls les paiements par carte sont enregistrés : un paiement par portefeuille électronique, comme Apple Pay, ne l'est jamais. Vous pouvez lire ce même objet à tout moment avec /get_cart_information/.


JSON
// webhook POST to your webhook_url
{ "action": "payment_received",
  "checkout_external_id": "booking_88",
  "requested_amount": 2900,
  "asked_amount": 2900,
  "paid_amount": 2900,
  "service_fee": 0,
  "status": "ok",
  "transactions": [
    { "transaction_id": "trs_7f3a92c5",
      "total_amount": 2900,
      "gift_amount": 0,
      "currency_code": "EUR",
      "date_of_payment": "2026-11-03 14:35:15",
      "gateway": "NEPTING_ECOMMERCE",
      "issuer": "VISA",
      "pan": "4970********0001",
      "status": "ok",
      "stored_card": { "id": "9b2f4c1e-7a3d-4f55-8a61-2d9c3b7e1a10",
                       "expiration_date": "2028-12" } } ] }

Étape 2 - Débiter la carte enregistrée

Lorsque vous devez débiter, créez un nouveau checkout pour le nouveau montant, puis payez-le avec la carte enregistrée. Les deux appels sont effectués par votre serveur, sans client ni navigateur.

Étape 4 - Créer le checkout à débiter

  • Attribuez-lui un nouveau checkout_external_id : un identifiant est réservé définitivement, même par un checkout annulé
  • Laissez le mode de capture à sa valeur par défaut (instantanée). Un checkout créé avec is_instant_capture: false ne peut pas être payé avec une carte enregistrée
  • Vous n'avez pas besoin de enrol_card ni d'un client ici
cURL
POST https://api.yavin.com/api/v5/ecommerce/checkouts/
Yavin-Secret: <your yavin_secret_key>
Content-Type: application/json

{ "checkout_external_id": "booking_88_noshow",
  "amount": 1500,
  "webhook_url": "https://shop.example.com/hooks/yavin" }
JSON
// 201 Created
{ "checkout_token": "Zk9q...",
  "payment_link": "https://..." }

Étape 5 - Payer avec la carte enregistrée

POST https://api.yavin.com/api/v5/ecommerce/checkouts/{checkout_token}/pay/

customer_card_idString (UUID)requis
Le stored_card.id que vous avez conservé à l'étape 3

La carte est débitée du montant total restant à payer sur le checkout. Le corps de la requête ne contient que customer_card_id : n'y joignez aucune donnée de carte.

La réponse est le Paiement en ligne : E-commerce API, renvoyé directement (sans être encapsulé dans data). Le même checkout est ensuite envoyé à votre webhook_url sous la forme payment_received, et la transaction est liée à la carte enregistrée via stored_card.

Définissez un délai d'attente côté client d'au moins 30 secondes et ne relancez jamais un débit à l'aveugle. Si vous n'avez pas obtenu de réponse, lisez d'abord le checkout avec /get_cart_information/ : le paiement a peut-être abouti.

cURL
POST https://api.yavin.com/api/v5/ecommerce/checkouts/Zk9q.../pay/
Yavin-Secret: <your yavin_secret_key>
Content-Type: application/json

{ "customer_card_id": "9b2f4c1e-7a3d-4f55-8a61-2d9c3b7e1a10" }
JSON
// 200 OK, the Checkout object
{ "checkout_external_id": "booking_88_noshow",
  "payment_link": "https://...",
  "requested_amount": 1500,
  "asked_amount": 1500,
  "paid_amount": 1500,
  "service_fee": 0,
  "gift_amount": 0,
  "status": "ok",
  "transactions": [
    { "transaction_id": "trs_91c4d0aa",
      "total_amount": 1500,
      "gift_amount": 0,
      "currency_code": "EUR",
      "date_of_payment": "2026-11-20 09:12:44",
      "gateway": "NEPTING_ECOMMERCE",
      "issuer": "VISA",
      "pan": "4970********0001",
      "status": "ok",
      "stored_card": { "id": "9b2f4c1e-7a3d-4f55-8a61-2d9c3b7e1a10",
                       "expiration_date": "2028-12" } } ] }

Étape 6 - Confirmer

Considérez la réponse 200 et le webhook payment_received comme la confirmation, comme pour tout autre paiement. Pour réconcilier, lisez le checkout :


cURL
POST https://api.yavin.com/api/v5/ecommerce/get_cart_information/
{ "checkout_external_id": "booking_88_noshow" }

Erreurs

Un débit refusé renvoie un corps d'erreur, de même forme pour tous les codes :

code est la valeur stable sur laquelle baser votre logique. transaction_id est présent lorsque la tentative a atteint la passerelle de paiement.

La carte ne peut pas être débitée

codeHTTPSignification et action à mener
card_not_found404Aucune carte enregistrée avec cet identifiant sur votre compte. Vérifiez l'identifiant que vous avez conservé : la carte d'un autre marchand a le même aspect.
card_not_consented422La carte n'a jamais été enregistrée avec le consentement du titulaire. Elle ne peut donc pas être débitée.
card_disabled422La carte a été désactivée, par exemple par votre marchand dans MyYavin.
card_expired422Le mois d'expiration de la carte est dépassé. Demandez une nouvelle carte au client. Une carte enregistrée sans date d'expiration n'expire jamais côté Yavin.
amount_over_limit422Ce débit ferait dépasser la limite définie sur votre compte pour le total débité au client via des cartes enregistrées. Demandez à Yavin de la relever, ou encaissez autrement.
amount_invalid422Le montant à débiter n'est pas supérieur à zéro.
company_not_configured422Le compte n'est pas configuré pour débiter des cartes enregistrées : la fonctionnalité est désactivée ou un contrat de paiement est manquant. Contactez Yavin.

Le checkout ne peut pas être payé

codeHTTPSignification et action à mener
invalid_payload422Le corps de la requête n'est pas un customer_card_id valide (un UUID), ou il le combine avec des données de carte. errors indique le champ concerné.
checkout_not_found404Aucun checkout avec ce token sur votre compte.
checkout_cancelled409Le checkout a été annulé. Créez-en un nouveau.
checkout_already_paid409Le checkout est déjà payé. Ne débitez pas à nouveau.
checkout_not_payable409Le checkout n'attend pas de paiement.
attempt_in_progress409Une autre tentative de paiement est en cours sur ce checkout. Patientez, puis consultez le checkout avant de réessayer.
checkout_amount_changed409Le montant à payer a changé pendant l'appel. Consultez le checkout et décidez de la suite.
checkout_requires_capture422Le checkout utilise la capture différée, qu'une carte enregistrée ne peut pas payer. Créez un checkout avec la capture immédiate par défaut.
checkout_version_incompatible422Ce checkout ne peut pas être payé de serveur à serveur. Créez-le avec POST /checkouts/.

Le paiement a été tenté

codeHTTPSignification et action à mener
payment_declined402L'émetteur a refusé le débit. Rien n'a été prélevé. Demandez au client un autre moyen de paiement.
insufficient_funds402Le solde de la carte est insuffisant. Rien n'a été prélevé.
gateway_connection_error503La passerelle de paiement n'a pas pu être jointe, le débit n'a donc pas été effectué. Vous pouvez réessayer sans risque.
acquirer_timeout504L'acquéreur n'a pas répondu dans le délai imparti. Le résultat est inconnu. Consultez le checkout avant de réessayer.
gateway_error502La passerelle a renvoyé une réponse dont Yavin n'a pas pu tirer de conclusion. Le résultat n'est pas certain. Consultez le checkout avant de réessayer.

Les codes invalid_auth, challenge_in_progress et challenge_not_completable sont rares : considérez que le paiement n'a pas été prélevé, et contactez le support s'ils persistent.

Un résultat inconnu n'est pas un échec. Après acquirer_timeout ou gateway_error, ou après tout appel resté sans réponse, consultez le checkout avec /get_cart_information/ et examinez ses champs status et paid_amount avant de débiter à nouveau. Rejouer un débit sans surveillance expose le client à un double débit.


JSON
{ "detail": "The payment was declined.",
  "code": "payment_declined",
  "errors": {},
  "transaction_id": "trs_91c4d0aa" }

Bon à savoir

  • Vous ne manipulez jamais de données de carte. Vous conservez un identifiant et une date d'expiration. Le numéro de carte et le CVC restent dans le champ carte de Yavin et n'atteignent jamais votre page ni vos serveurs
  • Conservez la preuve du consentement. Il vous revient d'enregistrer la mention affichée et le moment où le client l'a acceptée
  • Votre marchand voit les cartes dans MyYavin. La section des cartes enregistrées liste les cartes enregistrées depuis vos checkouts, avec les coordonnées du client, et permet d'en désactiver une. Une carte désactivée est ensuite refusée avec card_disabled
  • Les cartes enregistrées en magasin peuvent être débitées de la même manière. Une carte enregistrée sur un terminal Yavin est débitée avec le même appel et son identifiant, à condition que la fonctionnalité de cartes enregistrées soit activée sur votre compte
  • Plusieurs paiements, un seul client. Le plafond de dépenses additionne tous les débits réussis par carte enregistrée d'un même client, toutes cartes enregistrées confondues